问题
I have a list made of lists, to create a matrix, mostly filled with zeros and some other numbers like this
a = [[0,0,0,1,0,0,0,0],
[3,0,0,2,1,0,0,0],
[3,0,0,0,0,1,0,0],
[3,5,0,4,0,0,0,0]]
how could I make a function that takes that variable, and find if there is a number repeated 3 times in the diagonals?
For example that It returns True because of the 1. I tried to implement a function like this:
def find_diagonal(matriz):
for x in range(len(matrix)):
for for a in range(len(x)):
if matriz[x][a] != 0:
encontrar = matriz[x][a]
if matriz[x+1][a-1] == encontrar:
if matriz[x+2][a-2] == encontrar:
return True
and repeat the conditions after the if matriz[x][a] != 0
with the other diagonals (this one looks for the ones directing southwest).
But besides from not working with every example (sometimes it works once, and stops working with the same example), and at the edges always raises index errors because the list checks for rows over the top.
回答1:
As @ShadowRanger pointed out it's probably easier to use a flattened list and use slicing with a step that's +/- 1 of the rowlen (this gives you the items that are diagonal):
def has_diagonals(listoflist, n=3):
rowlen = len(listoflist[0])
numrows = len(listoflist)
l = [i for row in listoflist for i in row]
for rownr in range(numrows + 1 - n):
# Diagonals to the lower right
for start in range(rowlen + 1 - n):
lr_diag = l[start+rownr*rowlen::rowlen+1][:n]
print(lr_diag)
first = lr_diag[0]
if first == 0: # don't check for zero-diagonals
continue
if all(item == first for item in lr_diag): # are all items equal
print('match', lr_diag)
# Diagonals to the lower left
for start in range(n - 1, rowlen):
rl_diag = l[start+rownr*rowlen::rowlen-1][:n]
print(rl_diag)
first = rl_diag[0]
if first == 0: # don't check for zero-diagonals
continue
if all(item == first for item in rl_diag): # are all items equal
print('match', rl_diag)
I included some print
-calls that I used to visualize the inner workings, I think these might help you to understand what is happening. The print
s with 'match'
should be replaced by a return True
or whatever you want the function to return.
It's possible to make this more efficient, but it should be correct (at least for your a
-matrix)
回答2:
How about this:
def check_diag(a, y, x):
"""Take a[y][x] and check if it starts a diagonal"""
result = False
# We search downwards, there should be at least 2 more rows
if y > len(a)-2:
return False
if x < len(a[y])-2:
# Searching SE direction if possible
result |= (a[y][x] == a[y+1][x+1] == a[y+2][x+2])
if x > 1:
# Searching SW direction if possible
result |= (a[y][x] == a[y+1][x-1] == a[y+2][x-2])
return result
def has_diag(a):
# Take all non-zero elements and check if they meet the diag condition
return any([check_diag(a,y,x)
for y in range(len(a))
for x in range(len(a[y]))
if a[y][x]])
This will also find longer diagonals, not sure if you want that.
Edit: more "brutal" version with less indexing magic:
def check_diag2(a, y, x):
"""Take a[y][x] and check if it starts a diagonal"""
try:
if (a[y][x] == a[y+1][x+1] == a[y+2][x+2]):
return True
except IndexError:
pass
try:
if (a[y][x] == a[y+1][x-1] == a[y+2][x-2]):
return True
except IndexError:
pass
return False
来源:https://stackoverflow.com/questions/44013618/find-pattern-in-matrix-made-of-lists-python-3