Shortest Path on Chessboard , Python - python

I have NxN chessboard. I have a pawn that can move 1 box up,down,right,left. There is a random exit point. What I want to do is calculate the shortest path from pawn location to exit location.
Pawn is located at random place
Obstacles are located at random place
Exit place is located at random place
I already created adjaceny array for chessboard. I did it with 2D array. For instance, obstacles are 0 where others are 1. It looks like 2D array. R is pawn. O is obstacle. X are ways that robot can go. E is exit point.
E X X O O
X X X X X
X X R O X
X X X X X
X X X X X
Array representation is
1 1 1 0 0
1 1 1 1 1
1 1 1 0 1
1 1 1 1 1
1 1 1 1 1
My question is should I traverse from R to E and create Tree implementation then calculate the minimum path (DFS, BFS) ? If so how should I do it ?. Or is there another method for that ? For instance using graphs ?

You are right, you should use BFS (Breadth-first Search). It is easy to see that DFS (Depth-first Search) would not necessarily return the shortest. However, it will still tell you whether there exists a path between your pawn and the exit.
To apply the BFS algorithm you should think of your problem as a graph without changing the actual representation of the problem:
vertices of the graphs are all tuples (x, y) that are in your 2D list and are not obstacles
two vertices are connected by an edge if they are "neighbors" in the 2D array
Here's a standard implementation of BFS in which we replace the vertices and edges as explained above.
def BFS(board, start):
queue = list()
queue.append(start)
visited = set()
# this keeps track of where did we get to each vertex from
# so that after we find the exit we can get back
parents = dict()
parents[start] = None
while queue:
v = queue[0]
if board[v[0]][v[1]] == 'E':
break
queue = queue[1:] # this is inefficient, an actual queue should be used
visited.add(v)
for u in neighbors(board, v):
if u not in visited:
parents[u] = v
queue.append(u)
# we found the exit, now we have to go through the parents
# up to the start vertex to return the path
path = list()
while v != None:
path.append(v)
v = parents[v]
# the path is in the reversed order so we reverse it
path = reverse(path)
return path
def neighbors(board, v):
diff = [(0, 1), (0, -1), (1, 0), (-1, 0)]
retval = list()
for d in diff:
newr = d[0] + v[0]
newc = d[1] + v[1]
if newr < 0 or newr >= len(board) or newc < 0 or newc >= len(board[0]):
continue
if board[newr][newc] == 'X':
continue
retval.append((newr, newc))
return retval

Related

Want algorithm to find shortest path in grid which 3*3 dimension

I need a help on below image I want to achieve below logic in Python and I am newbie in Python.
[![I need a help on below image I want to achieve below logic in Python and I am newbie in Python.][1]][1]
Any help is appreciated.
I would suggest to seek for every possible path. An example from link then to compute every possible sums and look for the smallest
import numpy as np
import copy
def findPaths(mat, path,paths, i, j):
# base case
if not mat or not len(mat):
return
(M, N) = (len(mat), len(mat[0]))
# if the last cell is reached, print the route
if i == M - 1 and j == N - 1:
paths.append(copy.deepcopy(path + [[i,j]] ))
return
# include the current cell in the path
path.append( [i,j])
# move right
if 0 <= i < M and 0 <= j + 1 < N:
findPaths(mat, path,paths, i, j + 1)
# move down
if 0 <= i + 1 < M and 0 <= j < N:
findPaths(mat, path,paths, i + 1, j)
# backtrack: remove the current cell from the path
path.pop()
if __name__ == '__main__':
mat = [ [2,5,4],
[3,2,1],
[8,0,3] ]
path = []
paths = []
x = y = 0
#find all possible paths
findPaths(mat, path,paths, x, y)
print(paths)
#get the sum of all paths
All_sums = []
for path in paths:
one_sum = 0
for p in path:
one_sum += mat[p[0]][p[1]]
All_sums.append(one_sum)
print(All_sums)
#get lower path
min_path = np.argmin(All_sums)
print(min_path)
#print the path
print(paths[min_path])
#print the val sequence
print([mat[p[0]][p[1]] for p in paths[min_path]])
We can easily solve this problem by using recursion. The idea is to start from the top-left cell of the matrix and recur for the next node (immediate right or immediate bottom cell) and keep on doing that for every visited cell until the destination is reached. Also maintain a path array to store the nodes in the current path and update the path array (including the current node) whenever any cell is visited. Now, whenever the destination (bottom-right corner) is reached, print the path array.

Python Recursion Issue (Leetcode 542)

I think I misunderstand some important concepts in Python and it is not specific to the Leetcode problem. I greatly appreciate for any help from who knows Python deeply.
The Leetcode 542 is that given a 2D array consisting of 0 and 1 only, and for each 1, find the shortest distance to reach 0. I have a dummy DFS solution:
class Solution:
def updateMatrix(self, matrix):
dists = []
for _ in range(len(matrix)):
dists.append([0]*len(matrix[0]))
for y in range(len(matrix)):
for x in range(len(matrix[0])):
if matrix[y][x] == 1:
self.min_dist = len(matrix)+len(matrix[0])
self.DFS(x, y, matrix, 0, set({}))
dists[y][x] = self.min_dist
return dists
def DFS(self, x, y, matrix, distance, visited):
if (x, y) in visited or (matrix[y][x] == 1 and distance > self.min_dist): return
if matrix[y][x] == 0:
print (x, y, "d:", distance)
print ('------')
self.min_dist = min(self.min_dist, distance)
return
print (x, y, distance)
visited.add((x, y))
if x > 0 and (x-1, y) not in visited:
self.DFS(x-1, y, matrix, distance+1, visited)
if y > 0 and (x, y-1) not in visited:
self.DFS(x, y-1, matrix, distance+1, visited)
if x < len(matrix[0])-1 and (x+1, y) not in visited:
self.DFS(x+1, y, matrix, distance+1, visited)
if y < len(matrix)-1 and (x, y+1) not in visited:
self.DFS(x, y+1, matrix, distance+1, visited)
Simply DFS until reaching 0. Every time we call DFS, distance + 1. It looks good to me. But a test case input = [[1,0,0],[0,1,1],[1,1,1]] gives me dist = [[1,0,0],[0,1,1],[1,2,3]].
If change matrix[y][x] == 1 to matrix[y][x] == 1 and x==2 and y==2 and run the above code, the output is
2 2 0
1 2 1
0 2 2
0 1 d: 3
------
1 1 2
0 1 d: 3
------
1 0 d: 3
------
2 1 3
2 0 d: 4
------
At (x,y)= (2,1) the initial distance is changed to 3. but the initial distance at (2,1) should be 1. My question is why it changes? Can anyone help me point out where I did wrong? Thanks!
You don't really need recursion for this. You can simply queue positions that need to update their neighbours and keep updating/queuing positions until no more updates are made:
def getDistances(matrix):
rows,cols = len(matrix),len(matrix[0])
maxDist = rows*cols+1 # start 1's at maximum distance
result = [[maxDist*bit for bit in row] for row in matrix]
more = { (r,c) for r,row in enumerate(matrix)
for c,bit in enumerate(row) if bit == 0}
while more: # process queue starting with zero positions
r,c = more.pop()
newDist = result[r][c]+1 # neighbours are at distance+1 from here
for nr,nc in [(r,c+1),(r,c-1),(r+1,c),(r-1,c)]: # 4 directions
if nr not in range(rows): continue
if nc not in range(cols): continue
if newDist >= result[nr][nc]: continue
result[nr][nc] = newDist # reduce neighbour's distance
more.add((nr,nc)) # queue neighbour to cascade updates
return result
output:
m = [[0,0,0,0,0,1],
[0,1,1,0,0,0],
[1,1,1,1,0,1],
[1,1,1,1,1,0]]
for r in getDistances(m): print(r)
[0, 0, 0, 0, 0, 1]
[0, 1, 1, 0, 0, 0]
[1, 2, 2, 1, 0, 1]
[2, 3, 3, 2, 1, 0]
Been taking a look at this. It seems the problem is the way the visited set is modified. I think it's being passed by reference which means by the time it tries to go (2,2) -> (2,1) the set already contains the point (2,1), i.e the preceding DFS paths have added all their points to it.
I found this article explains "Pass By Reference in Python" well - https://realpython.com/python-pass-by-reference/.
I got your test case to pass by always passing down visited.copy(), i.e self.DFS(x-1, y, matrix, distance+1, visited.copy()). I'm not a Python expert and imagine there are cleaner ways to handle this though.
First of all I want to point out that DFS, as well as BFS, is mainly used for searching in trees; indeed, you can think your matrix as a particular tree, but I wouldn't go that path for this task because you don't need to search but rather to keep track of some distance with respect to all of your neighbors, parents and children.
Moreover, with DFS you will need to traverse your matrix many times to find the minimum for every 1s and that's very inefficient.
Regarding your question, if you keep track of stack you're creating, you will get:
2 2 0
1 2 1
0 2 2
0 1 d: 3
------
back to (0, 2), with distance = 2
(1, 2) already visited
back to (1, 2) with distance = 1
1 1 2
0 1 d: 3
------
back to (1, 1) with distance = 2
1 0 d: 3
------
back to (1, 1) with distance = 2
2 1 3
2 0 d: 4
Back to your task, since you're using python I would tackle this task by using numpy, and look for 1s and 0s using np.where(matrix == 0). Then it's just a matter of doing some calculus:
import numpy as np
class Solution:
def update_matrix(self, matrix):
matrix = np.array(matrix)
x_ones, y_ones = np.where(matrix == 1)
x_zeros, y_zeros = np.where(matrix == 0)
for i in range(len(x_ones)):
temp = []
for j in range(len(x_zeros)):
temp.append(abs(x_ones[i] - x_zeros[j]) + abs(y_ones[i] - y_zeros[j]))
matrix[x_ones[i], y_ones[i]] = min(temp)
return matrix.tolist()
If you must not use external libraries, just proceed as follows:
class Solution:
def update_matrix(self, matrix):
x_ones, y_ones = [], []
x_zeros, y_zeros = [], []
# First scan to save coordinates
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == 1:
x_ones.append(i)
y_ones.append(j)
else:
x_zeros.append(i)
y_zeros.append(j)
for i in range(len(x_ones)):
temp = []
for j in range(len(x_zeros)):
temp.append(abs(x_ones[i] - x_zeros[j]) + abs(y_ones[i] - y_zeros[j]))
matrix[x_ones[i]][y_ones[i]] = min(temp)
return matrix

How to backtrack when using a iterative DFS implemented via a stack

I was completing this Leetcode problem: https://leetcode.com/problems/word-search/ and I randomly chose to implement the DFS iteratively with a while loop and a stack but I encountered some inconveniences when backtracking that I wouldn't normally occur if I had done the problem recursively i.e. I could only think of implementing a list (visited_index) to keep track of the indexes I had visited and pop values off to set the boolean matrix visited back to False when backtracking.
from collections import defaultdict
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
starting_points = defaultdict(list)
m, n = len(board), len(board[0])
for i in range(m):
for j in range(n):
starting_points[board[i][j]].append((i,j))
start = starting_points[word[0]]
visited = [[False] * n for _ in range(m)]
stack = []
directions = [(1,0), (0,-1), (-1,0), (0,1)]
for s in start:
stack.append((s[0], s[1], 0))
visited_index = [] # EXTRA LIST USED
while stack:
x, y, count = stack.pop()
while len(visited_index) > count:
i, j = visited_index.pop()
visited[i][j] = False # SETTING BACK TO FALSE WHEN BACKTRACKING
if x < 0 or x >= m or y < 0 or y >= n or visited[x][y] or board[x][y] != word[count]:
continue
else:
visited[x][y] = True
visited_index.append((x,y))
if count + 1 == len(word):
return True
for d in directions:
i, j = x + d[0], y + d[1]
stack.append((i,j, count + 1))
else:
stack.clear()
for i in range(m):
for j in range(n):
visited[i][j] = False
return False
I believe that in a recursive approach I could have reset the the visited boolean value back to False at the end of the function without the need to use an extra list. Does anyone have any suggestions to not introduce an extra data structure when doing an iterative DFS with a stack?
I would keep the parent node on the stack for as long there are children being processed. Then when all children have been processed and you pop the parent from the stack you'll have the right moment to also remove the visited mark for that parent.
One way to implement that idea is to put one more information in the tuple that you put on the stack: the last direction that was taken. You can use that info to look for the next direction to take, and if there is a valid direction available, push the current node back on the stack with that new direction, and then push the corresponding child on the stack. The latter gets some default value for that "previous" direction indication. For example -1.
I reworked your code to align with that idea:
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
stack = []
m, n = len(board), len(board[0])
for i in range(m):
for j in range(n):
if board[i][j] == word[0]:
# 4th member of tuple is previous direction taken. -1 is none.
stack.append((i, j, 1, -1)) # count=1, side=-1
visited = [[False] * n for _ in range(m)]
directions = [(1,0), (0,-1), (-1,0), (0,1)]
while stack:
x, y, count, side = stack.pop()
# perform the success-check here, so it also works for 1-letter words.
if count == len(word):
return True
visited[x][y] = True # will already be True when side > -1
# find next valid direction
found = False
while side < 3 and not found:
side += 1
dx, dy = directions[side]
i, j = x + dx, y + dy
found = 0 <= i < m and 0 <= j < n and not visited[i][j] and board[i][j] == word[count]
if not found: # all directions processed => backtrack
visited[x][y] = False
continue
stack.append((x, y, count, side)) # put node back on stack
stack.append((i, j, count + 1, -1))
return False

The longest path between two points from the array

I have a code that, using the algorithm of rotating calipers, defines two points with the longest distance.
The code takes in the first line the number of points N. And then N times takes the coordinates of the points X, Y. After displays the length of the longest distance.
For example:
INPUT
6
1 1
-1 0
-3 -1
-2 -2
2 3
4 -2
OUTPUT
7.0710678119
INPUT
6
2 2
0 -3
5 7
3 3
2 1
-1 1
OUTPUT
4.47213595499958 #my comment: from (3,3) to (5,7)
But there may be cases when 3 or more points are located on one straight line.
And then how should I act?
from math import *
def rast(x1, x2, y1, y2):
x = x2-x1
y = y2-y1
l = sqrt(pow(fabs(x), 2)+pow(fabs(y), 2));
return l
def orientation(p,q,r):
'''Return positive if p-q-r are clockwise, neg if ccw, zero if colinear.'''
return (q[1]-p[1])*(r[0]-p[0]) - (q[0]-p[0])*(r[1]-p[1])
def hulls(Points):
'''Graham scan to find upper and lower convex hulls of a set of 2d points.'''
U = []
L = []
Points.sort()
for p in Points:
while len(U) > 1 and orientation(U[-2],U[-1],p) <= 0: U.pop()
while len(L) > 1 and orientation(L[-2],L[-1],p) >= 0: L.pop()
U.append(p)
L.append(p)
return U,L
def rotatingCalipers(Points):
'''Given a list of 2d points, finds all ways of sandwiching the points
between two parallel lines that touch one point each, and yields the sequence
of pairs of points touched by each pair of lines.'''
U,L = hulls(Points)
i = 0
j = len(L) - 1
while i < len(U) - 1 or j > 0:
yield U[i],L[j]
# if all the way through one side of hull, advance the other side
if i == len(U) - 1: j -= 1
elif j == 0: i += 1
# still points left on both lists, compare slopes of next hull edges
# being careful to avoid divide-by-zero in slope calculation
elif (U[i+1][1]-U[i][1])*(L[j][0]-L[j-1][0]) > \
(L[j][1]-L[j-1][1])*(U[i+1][0]-U[i][0]):
i += 1
else: j -= 1
def diameter(Points):
'''Given a list of 2d points, returns the pair that's farthest apart.'''
diam,pair = max([((p[0]-q[0])**2 + (p[1]-q[1])**2, (p,q))
for p,q in rotatingCalipers(Points)])
return pair
n=int(input())
dots = []
for i in range(n):
tmp = [int(j) for j in input().split()]
dots.append([tmp[0],tmp[1]])
tmp = diameter(dots)
d1,d2=tmp[0],tmp[1]
print(rast(d1[0],d2[0],d1[1],d2[1]))
Once you have made the convex hull, you will need to sort the lines by the longest. In my example you will have the red line and after this the blue with arrow.
Take the longest line (red) and get its angle. Foreach point in the convex hull, check if the line between S and P is equal to the angle. If so calculate the distance of both lines SP & EP, if one is longer than the blue line, that is the longest line, you can stop. Else, ignore the red line and take the next longest. When there are no equal angles, you can stop.

Why this DFS way not working?

Problem description:
Given a 2D grid, each cell is either a wall 'W', an enemy 'E' or empty '0' (the number zero), return the maximum enemies you can kill using one bomb.
The bomb kills all the enemies in the same row and column from the planted point until it hits the wall since the wall is too strong to be destroyed.
Note that you can only put the bomb at an empty cell.
Example:
For the given grid
0 E 0 0
E 0 W E
0 E 0 0
return 3. (Placing a bomb at (1,1) kills 3 enemies)
My DFS solution:
def maxKilledEnemies(grid):
"""
:type grid: List[List[str]]
:rtype: int
"""
l_row, l_col = len(grid), len(grid[0])
visited = [[False] * l_col for _ in range(l_row)] #using this array to avoid duplicate traverse.
def dfs(i, j):
if 0 <= i < l_row and 0 <= j < l_col and not visited[i][j]:
visited[i][j] = True
if grid[i][j] == 'W': #wall return 0
return 0
elif grid[i][j] == '0': #0 means we just ignore this cell and traverse it adjacents
top_val = dfs(i - 1, j)
down_val = dfs(i + 1, j)
left_val = dfs(i, j - 1)
right_val = dfs(i, j + 1)
return left_val + right_val + top_val + down_val
elif grid[i][j] == 'E': # Enemy and we add it by 1
top_val = dfs(i - 1, j)
down_val = dfs(i + 1, j)
left_val = dfs(i, j - 1)
right_val = dfs(i, j + 1)
return left_val + right_val + top_val + down_val + 1
return 0
ret = [0]
for i in range(l_row):
for j in range(l_col):
if not visited[i][j] and grid[i][j] == '0':
val = dfs(i, j)
ret[0] = max(val, ret[0])
return ret[0]
Solution().maxKilledEnemies([["0","E","0","0"],["E","0","W","E"],["0","E","0","0"]]) #return 4 but expect 3.
The idea is quit simple that for every cell which num is 0, we traverse it by 4
directions(Top/Down/Left/Right).
I know there are other ways to solve it more smarter. But I would like to figure out why my way not working?
There are at least three errors in your code:
In each recursion step, you explore all possible directions. This makes your search behave like a flood-fill, but you want to start in all directions from the bomb location you're checking only. After that, recurse (or search iteratively) in the given direction.
You don't check whether the possible bomb location is empty, so your code could place the bomb on an enemy.
You don't reset the visited array between searches, so that effectively only cell (0, 0) is assessed.
One solution is to have two functions,
one for the possible bomb location. Check here whether the cell is empty. If yes, count the victims by recursing north, east, south and west.
One for the "rays" of the bomb. Travel in the given direction and count the enemies until you hit a wall. Because the ray travels in only one direction, you don't need the visited array any longer.
This isn't really depth first search, so instead of calling the second function recursively, you could just use loops.
Probably because the use of DFS is not a solution. When you use DFS you search all reachable spaces in the grid but for your question you should just search the directly horizontal or vertical spaces.
Ex: When using DFS at [0][0] you will find all enemies, not only the ones at [0][1] and [1][0].

Categories