Lesson 9
Listas bidimensionales (matrices)
1. Listas anidadas: procesamiento e impresión
En el mundo real A menudo las tareas tienen que almacenar la tabla de datos rectangular. [¡diga más sobre esto!] Tales tablas se llaman matrices o matrices bidimensionales. En Python, cualquier tabla se puede representar como una lista de listas (una lista, donde cada elemento es a su vez una lista). Por ejemplo, aquí está el programa que crea una tabla numérica con dos filas y tres columnas, y luego realiza algunas manipulaciones con ella:a = [[1, 2, 3], [4, 5, 6]] print(a[0]) print(a[1]) b = a[0] print(b) print(a[0][2]) a[0][1] = 7 print(a) print(b) b[2] = 9 print(a[0]) print(b)
El primer elemento de a
aquí - a[0]
- es una lista de números [1, 2, 3]
. El primer elemento de esta nueva lista es a[0][0] == 1
; además, a[0][1] == 2
, a[0][2] == 3
, a[1][0] == 4
, a[1][1] == 5
, a[1][2] == 6
.
Para procesar una matriz bidimensional, normalmente utiliza bucles anidados. El primer ciclo itera a través del número de fila, el segundo ciclo recorre los elementos dentro de una fila. Por ejemplo, así es como se visualiza la lista numérica bidimensional en la pantalla línea por línea, separando los números con espacios:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] for i in range(len(a)): for j in range(len(a[i])): print(a[i][j], end=' ') print()
Ya hemos intentado explicar que una variable for-loop en Python puede iterar no solo sobre un range()
, sino generalmente sobre todos los elementos de cualquier secuencia. Las secuencias en Python son listas y cadenas (y algunos otros objetos que aún no hemos encontrado). Vea cómo puede imprimir una matriz bidimensional, usando esta práctica función de bucle for
:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] for row in a: for elem in row: print(elem, end=' ') print()
Naturalmente, para generar una sola línea puede usar method join()
:
for row in a: print(' '.join([str(elem) for elem in row]))
Así es como puede usar 2 bucles anidados para calcular la suma de todos los números en la lista bidimensional:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] s = 0 for i in range(len(a)): for j in range(len(a[i])): s += a[i][j] print(s)
O lo mismo con iterar por elementos, no por las variables i
y j
:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] s = 0 for row in a: for elem in row: s += elem print(s)
2. Listas anidadas: creando
Supongamos que se dan dos números: el número de filas de n
el número de columnas m
. Debe crear una lista de tamaño n
× m
, rellenada con, por ejemplo, ceros.
La solución obvia parece ser incorrecta:
a = [[0] * m] * n
Esto se puede ver fácilmente si establece el valor de a[0][0]
en 5
, y luego imprime el valor de a[1][0]
- también será igual a 5. El motivo es, [0] * m
devuelve solo una referencia a una lista de m
ceros, pero no una lista. La repetición posterior de este elemento crea una lista de n
elementos que hacen referencia a la misma lista (al igual que la operación b = a
para listas no crea la nueva lista), por lo que todas las filas en la lista resultante son en realidad las mismas cuerda.
Usando nuestro visualizador, haga un seguimiento de la identificación de las listas. Si dos listas tienen el mismo número de identificación, en realidad es la misma lista en la memoria.
n = 3 m = 4 a = [[0] * m] * n a[0][0] = 5 print(a[1][0])
Por lo tanto, no se puede crear una lista bidimensional simplemente repitiendo una cadena. ¿Qué hacer?..
Una forma posible: puede crear una lista de n
elementos (por ejemplo, de n
ceros) y luego hacer que cada uno de los elementos sea un enlace a otra lista unidimensional de m
elementos:
n = 3 m = 4 a = [0] * n for i in range(n): a[i] = [0] * m
Otra forma (pero similar): crear una lista vacía y luego append
un nuevo elemento n
veces (este elemento debe ser una lista de longitud m
):
n = 3 m = 4 a = [] for i in range(n): a.append([0] * m)
Pero la forma más fácil es usar generator, creando una lista de n
elementos, cada uno de los cuales es una lista de m
ceros:
n = 3 m = 4 a = [[0] * m for i in range(n)]
En este caso, cada elemento se crea independientemente de los demás. La lista [0] * m
se construye n
veces como la nueva, y no se produce copia de referencias.
3. ¿Cómo se ingresa una matriz bidimensional?
Digamos que un programa toma una matriz bidimensional de entrada en forma de n
filas, cada una de las cuales contiene m
números separados por espacios. ¿Cómo fuerza al programa a leerlo? Un ejemplo de cómo puedes hacerlo:
# la primera línea de entrada es el número de filas de la matriz n = int(input()) a = [] for i in range(n): a.append([int(j) for j in input().split()])
O bien, sin utilizar llamadas sofisticadas anidadas:
# la primera línea de entrada es el número de filas de la matriz n = int(input()) a = [] for i in range(n): row = input().split() for i in range(len(row)): row[i] = int(row[i]) a.append(row)
Puedes hacer lo mismo con los generadores:
# la primera línea de entrada es el número de filas de la matriz n = int(input()) a = [[int(j) for j in input().split()] for i in range(n)]
4. Procesamiento de una matriz bidimensional: un ejemplo
Supongamos que le dan una matriz cuadrada (una matriz de n
filas n
columnas). Y supongamos que tiene que establecer elementos de la diagonal principal igual a 1 (es decir, aquellos elementos a[i][j]
para los cuales i==j
), para establecer elementos por encima de esa diagonal igual a 0, y para establecer elementos debajo de esa diagonal igual a 2. Es decir, necesita producir dicha matriz (ejemplo para n==4
):
1 0 0 0 2 1 0 0 2 2 1 0 2 2 2 1(En este caso, puede hacerlo manualmente estableciendo
a[0][0] = 1
, a[0][1] = 0
y así sucesivamente, pero no lo hará manualmente para arreglos de 100 filas y 100 columnas , que a menudo son el caso.) Estamos ansiosos por mostrarle varias formas de resolver este problema. Primero, nótese que los elementos que se encuentran sobre la diagonal principal - son los elementos a[i][j]
para los cuales i<j
, y los elementos debajo de la diagonal principal i>j
. Por lo tanto, podemos comparar los valores i
y j
, que determinan el valor de a[i][j]
. Obtenemos el siguiente algoritmo:
n = 4 a = [[0] * n for i in range(n)] for i in range(n): for j in range(n): if i < j: a[i][j] = 0 elif i > j: a[i][j] = 2 else: a[i][j] = 1 for row in a: print(' '.join([str(elem) for elem in row]))
Este algoritmo es lento: utiliza dos bucles y para cada par (i,j)
ejecuta una o dos if
instrucciones. Si complicamos el algoritmo, podremos hacerlo sin una declaración condicional.
Primero, completa la diagonal principal, para lo cual necesitaremos un bucle:
for i in range(n): a[i][i] = 1
Luego llene con ceros todos los elementos sobre la diagonal principal. Para hacer esto, para cada fila con el número i
necesita asignar un valor a a[i][j]
para j
= i+1
, ..., n-1
. Para hacer eso, necesitas bucles anidados:
for i in range(n): for j in range(i + 1, n): a[i][j] = 0
Por analogía, para j
= 0
, ..., i-1
establece los elementos a[i][j]
igual a 2
:
for i in range(n): for j in range(0, i): a[i][j] = 2
Puede combinar todo este código y recibir otra solución:
n = 4 a = [[0] * n for i in range(n)] for i in range(n): for j in range(0, i): a[i][j] = 2 a[i][i] = 1 for j in range(i + 1, n): a[i][j] = 0 for row in a: print(' '.join([str(elem) for elem in row]))
Aquí hay otra solución, que repite listas para construir las siguientes filas de la lista. La i
-ésima línea de la lista consiste en i
números 2
, seguido de un entero 1
, seguido de ni-1
ceros:
n = 4 a = [0] * n for i in range(n): a[i] = [2] * i + [1] + [0] * (n - i - 1) for row in a: print(' '.join([str(elem) for elem in row]))
Como de costumbre, puede reemplazar el ciclo con el generador:
n = 4 a = [0] * n a = [[2] * i + [1] + [0] * (n - i - 1) for i in range(n)] for row in a: print(' '.join([str(elem) for elem in row]))
5. Arrays bidimensionales: generadores anidados
Puede usar generadores anidados para crear matrices bidimensionales, colocando el generador de la lista que es una cadena, dentro del generador de todas las cadenas. Recuerde que puede crear una lista de n
filas m
columnas utilizando el generador (que crea una lista de n
elementos, donde cada elemento es una lista de m
ceros):
[[0] * m for i in range(n)]
Pero la lista interna también se puede crear usando, por ejemplo, dicho generador: [0 for j in range(m)]
. Al anidar un generador en otro, obtenemos
[[0 for j in range(m)] for i in range(n)]
¿Cómo está relacionado con nuestro problema? El caso es que si el número 0 se reemplaza por una expresión que depende de i
(el número de línea) j
(el número de columna), se obtiene la matriz rellenada de acuerdo con alguna fórmula.
Por ejemplo, supongamos que necesita inicializar la siguiente matriz (para mayor comodidad, se agregan espacios adicionales entre los elementos):
0 0 0 0 0 0 0 1 2 3 4 5 0 2 4 6 8 10 0 3 6 9 12 15 0 4 8 12 16 20
En este conjunto, hay n = 5
filas, m = 6
columnas, y el elemento con el índice de filas i
índice de columnas j
se calcula con la fórmula a[i][j] = i * j
.
Como siempre, puedes usar un generador para crear una matriz así:
[[i * j for j in range(m)] for i in range(n)]