Reputation: 566
After thorough research and based on this , this and a lot more I was suggested to implement k shortest paths algorithm in order to find first, second, third ... k-th shortest path in a large undirected, cyclic, weighted graph. About 2000 nodes.
The pseudocode on Wikipedia is this:
function YenKSP(Graph, source, sink, K):
//Determine the shortest path from the source to the sink.
A[0] = Dijkstra(Graph, source, sink);
// Initialize the heap to store the potential kth shortest path.
B = [];
for k from 1 to K:
// The spur node ranges from the first node to the next to last node in the shortest path.
for i from 0 to size(A[i]) − 1:
// Spur node is retrieved from the previous k-shortest path, k − 1.
spurNode = A[k-1].node(i);
// The sequence of nodes from the source to the spur node of the previous k-shortest path.
rootPath = A[k-1].nodes(0, i);
for each path p in A:
if rootPath == p.nodes(0, i):
// Remove the links that are part of the previous shortest paths which share the same root path.
remove p.edge(i, i) from Graph;
// Calculate the spur path from the spur node to the sink.
spurPath = Dijkstra(Graph, spurNode, sink);
// Entire path is made up of the root path and spur path.
totalPath = rootPath + spurPath;
// Add the potential k-shortest path to the heap.
B.append(totalPath);
// Add back the edges that were removed from the graph.
restore edges to Graph;
// Sort the potential k-shortest paths by cost.
B.sort();
// Add the lowest cost path becomes the k-shortest path.
A[k] = B[0];
return A;
The main problem is that I couldn't write the correct python script yet for this (delete edges and places them back in place correctly) so I've only got this far with reliyng on Igraph as usual:
def yenksp(graph,source,sink, k):
global distance
"""Determine the shortest path from the source to the sink."""
a = graph.get_shortest_paths(source, sink, weights=distance, mode=ALL, output="vpath")[0]
b = [] #Initialize the heap to store the potential kth shortest path
#for xk in range(1,k):
for xk in range(1,k+1):
#for i in range(0,len(a)-1):
for i in range(0,len(a)):
if i != len(a[:-1])-1:
spurnode = a[i]
rootpath = a[0:i]
#I should remove edges part of the previous shortest paths, but...:
for p in a:
if rootpath == p:
graph.delete_edges(i)
spurpath = graph.get_shortest_paths(spurnode, sink, weights=distance, mode=ALL, output="vpath")[0]
totalpath = rootpath + spurpath
b.append(totalpath)
# should restore the edges
# graph.add_edges([(0,i)]) <- this is definitely not correct.
graph.add_edges(i)
b.sort()
a[k] = b[0]
return a
It's a really poor try and it returns only a list in a list
I'm not very sure anymore what am I doing and I'm very desperate with this issue already and in the last days my point of view on this was changed with 180 degrees and even once. I'm just a noob doing its best. Please help. Networkx implementation can also be suggested.
P.S. It's likely that there are no other working ways about this because we researched it here already . I've already received lots of suggestions and I owe the community alot. DFS or BFS wont work. Graph is huge.
Edit: I keep correcting the python script. In a nutshell the aim of this question is the correct script.
Upvotes: 3
Views: 11155
Reputation: 174
I had the same problem as you so I ported Wikipedia's pseudocode for Yen's algorithm for use in Python with the igraph library.
You can find it there : https://gist.github.com/ALenfant/5491853
Upvotes: 2
Reputation: 88168
There is a python implementation of Yen's KSP on Github, YenKSP. Giving full credit to the author, the heart of the algorithm is given here:
def ksp_yen(graph, node_start, node_end, max_k=2):
distances, previous = dijkstra(graph, node_start)
A = [{'cost': distances[node_end],
'path': path(previous, node_start, node_end)}]
B = []
if not A[0]['path']: return A
for k in range(1, max_k):
for i in range(0, len(A[-1]['path']) - 1):
node_spur = A[-1]['path'][i]
path_root = A[-1]['path'][:i+1]
edges_removed = []
for path_k in A:
curr_path = path_k['path']
if len(curr_path) > i and path_root == curr_path[:i+1]:
cost = graph.remove_edge(curr_path[i], curr_path[i+1])
if cost == -1:
continue
edges_removed.append([curr_path[i], curr_path[i+1], cost])
path_spur = dijkstra(graph, node_spur, node_end)
if path_spur['path']:
path_total = path_root[:-1] + path_spur['path']
dist_total = distances[node_spur] + path_spur['cost']
potential_k = {'cost': dist_total, 'path': path_total}
if not (potential_k in B):
B.append(potential_k)
for edge in edges_removed:
graph.add_edge(edge[0], edge[1], edge[2])
if len(B):
B = sorted(B, key=itemgetter('cost'))
A.append(B[0])
B.pop(0)
else:
break
return A
Upvotes: 3