I have the following graph (corresponding edge values are written in the brackets):
L0 -> L1 ('01')
L1 -> L2 ('12')
L1 -> L4 ('14')
L4 -> L5 ('45')
L2 -> L3 ('23')
L3 -> L1 ('31')
Now I want the lists of the edge values of all possible paths of a certain length starting from L0
. So, if length = 3
(excluding starting node), I should get two lists:
['01', '12', '23'] and ['01', '14', '45'].
Traversing in a cycle is allowed. I tried using a 2-level dictionary to represent the graph.
graph = {'L0': {'L1': '01'}, 'L1': {'L2': '12', 'L4': '14'}, 'L2': {'L3': '23'}, 'L3': {'L1': '31'}, 'L4': {'L5': '45'}}
def find_path(graph, start, depth):
k = 0
while k < depth:
a = graph[start]
for node in graph[start]:
start = node
path.append(a[node])
k+=1
return path
print find_path(graph, 'L0', 4)
Obviously it gives one possible path outputs. But I want all possible ones.
I'd make it recursively progress further, until the limit is hit or it cannot progress further.
# graph: Graph we are operating on
# node: Node we are starting from
# hops: Number of hops we can still do (edges we can take)
def findPaths(graph, node, hops):
# if no further hops should be done, we were successful and
# can end the recursion
if hops == 0:
yield []
return
# if the node is not in the graph, we cannot go further, so
# the current path is invalid
if node not in graph:
return
# for every node we can reach from the current
for n in graph[node]:
# find all paths we can take from here
for path in findPaths(graph, n, hops - 1):
# and concat the edge names
yield [graph[node][n]] + path
Used on your graph (in the given representation) this gives:
>>> list(findPaths(graph, 'L0', 3))
[['01', '14', '45'], ['01', '12', '23']]
>>> list(findPaths(graph, 'L0', 4))
[['01', '12', '23', '31']]
>>> list(findPaths(graph, 'L0', 2))
[['01', '14'], ['01', '12']]
I'd represent it as a simple edge:edge
dictionary:
links = {
0: [1],
1: [2, 4],
2: [3],
4: [5],
3: [1]
}
Then you can just iterate over it:
def get_all_paths(links, length, start=0):
paths = [[start]]
for i in range(length):
newpaths = []
for path in paths:
try:
for next_node in links[path[-1]]:
newpaths.append(path + [next_node])
except KeyError:
pass
paths = newpaths
return paths
get_all_paths(links, 3)
#>>> [[0, 1, 2, 3], [0, 1, 4, 5]]
Conversion of each [0, 1, 2, 3]
to [(0,1), (1,2), (2,3)]
can be done in a separate step.
It also works with your graph:
links = {'L0': {'L1': '01'}, 'L1': {'L2': '12', 'L4': '14'}, 'L2': {'L3': '23'}, 'L3': {'L1': '31'}, 'L4': {'L5': '45'}}
def get_all_paths(links, length, start=0):
paths = [[start]]
for i in range(length):
newpaths = []
for path in paths:
try:
for next_node in links[path[-1]]:
newpaths.append(path + [next_node])
except KeyError:
pass
paths = newpaths
return paths
get_all_paths(links, 3, start='L0')
#>>> [['L0', 'L1', 'L4', 'L5'], ['L0', 'L1', 'L2', 'L3']]
You can then just convert each path to the ['01', '14', '45']
form.
Since you seem to be wondering how to do that last conversion, here's a method:
paths = [['L0', 'L1', 'L4', 'L5'], ['L0', 'L1', 'L2', 'L3']]
def join_paths(paths, links):
for path in paths:
yield [links[a][b] for a, b in zip(path, path[1:])]
list(join_paths(paths, links))
#>>> [['01', '14', '45'], ['01', '12', '23']]
zip(path, path[1:])
will turn [1, 2, 3, 4]
into [(1, 2), (2, 3), (3, 4)]
.
The for a, b in
will take each pair and set a
and b
to the items in it.
The links[a][b]
will then look up the path name from the dictionary.
yield
returns each item one-at-a-time, so you have to call list
on the output of join_paths
to get it into a list.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.