LeetCode: Flood Fill, Recursion in For Loop stuck in endless loop - python

I was working on this specific LeetCode problem and I encountered a problem where I would be stuck recursing. The way I understand it, if an input type is mutable, the input should be pass by reference, so they should be referencing the same thing. Can someone explain how my method breaks? I really want to try solving this problem using recursion, but I don't understand how to do it using my method. My code first finds north, east,south,west, and then determines if they are valid. It then determines if among those directions if they have the same count as the original node.
Of those that have the same count as the original node, I need to recurse on those and repeat the process until all nodes have the value of newColor
https://leetcode.com/problems/flood-fill/
class Solution:
def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
top = (sr-1, sc)
down = (sr+1, sc)
left = (sr, sc-1)
right = (sr, sc+1)
# Possible Directions
posDirec = [direc for direc in [top,down,left,right] if direc[0] >=0 and direc[1] >=0 and direc[0] < len(image) and direc[1] < len(image[0])]
# Neighbors that we can traverse
posNeigh = [e for e in posDirec if image[e[0]][e[1]] == image[sr][sc]]
image[sr][sc] = newColor
# print(image, '\n')
print(len(posNeigh), posNeigh, image)
if len(posNeigh) == 0:
pass
else:
for neigh in posNeigh: #top, down,left, right of only valids
self.floodFill(image, neigh[0], neigh[1], newColor)
return image
At the very end, my program should return the image. I want to return the image at the end, however, my code ends up stuck in recursion

Take a look at the following line:
# Neighbors that we can traverse
posNeigh = [e for e in posDirec if image[e[0]][e[1]] == image[sr][sc]]
This condition fails to account for the possibility that image[e[0]][e[1]] has already been filled in with newColor, resulting in an infinite loop between filled cells and a stack overflow.
If we change it to
posNeigh = [
e for e in posDirec
if image[e[0]][e[1]] == image[sr][sc]
and image[e[0]][e[1]] != newColor # <-- added
]
we can make sure we're not revisiting previously-filled areas.
Given that the list comprehensions have grown quite unwieldy, you might consider a rewrite:
def floodFill(self, image, sr, sc, new_color):
target_color = image[sr][sc]
image[sr][sc] = new_color
for y, x in ((sr + 1, sc), (sr, sc - 1), (sr, sc + 1), (sr - 1, sc)):
if y >= 0 and x >= 0 and y < len(image) and x < len(image[0]) and \
image[y][x] != new_color and image[y][x] == target_color:
self.floodFill(image, y, x, new_color)
return image

A mutable input does not pass by reference. The way I see it, solving it using recursion is not possible. Try an iterative solution.

Related

How do I find shortest path in maze with BFS?

I am trying to find a way to solve a maze. My teacher said I have to use BFS as a way to learn. So I made the algorithm itself, but I don't understand how to get the shortest path out of it. I have looked at others their codes and they said that backtracking is the way to do it. How does this backtracking work and what do you backtrack?
I will give my code just because I like some feedback to it and maybe I made some mistake:
def main(self, r, c):
running = True
self.queue.append((r, c))
while running:
if len(self.queue) > 0:
self.current = self.queue[0]
if self.maze[self.current[0] - 1][self.current[1]] == ' ' and not (self.current[0] - 1, self.current[1])\
in self.visited and not (self.current[0] - 1, self.current[1]) in self.queue:
self.queue.append((self.current[0] - 1, self.current[1]))
elif self.maze[self.current[0] - 1][self.current[1]] == 'G':
return self.path
if self.maze[self.current[0]][self.current[1] + 1] == ' ' and not (self.current[0], self.current[1] + 1) in self.visited\
and not (self.current[0], self.current[1] + 1) in self.queue:
self.queue.append((self.current[0], self.current[1] + 1))
elif self.maze[self.current[0]][self.current[1] + 1] == 'G':
return self.path
if self.maze[self.current[0] + 1][self.current[1]] == ' ' and not (self.current[0] + 1, self.current[1]) in self.visited\
and not (self.current[0] + 1, self.current[1]) in self.queue:
self.queue.append((self.current[0] + 1, self.current[1]))
elif self.maze[self.current[0] + 1][self.current[1]] == 'G':
return self.path
if self.maze[self.current[0]][self.current[1] - 1] == ' ' and not (self.current[0], self.current[1] - 1) in self.visited\
and not (self.current[0], self.current[1] - 1) in self.queue:
self.queue.append((self.current[0], self.current[1] - 1))
elif self.maze[self.current[0]][self.current[1] - 1] == 'G':
return self.path
self.visited.append((self.current[0], self.current[1]))
del self.queue[0]
self.path.append(self.queue[0])
As maze I use something like this:
############
# S #
##### ######
# #
######## ###
# #
## ##### ###
# G#
############
Which is stored in a matrix
What I eventually want is just the shortest path inside a list as output.
Since this is a coding assignment I'll leave the code to you and simply explain the general algorithm here.
You have a n by m grid. I am assuming this is is provided to you. You can store this in a two dimensional array.
Step 1) Create a new two dimensional array the same size as the grid and populate each entry with an invalid coordinate (up to you, maybe use None or another value you can use to indicate that a path to that coordinate has not yet been discovered). I will refer to this two dimensional array as your path matrix and the maze as your grid.
Step 2) Enqueue the starting coordinate and update the path matrix at that position (for example, update matrix[1,1] if coordinate (1,1) is your starting position).
Step 3) If not at the final coordinate, dequeue an element from the queue. For each possible direction from the dequeued coordinate, check if it is valid (no walls AND the coordinate does not exist in the matrix yet), and enqueue all valid coordinates.
Step 4) Repeat Step 3.
If there is a path to your final coordinate, you will not only find it with this algorithm but it will also be a shortest path. To backtrack, check your matrix at the location of your final coordinate. This should lead you to another coordinate. Continue this process and backtrack until you arrive at the starting coordinate. If you store this list of backtracked coordinates then you will have a path in reverse.
The main problem in your code is this line:
self.path.append(self.queue[0])
This will just keep adding to the path while you go in all possible directions in a BFS way. This path will end up getting all coordinates that you visit, which is not really a "path", because with BFS you continually switch to a different branch in the search, and so you end up collecting positions that are quite unrelated.
You need to build the path in a different way. A memory efficient way of doing this is to track where you come from when visiting a node. You can use the visited variable for that, but then make it a dictionary, which for each r,c pair stores the r,c pair from which the cell was visited. It is like building a linked list. From each newly visited cell you'll be able to find back where you came from, all the way back to the starting cell. So when you find the target, you can build the path from this linked list.
Some other less important problems in your code:
You don't check whether a coordinate is valid. If the grid is bounded completely by # characters, this is not really a problem, but if you would have a gap at the border, you'd get an exception
There is code repetition for each of the four directions. Try to avoid such repetition, and store recurrent expressions like self.current[1] - 1 in a variable, and create a loop over the four possible directions.
The variable running makes no sense: it never becomes False. Instead make your loop condition what currently is your next if condition. As long as the queue is not empty, continue. If the queue becomes empty then that means there is no path to the target.
You store every bit of information in self properties. You should only do that for information that is still relevant after the search. I would instead just create local variables for queue, visited, current, ...etc.
Here is how the code could look:
class Maze():
def __init__(self, str):
self.maze = str.splitlines()
def get_start(self):
row = next(i for i, line in enumerate(self.maze) if "S" in line)
col = self.maze[row].index("S")
return row, col
def main(self, r, c):
queue = [] # use a local variable, not a member
visited = {} # use a dict, key = coordinate-tuples, value = previous location
visited[(r, c)] = (-1, -1)
queue.append((r, c))
while len(queue) > 0: # don't use running as variable
# no need to use current; just reuse r and c:
r, c = queue.pop(0) # you can remove immediately from queue
if self.maze[r][c] == 'G':
# build path from walking backwards through the visited information
path = []
while r != -1:
path.append((r, c))
r, c = visited[(r, c)]
path.reverse()
return path
# avoid repetition of code: make a loop
for dx, dy in ((-1, 0), (0, -1), (1, 0), (0, 1)):
new_r = r + dy
new_c = c + dx
if (0 <= new_r < len(self.maze) and
0 <= new_c < len(self.maze[0]) and
not (new_r, new_c) in visited and
self.maze[new_r][new_c] != '#'):
visited[(new_r, new_c)] = (r, c)
queue.append((new_r, new_c))
maze = Maze("""############
# S #
##### ######
# #
######## ###
# #
## ##### ###
# G#
############""")
path = maze.main(*maze.get_start())
print(path)
See it run on repl.it

backtracking not trying all possibilities

so I've got a list of questions as a dictionary, e.g
{"Question1": 3, "Question2": 5 ... }
That means the "Question1" has 3 points, the second one has 5, etc.
I'm trying to create all subset of question that have between a certain number of questions and points.
I've tried something like
questions = {"Q1":1, "Q2":2, "Q3": 1, "Q4" : 3, "Q5" : 1, "Q6" : 2}
u = 3 #
v = 5 # between u and v questions
x = 5 #
y = 10 #between x and y points
solution = []
n = 0
def main(n_):
global n
n = n_
global solution
solution = []
finalSolution = []
for x in questions.keys():
solution.append("_")
finalSolution.extend(Backtracking(0))
return finalSolution
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
return finalSolution
def reject(k):
if solution[k] in solution: #if the question already exists
return True
if k > v: #too many questions
return True
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points in range (x, y+1) and k in range (u, v+1):
return True
return False
print(main(len(questions.keys())))
but it's not trying all possibilities, only putting all the questions on the first index..
I have no idea what I'm doing wrong.
There are three problems with your code.
The first issue is that the first check in your reject function is always True. You can fix that in a variety of ways (you commented that you're now using solution.count(solution[k]) != 1).
The second issue is that your accept function uses the variable name x for what it intends to be two different things (a question from solution in the for loop and the global x that is the minimum number of points). That doesn't work, and you'll get a TypeError when trying to pass it to range. A simple fix is to rename the loop variable (I suggest q since it's a key into questions). Checking if a value is in a range is also a bit awkward. It's usually much nicer to use chained comparisons: if x <= points <= y and u <= k <= v
The third issue is that you're not backtracking at all. The backtracking step needs to reset the global solution list to the same state it had before Backtracking was called. You can do this at the end of the function, just before you return, using solution[k] = "_" (you commented that you've added this line, but I think you put it in the wrong place).
Anyway, here's a fixed version of your functions:
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
solution[k] = "_" # backtracking step here!
return finalSolution
def reject(k):
if solution.count(solution[k]) != 1: # fix this condition
return True
if k > v:
return True
points = 0
for q in solution:
if q in questions:
points = points + questions[q]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for q in solution: # change this loop variable (also done above, for symmetry)
if q in questions:
points = points + questions[q]
if x <= points <= y and u <= k <= v: # chained comparisons are much nicer than range
return True
return False
There are still things that could probably be improved in there. I think having solution be a fixed-size global list with dummy values is especially unpythonic (a dynamically growing list that you pass as an argument would be much more natural). I'd also suggest using sum to add up the points rather than using an explicit loop of your own.

Python - speed up pathfinding

This is my pathfinding function:
def get_distance(x1,y1,x2,y2):
neighbors = [(-1,0),(1,0),(0,-1),(0,1)]
old_nodes = [(square_pos[x1,y1],0)]
new_nodes = []
for i in range(50):
for node in old_nodes:
if node[0].x == x2 and node[0].y == y2:
return node[1]
for neighbor in neighbors:
try:
square = square_pos[node[0].x+neighbor[0],node[0].y+neighbor[1]]
if square.lightcycle == None:
new_nodes.append((square,node[1]))
except KeyError:
pass
old_nodes = []
old_nodes = list(new_nodes)
new_nodes = []
nodes = []
return 50
The problem is that the AI takes to long to respond( response time <= 100ms)
This is just a python way of doing https://en.wikipedia.org/wiki/Pathfinding#Sample_algorithm
You should replace your algorithm with A*-search with the Manhattan distance as a heuristic.
One reasonably fast solution is to implement the Dijkstra algorithm (that I have already implemented in that question):
Build the original map. It's a masked array where the walker cannot walk on masked element:
%pylab inline
map_size = (20,20)
MAP = np.ma.masked_array(np.zeros(map_size), np.random.choice([0,1], size=map_size))
matshow(MAP)
Below is the Dijkstra algorithm:
def dijkstra(V):
mask = V.mask
visit_mask = mask.copy() # mask visited cells
m = numpy.ones_like(V) * numpy.inf
connectivity = [(i,j) for i in [-1, 0, 1] for j in [-1, 0, 1] if (not (i == j == 0))]
cc = unravel_index(V.argmin(), m.shape) # current_cell
m[cc] = 0
P = {} # dictionary of predecessors
#while (~visit_mask).sum() > 0:
for _ in range(V.size):
#print cc
neighbors = [tuple(e) for e in asarray(cc) - connectivity
if e[0] > 0 and e[1] > 0 and e[0] < V.shape[0] and e[1] < V.shape[1]]
neighbors = [ e for e in neighbors if not visit_mask[e] ]
tentative_distance = [(V[e]-V[cc])**2 for e in neighbors]
for i,e in enumerate(neighbors):
d = tentative_distance[i] + m[cc]
if d < m[e]:
m[e] = d
P[e] = cc
visit_mask[cc] = True
m_mask = ma.masked_array(m, visit_mask)
cc = unravel_index(m_mask.argmin(), m.shape)
return m, P
def shortestPath(start, end, P):
Path = []
step = end
while 1:
Path.append(step)
if step == start: break
if P.has_key(step):
step = P[step]
else:
break
Path.reverse()
return asarray(Path)
And the result:
start = (2,8)
stop = (17,19)
D, P = dijkstra(MAP)
path = shortestPath(start, stop, P)
imshow(MAP, interpolation='nearest')
plot(path[:,1], path[:,0], 'ro-', linewidth=2.5)
Below some timing statistics:
%timeit dijkstra(MAP)
#10 loops, best of 3: 32.6 ms per loop
The biggest issue with your code is that you don't do anything to avoid the same coordinates being visited multiple times. This means that the number of nodes you visit is guaranteed to grow exponentially, since it can keep going back and forth over the first few nodes many times.
The best way to avoid duplication is to maintain a set of the coordinates we've added to the queue (though if your node values are hashable, you might be able to add them directly to the set instead of coordinate tuples). Since we're doing a breadth-first search, we'll always reach a given coordinate by (one of) the shortest path(s), so we never need to worry about finding a better route later on.
Try something like this:
def get_distance(x1,y1,x2,y2):
neighbors = [(-1,0),(1,0),(0,-1),(0,1)]
nodes = [(square_pos[x1,y1],0)]
seen = set([(x1, y1)])
for node, path_length in nodes:
if path_length == 50:
break
if node.x == x2 and node.y == y2:
return path_length
for nx, ny in neighbors:
try:
square = square_pos[node.x + nx, node.y + ny]
if square.lightcycle == None and (square.x, square.y) not in seen:
nodes.append((square, path_length + 1))
seen.add((square.x, square.y))
except KeyError:
pass
return 50
I've also simplified the loop a bit. Rather than switching out the list after each depth, you can just use one loop and add to its end as you're iterating over the earlier values. I still abort if a path hasn't been found with fewer than 50 steps (using the distance stored in the 2-tuple, rather than the number of passes of the outer loop). A further improvement might be to use a collections.dequeue for the queue, since you could efficiently pop from one end while appending to the other end. It probably won't make a huge difference, but might avoid a little bit of memory usage.
I also avoided most of the indexing by one and zero in favor of unpacking into separate variable names in the for loops. I think this is much easier to read, and it avoids confusion since the two different kinds of 2-tuples had had different meanings (one is a node, distance tuple, the other is x, y).

Python: List index out of range, don't know why

I'm trying to iterate through a matrix and check for the number of cells touching the current cell that have a value of 1. I'm getting an out of bounds exception and I'm not sure why.
for x in range(0,ROWS):
for y in range(0,COLS):
#get neighbors
neighbors = []
if x!=0 & y!=COLS:
neighbors.append([x-1,y+1])
if y!=COLS:
neighbors.append([x,y+1])
if x!=ROWS & y!=COLS:
neighbors.append([x+1,y+1])
if x!=0:
neighbors.append([x-1,y])
if x!=ROWS:
neighbors.append([x+1,y])
if x!=0 & y!=0:
neighbors.append([x-1,y-1])
if y!=0:
neighbors.append([x,y-1])
if x!=ROWS & y!=0:
neighbors.append([x+1,y-1])
#determine # of living neighbors
alive = []
for i in neighbors:
if matrix[i[0]][i[1]] == 1:
alive.append(i)
I'm getting the error
IndexError: list index out of range
at this line if matrix[i[0]][i[1]] == 1:
Why is this out of range and how should I fix it?
The problem is that you are using &. This is a bit-wise AND, not a logical AND. In Python, you just use and. For example:
if x!=0 and y!=COLS:
neighbors.append([x-1,y+1])
However the real reason why using the bit-wise AND causes a problem is order of operations - it has a higher precedence!
>>> 1 != 2 & 3 != 3
True
>>> (1 != 2) & (3 != 3)
False
>>> 1 != (2 & 3) != 3
True
So even though your logic looks right, order of operations means that your code's actual behavior is drastically different than what you were expecting.
The other issue with your code is that you are checking if x and y are equal to ROWS and COLS, rather than if they are equal to ROWS-1 and COLS-1, which are the true boundary conditions.
EDIT: I THINK I FOUND IT
Upon further inspection of your code, I found that you're using if x!=0 & y!=0. This is bit-wise AND not logical AND, so it's not going to give you the result you want. Use and instead of & and see if your problem goes away.
I would refactor this slightly to make it easier to read.
for loc_x in range(ROWS):
for loc_y in range(COLS): # btw shouldn't ROWS/COLS be flipped?
# if your matrix isn't square this could be why
x_values = [loc_x]
if loc_x < ROWS: x_values.append(loc_x+1)
if loc_x > 0: x_values.append(loc_x-1)
y_values = [loc_y]
if loc_y < COLS: y_values.append(loc_y+1)
if loc_y > 0: y_values.append(loc_y-1)
neighbors = [(x,y) for x in x_values for y in y_values if (x,y) != (loc_x,loc_y)]
alive = [matrix[n[0]][n[1]] for n in neighbors if matrix[n[0]][n[1]]==1]
Try running this with your code and see if it doesn't solve the issue. If not, you may need to test further. For instance, wrap the alive definition in try/except tags that will give a better traceback.
try:
alive = ...
except IndexError:
print("Location: {},{}\nneighbors: {}\nROWS:{}\nCOLS:{}".format(x_loc,y_loc, neighbors,ROWS,COLS))
raise
As an aside, I've solved this problem before by creating objects that held the linked information and going top-to-bottom left-to-right and having each check the field to its right and below it. E.g.:
class Field(object):
def __init__(self,x,y,value):
self.x = x
self.y = y
self.value = value
self.neighbors = neighbors
class Matrix(list):
def __init__(self,size):
self.ROWS,self.COLS = map(int,size.lower().split("x"))
for y in range(ROWS):
self.append([Field(x,y,random.randint(0,1)) for x in range(COLS)])
self.plot()
def plot(self):
for row in self:
for col in row:
try:
self[row][col].neighbors.append(self[row+1][col])
self[row+1][col].neighbors.append(self[row][col])
except IndexError: pass
try:
self[row][col].neighbors.append(self[row][col+1])
self[row][col+1].neighbors.append(self[row][col])
except IndexError: pass
Of course this doesn't take care of diagonals. I'm pretty sure you can figure out how to manage those, though!!

Memory Error in Python 2.7.3

I'm trying to run this program on python which simulates a mindless being going around in space and to see whether it'll reach it's target.. But I'm getting a memory error every time i run it..--
init_posn=[0,0]
posn_final=[2,2]
obs = [[-1,1],[-1,0],[-1,1],[0,1],[1,1],[1,0],[1,-1],[0,-1]]
# algo can be improved
def obs_det(posn_x,posn_y,obs):
for e in obs:
if e[0]==posn_x & e[1]==posn_y:
return 1
return 0
posn=[]
posn.append(init_posn)
def posn_progress(posn,posn_final,obs):
i=0
non=0
while (non==0 | (posn[i][0]==posn_final[0] & posn[i][1]==posn_final[1])):
l=posn[i][0]
m=posn[i][1]
if obs_det(l,m+1,obs) == 0:
posn.append([l,m+1])
elif obs_det(l+1,m,obs) == 0:
posn.append([l+1,m])
elif obs_det(l,m-1,obs) == 0:
posn.append([l,m-1])
elif obs_det(l-1,m,obs) == 0:
posn.append([l-1,m])
else:
non=1
i=i+1
if non==1:
return 0
else:
return posn
print posn_progress(posn,posn_final,obs)
Because this looks like homework, I will answer with two hints:
How would you write a unit test to see if the obstacle detection routine is working as intended?
Read about logical and bitwise operators.
Here is some example code; it basically does a flood-fill from the starting point until it runs into the end point. Note that it keeps track of the places it has already been (seen) so it doesn't endlessly re-explore them.
dir = [(dx,dy) for dx in (-1,0,1) for dy in (-1,0,1) if (dx,dy)!=(0,0)]
def backtrack(b, seen):
if seen[b]==0:
return [b]
else:
seek = seen[b]-1
x,y = b
for dx,dy in dir:
p = (x+dx, y+dy)
if p in seen and seen[p]==seek:
return backtrack(p, seen) + [b]
def find_path(a, b, dist=0, here=None, seen=None):
if here is None: here = set([a])
if seen is None: seen = {a:0}
next = set()
for x,y in here:
for dx,dy in dir:
p = (x+dx, y+dy)
if p not in seen:
seen[p] = dist+1
next.add(p)
if b in seen: # found it!
return backtrack(b, seen)
else:
return find_path(a, b, dist+1, next, seen)
def main():
print find_path((0,0), (2,2))
if __name__=="__main__":
main()
I think your mindless being is walking in circles.
You could teach it to avoid ever going back where it came from. Then it won't loop, but may fail to find the way, even if one exists.
Or you could make it smarter, and tell it that if it goes back, it should try a different route the next time.

Categories

Resources