Converting BFS to Djikstra to find the shortest path - python

How can I convert the following BFS algorithm to find the shortest path using Djikstra? I know that I need to update distances of neighbors, but I am confused on how exactly to extend the following BFS with it. The constraint is we can move only along L shaped paths between two nodes.
from collections import deque
N = 8
board_p = [[(-1,-1) for f in range(0,N)] for i in range(0,N)]
def Adjacents(u):
adj = []
for e in [(-2,-1),(-2,1),(2,1),(2,-1),(-1,-2),(1,-2),(-1,2),(1,2)]:
v = (u[0] + e[0], u[1] + e[1])
if v[0] >= 0 and v[0] < N and v[1] >= 0 and v[1] < N: adj.append(v)
return adj;
def Moves(s,t):
q = deque()
q.append(s)
board_p[s[0]][s[1]] = s # "root" of BFS-traversal points to it self (avoid loop over "back-edge" to s)
while q:
u = q.popleft()
if u == t: break
for v in Adjacents(u):
if board_p[v[0]][v[1]] == (-1,-1):
board_p[v[0]][v[1]] = u
q.append(v)
# walk the path back (using parent "pointers")
path = [(t)]
while t != s:
t = board_p[t[0]][t[1]]
path.append(t)
path.reverse()
return path
print(Moves((1,1),(5,5)))

Related

How do I return the length of the path found by Breadth First Search?

I'm trying to augment the typical BFS algorithm to also return the length of the path it found. Here's what I've written so far:
from collections import deque
length = 1
visited = set()
q = deque()
visited.add("Start")
while q:
v = q.popleft()
length += 1
if v == "End":
return length
else:
for neighbor in graph[v]:
if neighbor not in visited:
visited.add(neighbor)
q.append(neighbor)
return 0
Here, I'm assuming a graph of strings. An example graph could be
graph = { "Start" : ["A"],
"A" : ["B"],
"B" : ["End"],
"End" : []
}
I understand that this is wrong as it will count the entire size of graph and not just the length of the path to the goal. How can I modify it to return the length of the path to the goal it found?
Based on the advice of #Karl Knechtel, I was able to write it!
from collections import deque
visited = set()
q = deque([("Start", 0)])
visited.add("Start")
while q:
v, length = q.popleft()
if v == "End":
return length + 1
else:
for neighbor in graph[v]:
if neighbor not in visited:
visited.add(neighbor)
q.append((neighbor, length + 1))
return 0

Shortest path Graph BFS python

Trying to return the int for shortest path in a graph, using BFS. The idea is to use a q, append into the q as [node,distance] and then when we traverse increase distance and keep track of the count and when we hit our destination first time that means we found shortest path so we return that. But I got error " currNode,distance = q.popleft()
ValueError: not enough values to unpack (expected 2, got 1)"
def shortestPath(graph,nodeA,nodeB):
q = deque((nodeA,0))
visited = set(nodeA)
while q:
currNode,distance = q.popleft()
if currNode == nodeB:
return distance
for neighbor in graph[currNode]:
if neighbor not in visited:
visited.add(neighbor)
q.append([neighbor,distance+1])
return -1
graph_three = {
'w':['x','v'],
'x':['w','y'],
'y':['x','z'],
'z':['y','v'],
'v':['z','w']
}
print(shortestPath(graph_three,'w','z'))
Deque takes an iterable of elements as input, you gave it a tuple so your deque will contains two elements instead of the expected one tuple of two elements.
fix line 2 into:
q = deque([(nodeA,0)])
also here is a cleaner implementation of BFS:
def shortestPath(graph, root, target):
if root == target: return 0
q = collections.deque(root)
visited = set(root)
distance = 0
while q:
for _ in range(len(q)):
node = q.popleft()
for neighbor in graph[node]:
if neighbor == target:
return distance + 1
elif neighbor not in visited:
visited.add(neighbor)
q.append(neighbor)
distance += 1
return -1

Absolute value of a difference in gurobi with abs_() not working

I've got a problem with a gurobi program which is supposed to find a certain number of distinct shortest paths in a graph with a length not exceeding maxLength by using an LP. For making sure that the different paths are distinct I tried to sum up the number of arcs where one path is different from another. y[a,i,j] should be one if paths i and j are different in arc a and zero otherwise.
I tried to achieve that by taking the difference between x[a,i] and x[a,j] at every arc and then expect the sum over all arcs for every combination of i and j to be greater one. Everything above that are just constraints for a regular min cost flow. Somehow my problem is infeasible for any of the test instances if I want more than 1 path. Any ideas? Thanks in advance.
def findXShortestPaths(V, A, pred, succ, start, end, cost, amount, maxLength, origin, destination):
model = Model("Shortest Path")
I = range(amount)
x = model.addVars(A, I, vtype = GRB.BINARY, name = "x")
y = model.addVars(A, I, I, vtype = GRB.INTEGER, name = "y")
z = model.addVars(A,I,I,vtype=GRB.BINARY,name="z")
model.setObjective(quicksum(cost[a] * x[a, i] for a in A for i in I), GRB.MINIMIZE)
model.addConstrs(quicksum(x[a,i] for a in pred[v]) - quicksum(x[a,i] for a in succ[v]) == 0 for i in I for v in V if v != origin and v != destination)
model.addConstrs(quicksum(x[a,i] for a in succ[origin]) == 1 for i in I)
model.addConstrs(quicksum(x[a,i] for a in pred[destination]) == 1 for i in I)
model.addConstrs(x[a,i] + x[b,i] <= 1 for i in I for a in A for b in A if end[a] == start[b] and end[b] == start[a])
model.addConstrs(y[a,i,j]==x[a,i]-x[a,j] for a in A for i in I for j in I)
model.addConstrs(z[a,i,j]== abs_(y[a,i,j]) for a in A for i in I for j in I)
model.addConstrs(quicksum(z[a,i,j] for a in A) >= 1 for i in I for j in I if i != j)
model.addConstrs(quicksum(x[a,i]*cost[a] for a in A) <= maxLength for i in I)
model.optimize()

Issue with storing values to a list in a recursive function in Python

I am trying to write a Python function that stores all the paths between two points on a grid. Here is the original question and I am inspired by the answers and tried to create a similar version in Python.
I was able to successfully print out all the paths found by applying a recursive function, but the problem was that when I needed them to be stored in a list, I got an list of empty list. I have tried using global variables and local variables as the list for storage, and they gave the same results. Could anyone please comment on my code below and suggest on where I did incorrectly?
Many thanks!
import numpy as np
#initialize a matrix
grid = np.arange(0, 1225).reshape((35,35))
m = 10
n = 5
#starting point
s = (0, 0)
#target point
d = (n, m)
results = []
#a function to find the next step in the path
def next_step(x, y):
step = []
dx = [0, 1]
dy = [1, 0]
for i in range(2):
next_x = x + dx[i]
next_y = y + dy[i]
if next_x >= 0 and next_x <= n and next_y >= 0 and next_y <= m:
step.append((next_x, next_y))
return step
def print_all_path(s, d, visited, path):
global results
visited[s[0]][s[1]] = True
path.append(grid[s[0]][s[1]])
if s == d:
results.append(path)
print(path)
else:
for step in next_step(s[0], s[1]):
if visited[step[0],step[1]] == False:
print_all_path(step, d, visited, path)
path.pop()
visited[s[0]][s[1]] = False
def print_path(s, d):
visited = np.zeros((35,35), dtype = bool)
path = []
print_all_path(s, d, visited, path)
print_path(s, d)
The problem is probably that when you append, you only append path I suspect you did something like this:
# global
all_lists = []
# your functions
...
def print_all_path(s, d, visited, path):
global results
visited[s[0]][s[1]] = True
path.append(grid[s[0]][s[1]])
if s == d:
results.append(path)
print(path)
all_lists.append(path)
...
However, path is still linked to the original path variable.
You can solve this by using:
all_lists.append(path + [])
This copies the list and removes the link
So the overall program is now
import numpy as np
#initialize a matrix
grid = np.arange(0, 1225).reshape((35,35))
m = 10
n = 5
#starting point
s = (0, 0)
#target point
d = (n, m)
results = []
all_paths = []
#a function to find the next step in the path
def next_step(x, y):
step = []
dx = [0, 1]
dy = [1, 0]
for i in range(2):
next_x = x + dx[i]
next_y = y + dy[i]
if next_x >= 0 and next_x <= n and next_y >= 0 and next_y <= m:
step.append((next_x, next_y))
return step
def print_all_path(s, d, visited, path):
global results
visited[s[0]][s[1]] = True
path.append(grid[s[0]][s[1]])
if s == d:
results.append(path)
all_paths.append(path + [])
#print(path)
else:
for step in next_step(s[0], s[1]):
if visited[step[0],step[1]] == False:
print_all_path(step, d, visited, path)
path.pop()
visited[s[0]][s[1]] = False
def print_path(s, d):
visited = np.zeros((35,35), dtype = bool)
path = []
print_all_path(s, d, visited, path)
print_path(s, d)
print all_paths

Breadth first search: shortest reach hackerrank

Given an undirected graph consisting of N nodes (labelled 1 to N) where a node S represents the start position and an edge between any two nodes is of length 6 units in the graph. Problem here.
It is required to calculate the shortest distance from start position (Node S) to all of the other nodes in the graph.
Solution: This clearly is an application of floyd algorithm for minimum distances.
What I've tried: I have tried below code and it is passing 2 testcases but failing in all other test cases. I am at my wits end as to the sneaky bug. I just want hint towards the solution. It would be nice to provide hints to other ways to solve this with respect to complexity but I am looking for a sneaky bug with the current code.
def short_paths(cost, nodes):
for i in range(1, nodes):
for j in range(1, nodes):
for k in range(1, nodes):
if cost[i][j] > cost[i][k]+cost[k][j]:
cost[i][j] = cost[i][k]+cost[k][j]
return cost
tests = int(input())
while tests:
x = input().split(" ")
nodes, edges = int(x[0]), int(x[1])
#initialize everything with infinity
dp = [[1<<31 for i in range(nodes+1)] for i in range(nodes+1)]
#distance between self is 0
for i in range(nodes+1):
dp[i][i] = 0
while edges:
p = input().split(" ")
x, y = int(p[0]), int(p[1])
#undirected graph
dp[x][y] = 6
dp[y][x] = 6
edges -= 1
src = int(input())
dp = short_paths(dp, nodes+1)
result = []
for i in range(1, nodes+1):
if src != i:
if dp[src][i] == 1<<31:
result.append("-1")
else:
result.append(dp[src][i])
print(" ".join(str(e) for e in result))
tests -= 1
I think there is a problem in these lines:
for i in range(1, nodes):
for j in range(1, nodes):
for k in range(1, nodes):
You should iterate over k first in order for the result to be correct:
Try:
for k in range(1, nodes):
for i in range(1, nodes):
for j in range(1, nodes):
As the DP uses previous results it turns out that the order of the iteration is crucial to get the correct results.
The way I remember the order is to think that the k^th iteration of the algorithm computes the shortest path from i to j using just intermediate nodes just from positions 1 to k.
However, for this problem this O(N^3) approach will timeout. A better approach is to perform a breadth first search from the starting location which will have complexity of N+M instead.
import queue
def BFS(s):
q = queue.Queue()
q.put(s)
visited[s] = True
dist[s] = 0
while not q.empty():
u = q.get()
for v in graph[u]:
if not visited[v]:
visited[v] = True
q.put(v)
dist[v] = dist[u] + 1
Q = int(input())
for _ in range(Q):
n, m = map(int, input().split())
graph = [[] for i in range(n)]
visited = [False for i in range(n)]
dist = [-1 for i in range(n)]
for i in range(m):
u, v = map(lambda x: int(x) - 1, input().split())
graph[u].append(v)
graph[v].append(u)
s = int(input()) - 1
BFS(s)
for i in range(n):
if i == s:
continue
print(dist[i]*6 if dist[i] != -1 else '-1', end = ' ')
print()
Just use normal BFS

Categories