Obtenez tous les diagonales dans une matrice/liste de listes en Python
je cherche un moyen pythonique pour obtenir toutes les diagonales d'une matrice (carrée), représentée comme une liste de listes.
supposons que j'ai la matrice suivante:
matrix = [[-2, 5, 3, 2],
[ 9, -6, 5, 1],
[ 3, 2, 7, 3],
[-1, 8, -4, 8]]
puis les grandes diagonales sont faciles:
l = len(matrix[0])
print [matrix[i][i] for i in range(l)] # [-2, -6, 7, 8]
print [matrix[l-1-i][i] for i in range(l-1,-1,-1)] # [ 2, 5, 2, -1]
Mais j'ai du mal à trouver un moyen de générer toutes les diagonales. La sortie que je cherche est:
[[-2], [9, 5], [3,-6, 3], [-1, 2, 5, 2], [8, 7, 1], [-4, 3], [8],
[2], [3,1], [5, 5, 3], [-2, -6, 7, 8], [9, 2, -4], [3, 8], [-1]]
6 réponses
il y a probablement de meilleures façons de le faire dans numpy que ci-dessous, mais je ne suis pas trop familier avec elle encore:
import numpy as np
matrix = np.array(
[[-2, 5, 3, 2],
[ 9, -6, 5, 1],
[ 3, 2, 7, 3],
[-1, 8, -4, 8]])
diags = [matrix[::-1,:].diagonal(i) for i in range(-3,4)]
diags.extend(matrix.diagonal(i) for i in range(3,-4,-1))
print [n.tolist() for n in diags]
Sortie
[[-2], [9, 5], [3, -6, 3], [-1, 2, 5, 2], [8, 7, 1], [-4, 3], [8], [2], [3, 1], [5, 5, 3], [-2, -6, 7, 8], [9, 2, -4], [3, 8], [-1]]
modifier : mise à jour pour généraliser pour n'importe quelle taille de matrice.
import numpy as np
# Alter dimensions as needed
x,y = 3,4
# create a default array of specified dimensions
a = np.arange(x*y).reshape(x,y)
print a
print
# a.diagonal returns the top-left-to-lower-right diagonal "i"
# according to this diagram:
#
# 0 1 2 3 4 ...
# -1 0 1 2 3
# -2 -1 0 1 2
# -3 -2 -1 0 1
# :
#
# You wanted lower-left-to-upper-right and upper-left-to-lower-right diagonals.
#
# The syntax a[slice,slice] returns a new array with elements from the sliced ranges,
# where "slice" is Python's [start[:stop[:step]] format.
# "::-1" returns the rows in reverse. ":" returns the columns as is,
# effectively vertically mirroring the original array so the wanted diagonals are
# lower-right-to-uppper-left.
#
# Then a list comprehension is used to collect all the diagonals. The range
# is -x+1 to y (exclusive of y), so for a matrix like the example above
# (x,y) = (4,5) = -3 to 4.
diags = [a[::-1,:].diagonal(i) for i in range(-a.shape[0]+1,a.shape[1])]
# Now back to the original array to get the upper-left-to-lower-right diagonals,
# starting from the right, so the range needed for shape (x,y) was y-1 to -x+1 descending.
diags.extend(a.diagonal(i) for i in range(a.shape[1]-1,-a.shape[0],-1))
# Another list comp to convert back to Python lists from numpy arrays,
# so it prints what you requested.
print [n.tolist() for n in diags]
Sortie
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[0], [4, 1], [8, 5, 2], [9, 6, 3], [10, 7], [11], [3], [2, 7], [1, 6, 11], [0, 5, 10], [4, 9], [8]]
commence par les diagonales qui montent à droite.
Si (x,y) est une coordonnée rectangulaire à l'intérieur de la matrice, vous voulez transformer en/à partir d'un schéma de coordonnées (p,q), Où p est le nombre de la diagonale et q est l'index le long de la diagonale. (Donc p=0 est la diagonale [-2], p=1 est la diagonale [9,5], p=2 est la diagonale [3,-6,3], et ainsi de suite.)
pour transformer a(p, q) en un (x,y), Vous pouvez utiliser:
x = q
y = p - q
Essayez de brancher les valeurs de p et q pour voir comment cela fonctionne.
maintenant tu fais juste une boucle... Pour p de 0 à 2N-1, et q de max (0, p-N+1) À min(p, N-1). Transformer p, q en x, y et imprimer.
ensuite pour les autres diagonales, répéter les boucles mais utiliser une transformation différente:
x = N - 1 - q
y = p - q
(ce qui fait basculer la matrice de gauche à droite.)
Désolé, je n'ai pas codé ça en Python. :- )
c'est pour Moe
, qui a demandé une question similaire .
je commence par faire des fonctions simples pour copier des lignes ou des colonnes de n'importe quelle matrice rectangulaire.
def get_rows(grid):
return [[c for c in r] for r in grid]
def get_cols(grid):
return zip(*grid)
avec ces deux fonctions j'obtiens alors les diagonales en ajoutant un tampon croissant/décroissant au début/fin de chaque ligne. J'obtiens alors les colonnes de cette grille tamponnée, puis j'enlève le tampon sur chaque colonne ensuite. ie)
1 2 3 |X|X|1|2|3| | | |1|2|3|
4 5 6 => |X|4|5|6|X| => | |4|5|6| | => [[7],[4,8],[1,5,9],[2,6],[3]]
7 8 9 |7|8|9|X|X| |7|8|9| | |
.
def get_backward_diagonals(grid):
b = [None] * (len(grid) - 1)
grid = [b[i:] + r + b[:i] for i, r in enumerate(get_rows(grid))]
return [[c for c in r if not c is None] for r in get_cols(grid)]
def get_forward_diagonals(grid):
b = [None] * (len(grid) - 1)
grid = [b[:i] + r + b[i:] for i, r in enumerate(get_rows(grid))]
return [[c for c in r if not c is None] for r in get_cols(grid)]
je suis tombé sur une autre solution intéressante à cette question. La ligne, la colonne, la diagonale avant et arrière peuvent toutes être immédiatement découvertes en regardant une combinaison de x et Y.
Row = x Column = y F-Diag = x+y B-Diag = x-y B-Diag` = -MIN+x-y
| 0 1 2 | 0 1 2 | 0 1 2 | 0 1 2 | 0 1 2
--|--------- --|--------- --|--------- --|--------- --|---------
0 | 0 1 2 0 | 0 0 0 0 | 0 1 2 0 | 0 1 2 0 | 2 3 4
1 | 0 1 2 1 | 1 1 1 1 | 1 2 3 1 |-1 0 1 1 | 1 2 3
2 | 0 1 2 2 | 2 2 2 2 | 2 3 4 2 |-2 -1 0 2 | 0 1 2
à partir du diagramme, vous pouvez voir que chaque diagonale et axe est identifiable de façon unique à l'aide de ces équations. Prendre chaque numéro unique de chaque table et créer un conteneur pour identifiant.
notez que les diagonales en arrière ont été décalage pour commencer à un indice zéro, et que la longueur des diagonales avant est toujours égale à la longueur des diagonales arrière.
test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]
max_col = len(test)
max_row = len(test[0])
cols = [[] for i in range(max_col)]
rows = [[] for i in range(max_row)]
fdiag = [[] for i in range(max_col + max_row - 1)]
bdiag = [[] for i in range(len(fdiag))]
min_bdiag = -max_col + 1
for y in range(max_col):
for x in range(max_row):
cols[y].append(test[y][x])
rows[x].append(test[y][x])
fdiag[x+y].append(test[y][x])
bdiag[-min_bdiag+x-y].append(test[y][x])
print(cols)
print(rows)
print(fdiag)
print(bdiag)
qui imprimera
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]
[[1], [2, 4], [3, 5, 7], [6, 8, 10], [9, 11], [12]]
[[10], [7, 11], [4, 8, 12], [1, 5, 9], [2, 6], [3]]
j'ai fini par réinventer cette roue récemment. Voici une méthode facile à réutiliser / étendre pour trouver les diagonales dans une liste carrée de listes:
def get_diagonals(grid, bltr = True):
dim = len(grid)
assert dim == len(grid[0])
return_grid = [[] for total in xrange(2 * len(grid) - 1)]
for row in xrange(len(grid)):
for col in xrange(len(grid[row])):
if bltr: return_grid[row + col].append(grid[col][row])
else: return_grid[col - row + (dim - 1)].append(grid[row][col])
return return_grid
indices de liste hypothétiques:
00 01 02 03
10 11 12 13
20 21 22 23
30 31 32 33
puis le réglage bltr = True
(la valeur par défaut), renvoie le diagonales de bas-gauche à haut-droite, i.e.
00 # row + col == 0
10 01 # row + col == 1
20 11 02 # row + col == 2
30 21 12 03 # row + col == 3
31 22 13 # row + col == 4
32 23 # row + col == 5
33 # row + col == 6
setting bltr = False
, renvoie les diagonales de bas-gauche à haut-droite, i.e.
30 # (col - row) == -3
20 31 # (col - row) == -2
10 21 32 # (col - row) == -1
00 11 22 33 # (col - row) == 0
01 12 23 # (col - row) == +1
02 13 # (col - row) == +2
03 # (col - row) == +3
Voici une version exécutable utilisant la matrice D'entrée de OP.
cela ne fonctionne que pour les inscriptions de largeur et de hauteur égales. Mais il ne repose pas sur des tiers.
matrix = [[11, 2, 4],[4, 5, 6],[10, 8, -12]]
# only works for diagnoals of equal width and height
def forward_diagonal(matrix):
if not isinstance(matrix, list):
raise TypeError("Must be of type list")
results = []
x = 0
for k, row in enumerate(matrix):
# next diag is (x + 1, y + 1)
for i, elm in enumerate(row):
if i == 0 and k == 0:
results.append(elm)
break
if (x + 1 == i):
results.append(elm)
x = i
break
return results
print 'forward diagnoals', forward_diagonal(matrix)