Python 以优先可行顺序获得有向无环图的预处理器和后继器
对于一个给定的有向无环图G,只有已知的后继者,我试图找到任何可能的给定节点N的所有直接和间接的前继者和后继者。解的顺序应该是优先可行的,并且解本身应该包含N。一个节省资源的解决方案会很好,因为G的大小可能会急剧增加 例如: 对于N=8Python 以优先可行顺序获得有向无环图的预处理器和后继器,python,search,directed-acyclic-graphs,Python,Search,Directed Acyclic Graphs,对于一个给定的有向无环图G,只有已知的后继者,我试图找到任何可能的给定节点N的所有直接和间接的前继者和后继者。解的顺序应该是优先可行的,并且解本身应该包含N。一个节省资源的解决方案会很好,因为G的大小可能会急剧增加 例如: 对于N=8 [0, 3, 1, 4, 6, 7, 8, 11] 或 可能是可行的解决方案,但不是 [0, 3, 1, 6, 4, 7, 8, 11] 因为6是4的继承者 如上所示,可能存在几种解决方案。一个就足够了,但如果对于给定的N(如果存在多个解决方案),它并不总是
[0, 3, 1, 4, 6, 7, 8, 11]
或
可能是可行的解决方案,但不是
[0, 3, 1, 6, 4, 7, 8, 11]
因为6是4的继承者
如上所示,可能存在几种解决方案。一个就足够了,但如果对于给定的N(如果存在多个解决方案),它并不总是相同的,那就更好了。以下是解决方案的摘要:
细节 第一步 为了列出图形的所有路径,我使用了此解决方案,并进行了一些修改:
G = {
0: [1,2,3],
1: [4,5],
2: [9,10],
3: [8],
4: [6,7],
5: [9,10],
6: [8,9],
7: [8],
8: [11],
9: [11],
10: [11],
11: [],
}
G2 = {k:set(v) for k,v in G.items()}
def dfs_paths(graph, start, goal):
stack = [(start, [start])]
while stack:
(vertex, path) = stack.pop()
for next in graph[vertex] - set(path):
if next == goal:
yield path + [next]
else:
stack.append((next, path + [next]))
def run_paths(graph, start, end, N = None):
all_paths = dfs_paths(graph, start, end)
if N == None :
return([x for x in all_paths])
else:
return([x for x in all_paths if (N in x)])
请注意,在运行路径(…)
中,如果指定了N
,函数将返回仅包含N的路径
print(run_paths(G2, 0,11))
[[0, 3, 8, 11], [0, 2, 10, 11], [0, 2, 9, 11], [0, 1, 5, 10, 11], [0, 1, 5, 9, 11], [0, 1, 4, 7, 8, 11],
print(run_paths(G2, 0,11, 8))
[[0, 3, 8, 11], [0, 1, 4, 7, 8, 11], [0, 1, 4, 6, 8, 11]]
步骤2
使用中描述的成对函数,我们可以生成所有路径组合索引并计算它们的路径。当计算直接和间接路径时,set()
函数将消除重复。最后,创建一个字典,其中键是数组的长度,值是相关路径索引的列表
from itertools import *
def get_size_dict( list_of_sols ):
def powerset(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
comb_of_indexs = powerset(range(len(list_of_sols))) # return a comb of indicies
d = {}
for indexs in comb_of_indexs:
myset = set([x for index in indexs for x in list_of_sols[index]])
set_length = len(myset)
if set_length not in d.keys():
d[set_length] = []
d[set_length].append(indexs)
return(d)
对于我们的示例,给定步骤1的输出,函数将返回以下字典
{0: [()], 4: [(0,)], 6: [(1,), (2,)], 7: [(0, 1), (0, 2), (1, 2)], 8: [(0, 1, 2)]}
如果我们需要N=8,我们需要使用路径索引0,即[0,3,8,11]
,索引1和索引2
步骤3
合并路径。这里使用的技术是确定两条路径之间的公共节点的位置。然后在每对公共节点上循环,在第一条路径中找到它们之间的节点,并将其附加到列表中,然后对第二条路径执行相同的操作。然后,我们可以使用reduce
函数将此技术应用于合并多个路径
from functools import *
def merge_paths(a, b):
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def find_elements(x, text1, text2):
return(x[x.index(text1)+1:x.index(text2)])
my_list = []
interesect_points = [x for x in a if x in b]
for (x,y) in pairwise(interesect_points):
'''
get elements from a that within x,y --> e1
get elements from b that within x,y --> e2
if my_list is empty:
add x,e1, e2, y to my_list
else add e1, e2, y
'''
e1 = find_elements(a, x, y)
e2 = find_elements(b, x, y)
if len(my_list ) == 0:
my_list.extend([x] + e1 + e2 + [y])
else:
my_list.extend(e1 + e2 + [y])
return(my_list)
完成解决方案
最后一部分是函数process\u图
,它封装了前面讨论的所有功能
在计算字典d中的长度后,如果值N不是字典键的一部分,则函数终止并返回None
def set_for_size_N(paths, indexes ):
for index in indexes:
yield [paths[i] for i in index]
def process_graph(G, start, end, N):
paths = run_paths(G2, start, end, N)
d = get_size_dict(paths)
if N not in d.keys():
return(None)
list_of_paths_of_size_N = set_for_size_N(paths,d[N])
for paths_of_size_N in list_of_paths_of_size_N:
yield(reduce(merge_paths, paths_of_size_N))
N=8的输出:
for i in process_graph(G2, 0, 11, 8):
print(i)
[0, 3, 1, 4, 7, 6, 8, 11]
下面是N=6
的另一个例子:
for i in process_graph(G2, 0, 11, 6):
print(i)
[0, 1, 4, 6, 9, 11]
[0, 1, 4, 6, 8, 11]
这可能不是最有效或最微妙的解决方案,还有改进的余地。@qualitaetsmuell,如果它能解决您的问题,请接受答案。
for i in process_graph(G2, 0, 11, 8):
print(i)
[0, 3, 1, 4, 7, 6, 8, 11]
for i in process_graph(G2, 0, 11, 6):
print(i)
[0, 1, 4, 6, 9, 11]
[0, 1, 4, 6, 8, 11]