[英]Get preprocessors and successors of a directed acyclic graph in precedence feasible ordering
對於僅具有已知后繼者的給定有向無環圖G ,我試圖為任何可能的給定節點N找到所有直接和間接前驅者和后繼者。 解決方案的排序應該是優先可行的,並且解決方案應該包含N本身。 節省資源的解決方案會很好,因為G的大小可能會急劇增加。
例子:
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: []}
對於 N = 8
[0, 3, 1, 4, 6, 7, 8, 11]
或者
[0, 1, 4, 7, 6, 3, 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)])
請注意,在run_paths(...)
中,如果指定了N
,則 function 將返回僅包含 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]]
使用itertool 文檔中描述的成對 function,我們可以生成所有路徑組合索引並計算它們的路徑。 set()
function 將在計算直接和間接路徑時消除重復。 最后,創建一個字典,其中鍵是數組的長度,值是相關路徑索引的列表。
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)
對於我們的示例,function 將返回以下字典,給定步驟 1 中的 output。
{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 。
合並路徑。 這里使用的技術是識別兩條路徑之間的公共節點在哪里。 然后遍歷每對公共節點,在第一條路徑中找到它們之間的節點,並將 append 放到一個列表中,然后對第二條路徑執行相同的操作。 然后我們可以使用reduce
function 來應用這種技術來合並多條路徑。
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)
最后一部分,function process_graph
,它包含了前面討論的所有功能。
一旦我們計算了字典d中的長度,如果值 N 不是字典鍵的一部分,則 function 終止並返回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
的 output :
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]
這可能不是最有效或最精細的解決方案,還有改進的余地。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.