Related
I need to find the longest path of 0's in a 2d matrix recursively and can't figure out how to do it.( from a given (i , j) it can only move up, down, right or left)
For example this matrix:
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
print(question3_b(mat))
This should return 6 as there are communities of sizes 1,3,6.
My attempt: I created a few wrapper functions to find the maximum in a list, and a function to find the route at a given (i,j) element and add it to a list, and doing this on every point(i,j) in the matrix.
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
for i in range(rows):
for j in range(cols):
visited = zeros(rows, cols) # create a 2d list of 0's with size rows,cols
a = findcommunity(mat, (i, j), visited)
print("a=", a)
community_lengths.append(a)
print(community_lengths)
return findmax(community_lengths)
def findcommunity(mat: list, indices: tuple, visited: list): # find length of community
#global rec_counter
#rec_counter += 1
i, j = indices
rows = len(mat)
cols = len(mat[0])
if mat[i][j] == 0:
visited[i][j] = 1
if i < rows - 1:
if visited[i + 1][j] == 0:
return 1 + findcommunity(mat, (i + 1, j), visited)
if j < cols - 1:
if visited[i][j + 1] == 0:
return 1 + findcommunity(mat, (i, j + 1), visited)
if i > 0:
if visited[i - 1][j] == 0:
return 1 + findcommunity(mat, (i - 1, j), visited)
if j > 0:
if visited[i][j - 1] == 0:
return 1 + findcommunity(mat, (i, j - 1), visited)
else: # mat[i][j]!=0
return 0
def zeros(rows:int,cols:int)->list: #create a 2d matrix of zeros with size (rows,cols)
if rows==1 and cols==1:return [[0]]
if rows==1 and cols>1:return [[0]*cols]
if rows>1 and cols>1:return zeros(rows-1,cols)+zeros(1,cols)
def findmax(arr:list)->int: # find max in an array using recursion
if len(arr)==2:
if arr[0]>arr[1]:return arr[0]
else:return arr[1]
else:
if arr[0]>arr[1]:
arr[1]=arr[0]
return findmax(arr[1:])
else:
return findmax(arr[1:])
Where did I go wrong? for a matrix of 4X4 zeros, I expect it to run 16*16 times[16 times for each i,j, and there are 16 elements in the matrix]. but it only runs once.
zeros is a recursive function I made that functions like np.zeros, it creates a 2d matrix full of 0's with specified size.
It got really messy but I tried to just change your code instead of writing a new solution. You should have a look at collections deque. Saw this several times where people keep track of visited a lot easier.
I changed visited to outside of the loop and defined it with np.zeros (didn't have your function ;) ). I'm not sure if your recursive function calls at return were wrong but your if-statements were, or at least the logic behind it (or I didn't understand, also possible :) )
I changed that block completly. The first time you come across a 0 in mat the recursive part will dig into the mat as long as it finds another 0 left,right,bottom or top of it (that's the functionality behind dc and dr). That's where the community_counter is increasing. If the function is returning the last time and you jump out to the outerloop in question_3b the counter gets resetted and searchs for the next 0 (next start of another community).
import numpy as np
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
visited = np.zeros((rows, cols)) # create a 2d list of 0's with size rows,cols
community_count = 0
for row in range(rows):
for col in range(cols):
if visited[row][col]==0:
community_count,visited = findcommunity(mat, (row, col), visited, community_count)
if community_count!=0:
community_lengths.append(community_count)
community_count=0
return community_lengths
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
dr = [-1, 0, 1, 0]
dc = [0,-1, 0, 1]
for k in range(4):
rr = i + dr[k]
cc = j + dc[k]
if 0<=rr<rows and 0<=cc<cols:
if visited[rr][cc]==0 and mat[rr][cc]==0:
community_count, visited = findcommunity(mat, (rr,cc), visited, community_count)
return community_count,visited
else:
return community_count,visited
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
all_communities = question3_b(mat)
print(all_communities)
# [6, 3, 1]
print(max(all_communities))
# 6
EDIT
Here is the findcommunity function in your way. Tested it and it works aswell.
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
if i < rows - 1:
if visited[i + 1][j] == 0:
community_count, visited = findcommunity(mat, (i + 1, j), visited, community_count)
if j < cols - 1:
if visited[i][j + 1] == 0:
community_count, visited = findcommunity(mat, (i, j + 1), visited, community_count)
if i > 0:
if visited[i - 1][j] == 0:
community_count, visited = findcommunity(mat, (i - 1, j), visited, community_count)
if j > 0:
if visited[i][j - 1] == 0:
community_count, visited = findcommunity(mat, (i, j - 1), visited, community_count)
return community_count,visited
else:
return community_count,visited
Here a completely different approach, in case someone is interested.
import numpy as np
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
mat = np.array(mat)
# some padding of -1 to prevent index error
mask = np.full(np.array(mat.shape) + 2, -1)
mask[1:-1, 1:-1 ] = mat
# visiteds are set to -2
def trace(f, pt):
mask[tuple(pt)], pts = -2, [pt - 1]
pts += [trace(f, pt + d) for d in
([0, 1], [1, 0], [0, -1], [-1, 0]) if
mask[tuple(pt + d)] == f]
return pts
clusters = lambda f: {tuple(pt-1): trace(f, pt) for pt in np.argwhere(mask==f) if mask[tuple(pt)]==f}
# now call with value your looking for
print(clusters(0))
I'm trying to recover separately value R, G & B from a ppm file in three different matrices.
I wrote this script :
#Read file
feep_file = open("feep.ascii.ppm", "r")
def createMatrixRGB(filename):
matrixR = []
matrixG = []
matrixB = []
tableR = []
tableG = []
tableB = []
tmp = False
for lines in filename:
x = lines.split()
for y in x:
if(y == 0 and tmp == False):
tableR.append(y)
if(y == 1 and tmp == False):
tableG.append(y)
if(y == 2 and tmp == False):
tableB.append(y)
tmp = True
matrixR.append(tableR)
matrixG.append(tableG)
matrixB.append(tableB)
tableR = []
tableG = []
tableB = []
return matrixR, matrixB, matrixG
#Read lines
magic_number = feep_file.readline()
name_file = feep_file.readline()
dimension = feep_file.readline()
k = feep_file.readline()
print(magic_number)
ppm_matrix = createMatrixRGB(feep_file)
feep_file.close()
print(ppm_matrix)
The problem is that I have this result :
P3
([[]], [[]], [[]])
I don't see where I did something wrong.
Thanks for your help
The main issue is not converting strings to integers, and the usage of y as an index.
Since it's a coding practice, I am not going to just give a solution.
I will try to show you how to use the debugger to find the problem.
Place a breakpoint, run with Debug, and add x and y to the watch:
As you can see, the value of y is '0', and the type is str.
You can also evaluate expressions in the debugger Console (while debugging).
Paste the expression: y == 0 and tmp == False:
As you can see, the expression: y == 0 and tmp == False evaluates to False.
You may run a single step, and notice that the cursor doesn't step to tableR.append(y) (but skip to if(y == 1 and tmp == False)), because the condition is False.
The reason we are getting False is that y == '0', but not 0.
Now evaluate the expression after converting y to int:
int(y) == 0 and tmp == False:
As you can see int(y) == 0 is True.
After all that...
I don't think you have meant to use the value of y.
I think you meant to use the index of y in x.
[Note: Your conventions are wrong - normally y is the line, and x is the position in the line, but I kept your names].
Corrected code (at least partially corrected):
#Read file
feep_file = open("feep.ascii.ppm", "r")
def createMatrixRGB(filename):
matrixR = []
matrixG = []
matrixB = []
tableR = []
tableG = []
tableB = []
for lines in filename:
x = lines.split()
color_index = 0
for y in x:
if color_index % 3 == 0: # Use color_index modulo 3 (like counting 0, 1, 2, 0, 1, 2...)
tableR.append(int(y))
if color_index % 3 == 1:
tableG.append(int(y))
if color_index % 3 == 2:
tableB.append(int(y))
color_index += 1
matrixR.append(tableR)
matrixG.append(tableG)
matrixB.append(tableB)
tableR = []
tableG = []
tableB = []
return matrixR, matrixB, matrixG
#Read lines
magic_number = feep_file.readline()
name_file = feep_file.readline()
dimension = feep_file.readline()
k = feep_file.readline()
print(magic_number)
ppm_matrix = createMatrixRGB(feep_file)
feep_file.close()
print(ppm_matrix)
Result:
([[0, 0, 0, 15], [0, 0, 0, 0], [0, 0, 0, 0], [15, 0, 0, 0]], [[0, 0, 0, 15], [0, 7, 0, 0], [0, 0, 7, 0], [15, 0, 0, 0]], [[0, 0, 0, 0], [0, 15, 0, 0], [0, 0, 15, 0], [0, 0, 0, 0]])
I am currently creating minesweeper, and have got to the part where i have to create a function that once the player clicks on a cell that is empty, it reveals that cell and all other empty cells including the numbered cells that surround the cluster of empty cells ( come on guys, you know minesweeper :) my approach to programming this game, is to have a list of lists which contain values, 0 is empty, 1-8 is how many bombs are touching that cell, and 50 is a bomb. i call this grid. I then use this grid to map out the whole game. That grid is then hidden with a use of another list of lists, which all contain booleans. True is hidden, False is revealed. I call this boolgrid. for example when a player clicks on cell[2][10] the boolgrid[2][10] turns False, revealing that cell.
When an empty cell is selected, the surrounding cells have to be revealed, which in some cases, are also empty, so the surrounding cells of THAT cell need to be revealed, and so on. My problem is writing a function that supports that, i have tried many things, like creating a list of tuples of cell coordinates where the cell == 0, and then a new list to hold all of the new tuples which can eventually be used to turn all of the corresponding boolgrid cells to False. it doesnt work very well, is messy, unpythonic, takes up alot of memory.
I would be most grateful to anyone who can help me with a function that gives me some pythonic way of achieving this.
below is some stripped down code, which contains the bare elements. the code contains all 0's in the grid so every bool should turn False
# used to hold values to denote what the cell contains,
grid = [[0 for x in range(30)] for x in range(15)]
# used to check what cell is hidden, true is hidden, false is revealed,
booleangrid = [[True for x in range(30)] for x in range(15)]
list_to_hold_tuples = []
def find_connected_0_value_cells(cell):
i = 5 # used as an example in the real program these are whatever cell the player selects
j = 10 # as above
# this function should be given an argument of a cell that the player selected.
# reveal the surrounding cells, AND THEN take each surrounding cell, and go through
# the same process again, until every surrounding cell containing 0, and its surrounding
# cells containing zero have been revealed.
# i know i need some kind of loop, but cannot seem to build it.
# currently this function just reveals the cell and its immediate surrounding cells
if cell[i][j] == 0:
s = i, j + 1
t = i, j - 1
u = i - 1, j - 1
v = i - 1, j
w = i - 1, j + 1
x = i + 1, j - 1
y = i + 1, j
z = i + 1, j + 1
tempholding = [s, t, u, v, w, x, y, z]
for i in tempholding:
list_to_hold_tuples.append(i)
def reveal_empty_cells():
for a,b in list_to_hold_tuples:
booleangrid[a][b] = False
print(booleangrid)
find_connected_0_value_cells(grid)
reveal_empty_cells()
I have refactored and got the rest of it to work.
thanks #zettatekdev
grid = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 2, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 3, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 5, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 5, 0, 0, 0, 0, 0, 0]]
list_to_hold_tuples = []
list_change_boolgrid =[]
row = 6
cell = 10
booleangrid = [[True for x in range(cell)] for x in range(row)]
def find_connected_0_value_cells(a, b):
list_to_hold_tuples.append((a, b))
if grid[a][b] == 0:
coord_list = get_surrounding_coords(a, b)
for a,b in coord_list:
if check_coord_values(a, b):
if grid[a][b] != 0:
c = a,b
if c not in list_to_hold_tuples:
list_to_hold_tuples.append(c)
else:
c = a,b
if c not in list_to_hold_tuples:
find_connected_0_value_cells(a,b)
def add_surrounding_cells():
extra_coord = True
for a,b in list_to_hold_tuples:
if grid[a][b] == 0:
coord_list = get_surrounding_coords(a,b, extra_coord)
for i in coord_list:
if i not in list_change_boolgrid:
list_change_boolgrid.append(i)
else:
c = a,b
if c not in list_change_boolgrid:
list_change_boolgrid.append(c)
def reveal_empty_cells():
global booleangrid
for a, b in list_change_boolgrid:
if check_coord_values(a,b):
booleangrid[a][b] = False
def check_coord_values(a,b):
if a == -1 or a >= row:
return False
if b == -1 or b >= cell:
return False
else:
return True
def get_surrounding_coords(a, b, *extra_coord):
c = (a, b + 1)
d = (a, b - 1)
e = (a - 1, b - 1)
f = (a - 1, b)
g = (a - 1, b + 1)
h = (a + 1, b - 1)
i = (a + 1, b)
j = (a + 1, b + 1)
if extra_coord:
k = (a, b)
return [c, d, e, f, g, h, i, j, k]
return [c, d, e, f, g, h, i, j]
find_connected_0_value_cells(3,5)
add_surrounding_cells()
reveal_empty_cells()
print(booleangrid)
Ok, so I worked with your code for a bit, and I was able to make a code that works. I did adjust the grid size just to make it easier to figure out, but it should work for your original size. Just make sure you don't test with a grid of all 0s like in your posted code because it will return an error for too many recursions. Here's the code.
EDIT: I changed the code so now it reveals the surrounding numbers as well
# used to hold values to denote what the cell contains,
grid = [[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,5,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,6,0,0,0]]
# used to check what cell is hidden, true is hidden, false is revealed,
booleangrid = [[True for x in range(10)] for x in range(6)]
list_to_hold_tuples = []
def find_connected_0_value_cells(i,j):
list_to_hold_tuples.append((i,j))
if grid[i][j] == 0:
s = (i, j + 1)
t = (i, j - 1)
u = (i - 1, j - 1)
v = (i - 1, j)
w = (i - 1, j + 1)
x = (i + 1, j - 1)
y = (i + 1, j)
z = (i + 1, j + 1)
tempholding = [s, t, u, v, w, x, y, z]
for a in tempholding:
if a[0]>=0 and a[1]>=0 and a[0]<=len(grid)-1 and a[1]<=len(grid[i])-1 and grid[a[0]][a[1]]==0:
if (a[0]!=i and a[1]!=j) and (a not in list_to_hold_tuples):
find_connected_0_value_cells(a[0],a[1])
list_to_hold_tuples.append(a)
elif a[0]>=0 and a[1]>=0 and a[0]<=len(grid)-1 and a[1]<=len(grid[i])-1:
list_to_hold_tuples.append(a) #this part adds surrounding non-zero numbers
def reveal_empty_cells():
global booleangrid
for a,b in list_to_hold_tuples:
if a>=0 and a<len(booleangrid) and b>=0 and b<len(booleangrid[a]):
booleangrid[a][b] = False
for i in booleangrid:
print(i)
find_connected_0_value_cells(5,8)#Just using coordinates of 5,8 for testing, this would be click spot
reveal_empty_cells()
This function is intended to recursively navigate a maze and find the length of the shortest path. The path itself is not necessary, only the length. The maze is represented by a 2d list with values such as
0 1 0 0 0
0 0 0 1 0
0 0 0 1 0
The user starts at (0,0) and must end up at the end of the maze as defined (in my case it is the bottom right cell). 1's represent walls.
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
array[x][y] = 1
up = maze(x - 1,y,array,length + 1)
right = maze(x,y + 1,array,length + 1)
down = maze(x + 1,y,array,length + 1)
left = maze(x,y - 1,array,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print(minLength)
I designed it so that it recursively finds all possible paths from each direction (up, down, left and right), and returns the lowest value from all these paths with each step of the way. It returns inf for any path that is not valid.
For this specific array, it returns 11, which is false, it should be 9. I do not believe it is merely a mathematical error, as I tried printing each step of the way and it is not recognizing certain paths (it returns inf for paths that most definitely have options).
I can't seem to find where my code is going wrong, it seems like it should properly return the value, but in practice it does not.
array is a reference to the original array, not a local copy. See any of the on-line tutorials on how Python passes function arguments, or how it handles lists. You can see the effect by printing array in your main program after the call to maze:
Final Maze [
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 0]
]
Fixing this is relatively easy: copy the nested list and use that copy locally.
from copy import deepcopy
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
new_maze = deepcopy(array)
new_maze[x][y] = 1
up = maze(x - 1,y,new_maze,length + 1)
right = maze(x,y + 1,new_maze,length + 1)
down = maze(x + 1,y,new_maze,length + 1)
left = maze(x,y - 1,new_maze,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print("Final Maze", array)
print(minLength)
The output from this is (edited for readability again)
Final Maze [
[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 1, 0]
]
9
I am trying to write a function that takes the number or rows and columns in a grid, simulates a random walk starting in the center of the grid, and computes the number of times each intersection has been visited by the random walk. Then prints the table line by line once the random walk moves outside the grid
So far I have this but i cant get it to work right.
def manhattan(x,y):
'int,int==>nonetype'
import random
res=[]
for i in range(x):
res.append([])
for i in res:
for j in range(y):
i.append(0)
position=(x//2+1,y//2+1)
z=position[0]
v=position[1]
while z!=-1 or z!=x or v!=-1 or v!=y:
direction=random.randrange(1,5)
if direction==1:
v+=1
elif direction==2:
z+=1
elif direction==3:
v-=1
else:
z-=1
for i in range(len(res)):
if i ==z:
res[i]+=1
for j in range(i):
if v==j:
i[j]+=1
for i in res:
print(i)
It should read when done:
manhattan(5,11)
[0,0,0,0,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,0,0,0,0]
[0,0,0,0,0,1,1,1,1,2,2]
[0,0,0,0,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,0,0,0,0]
You were very close, try the following:
def manhattan(x,y):
'int,int==>nonetype'
import random
res=[]
for i in range(x):
res.append([])
for i in res:
for j in range(y):
i.append(0)
position=(x//2+1,y//2+1)
z=position[0]
v=position[1]
while z!=-1 and z!=x and v!=-1 and v!=y:
res[z][v] += 1
direction=random.randrange(1,5)
if direction==1:
v+=1
elif direction==2:
z+=1
elif direction==3:
v-=1
else:
z-=1
for i in res:
print(i)
Nothing is different until the while loop, and there are only a couple of changes. First you need to use and instead of or in the loop condition check since you want to exit if any of those conditions are met.
The other change was to remove the for loop from the bottom of the while loop and replace it by res[z][v] += 1, this works because z and v represent the intersection and you already initialized res to be a two-dimensional list of all the intersections so looping is unnecessary. I also moved this up to the top of the loop because otherwise you might try to modify res after moving past the boundary.
Here is a bit less verbose version that uses random.choice instead of your chained elif statements. I found it helpful when learning python to see the same problem in different ways, so here is a pure python and a numpy + python implementation.
Pure Python
import random
def manhattan(n,m):
grid = [[0,]*m for _ in xrange(n)]
directions = [[-1,0],[1,0],[0,-1],[0,1]]
pt = [n//2, m//2]
while pt[0]>=0 and pt[0]<n and pt[1]>=0 and pt[1]<m:
grid[pt[0]][pt[1]] += 1
d = random.choice(directions)
pt[0] += d[0]
pt[1] += d[1]
return grid
for row in manhattan(5,11):
print row
This gives, for example,
[0, 0, 0, 1, 3, 3, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 3, 3, 2, 0, 0, 0]
[0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0]
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0]
Python + numpy
import numpy as np
import random
def manhattan(n,m):
grid = np.zeros((n,m),dtype=int)
directions = [[-1,0],[1,0],[0,-1],[0,1]]
pt = np.array([n//2, m//2])
while (pt>=0).all() and (pt<grid.shape).all():
grid[pt[0],pt[1]] += 1
pt += random.choice(directions)
return grid
print manhattan(5,11)