簡體   English   中英

在優先可行排序中獲取有向無環圖的預處理器和后繼者

[英]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並不總是相同(如果存在多個解決方案),那就太好了。

以下是解決方案的摘要:

  1. 對於給定的起點終點,以及特定值N ,列出通過 N 的所有可能路徑。
  2. 計算所有路徑組合(直接和間接)的長度,並創建一個字典,其中key長度,n 和value路徑的索引
  3. 合並給定 N 的路徑,同時保持優先可行解

細節

步驟1

為了列出圖表的所有路徑,我在這里使用了這個解決方案,並進行了一些修改:

 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]]

第2步

使用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

第 3 步

合並路徑。 這里使用的技術是識別兩條路徑之間的公共節點在哪里。 然后遍歷每對公共節點,在第一條路徑中找到它們之間的節點,並將 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM