簡體   English   中英

dict中元素的Python組合

[英]Python combinations of elements in dict

我有一堆像下面這樣的字典(有些可能很大):

V = {
    0: [823, 832, 1151, 1752, 2548, 3036],
    823: [832, 1151, 1752, 2548, 3036, 3551],
    832: [1151, 1752, 2548, 3036, 3551],
    1151: [1752, 2548, 3036, 3551],
    1752: [2548, 3036, 3551, 4622],
    2548: [3036, 3551, 4622],
    3036: [3551, 4622, 5936, 6440],
    3551: [4622, 5936, 6440],
    4622: [5936, 6440, 9001],
    5936: [6440, 9001],
    6440: [9001],
    9001: []
}

dict 表示幫助導出所有可能路徑(它們是路徑)的基本規則。 路徑是上述整數的序列。

dict 值列表中的每個值也是一個鍵。

我如何確定所有可能的路徑,例如:

[3036, 4622, 9001] 是有效路徑,

但是 [3036, 9001] 不是,原因是 3036 后面必須跟 V[3036] 中的元素之一。 並且每個組合都必須包含一個兼容的序列,並且每個序列都必須以 9001 結尾,也就是說,要到達 9001,必須經過 6440、5936 或 4622。

每個序列也必須從 V[0] 中的一個點開始。

我嘗試了兩件事:

  1. 我首先使用 itertools.product 導出所有路徑,然后過濾掉無效路徑,但是對於大多數 dicts,itertools.product 組合的數量太大了。
  2. 蒙特卡洛模擬,但循環數以百萬計,無法保證捕獲所有路徑。

看起來像一個簡單的 DFS。 由於該圖似乎是有向的(每個節點都有其后繼者的數量大於該節點的數量),因此您甚至無需小心避免循環。

>>> def dfs(graph, start, end):
...     if start == end:
...         return [[end]]
...     return [[start] + result for s in graph[start] for result in dfs(graph, s, end)]
...
>>> dfs(V, 0, 9001)
[[0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 4622, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3551, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3551, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 3551, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3551, 4622, 9001], [0, 823, 832, 1151, 1752, 2548, 3551, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3551, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 3551, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 4622, 9001], [0, 823, 832, 1151, 1752, 3036, 3551, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 3036, 3551, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 3036, 3551, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 3036, 3551, 4622, 9001], [0, 823, 832, 1151, 1752, 3036, 3551, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 3036, 3551, 5936, 9001], [0, 823, 832, 1151, 1752, 3036, 3551, 6440, 9001], [0, 823, 832, 1151, 1752, 3036, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 3036, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 3036, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 3036, 4622, 9001], [0, 823, 832, 1151, 1752, 3036, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 3036, 5936, 9001], [0, 823, 832, 1151, 1752, 3036, 6440, 9001], [0, 823, 832, 1151, 1752, 3551, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 3551, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 3551, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 3551, 4622, 9001], [0, 823, 832, 1151, 1752, 3551, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 3551, 5936, 9001], [0, 823, 832, 1151, 1752, 3551, 6440, 9001], [0, 823, 832, 1151, 1752, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 1752, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 4622, 9001], [0, 823, 832, 1151, 2548, 3036, 3551, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 2548, 3036, 3551, 4622, 5936, 9001], [0, 823, 832, 1151, 2548, 3036, 3551, 4622, 6440, 9001], [0, 823, 832, 1151, 2548, 3036, 3551, 4622, 9001], [0, 823, 832, 1151, 2548, 3036, 3551, 5936, 6440, 9001], [0, 823, 832, 1151, 2548, 3036, 3551, 5936, 9001], [0, 823, 832, 1151, 2548, 3036, 3551, 6440, 9001], [0, 823, 832, 1151, 2548, 3036, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 2548, 3036, 4622, 5936, 9001], [0, 823, 832, 1151, 2548, 3036, 4622, 6440, 9001], [0, 823, 832, 1151, 2548, 3036, 4622, 9001], [0, 823, 832, 1151, 2548, 3036, 5936, 6440, 9001], [0, 823, 832, 1151, 2548, 3036, 5936, 9001], [0, 823, 832, 1151, 2548, 3036, 6440, 9001], [0, 823, 832, 1151, 2548, 3551, 4622, 5936, 6440, 9001], [0, 823, 832, 1151, 2548, 3551, 4622, 5936, 9001], ...]

如果上述函數在您的一個 dicts 上永遠旋轉,那么是時候修改關於被定向圖的假設了。

您可以將字典視為鄰接列表。 您可以使用 vanilla Python(如 Samwise 的答案),但如果圖表有循環,他們的答案將不起作用。

networkx公開了一種查找所需路徑的方法,因此我們可以使用它。 這個函數返回一個生成器,這意味着它不會一次將所有路徑加載到內存中(盡管如果你想使用list()可以這樣做——但如果圖形很大,你可能會耗盡內存):

import networkx as nx

graph = nx.DiGraph(V)
for path in nx.all_simple_paths(graph, 0, 9001):
    print(path)

輸出的第一行和最后三行:

[0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 5936, 6440, 9001]
[0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 5936, 9001]
[0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 6440, 9001]
... [755 more lines]
[0, 3036, 5936, 6440, 9001]
[0, 3036, 5936, 9001]
[0, 3036, 6440, 9001]

非遞歸深度優先搜索生成器函數

  • 解決方案比其他兩個解決方案(即networkx,dfs)更快
  • 更新了 KellyBundy 在評論中的觀察,這使得代碼稍微快了一點。

代碼

def dfs_stack(graph, start, goal):
    '''
        Depth First Search for all paths from start to goal
    '''
    # Init stack to path with just starting vertex
    stack = [[start]]
    
    while stack:
        # Expand path at end of stack
        path = stack.pop()
        
        if path[-1] == goal:
            yield path                # reached goal
        else:
            # Add all paths of vertex to stack
            for start in graph[path[-1]]:
                stack.append(path + [start])

用法

# Use list on generator to obtain all paths
paths = list(dfs_stack(V, 0, 9001))

print(paths [:3])    # First 3 paths
# Output: [0, 3036, 6440, 9001], [0, 3036, 5936, 9001], [0, 3036, 5936, 6440, 9001]]

print(paths [-3:])    # Last 3 paths
# Output: [[0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 6440, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 5936, 9001], [0, 823, 832, 1151, 1752, 2548, 3036, 3551, 4622, 5936, 6440, 9001]]

時序比較

當前方法的速度是其他兩個在 OP 數據上發布的解決方案的兩倍多。

Current Approach
    %timeit list(dfs_stack(V, 0, 9001))
    Result: 874 µs ± 42.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

DFS function from Samwise solution
    %timeit dfs(V, 0, 9001)
    Result: 2.1 ms ± 91.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Networkx solution from BrokenBenchmark solution
    %%timeit 
    graph = nx.DiGraph(V)
    list(nx.all_simple_paths(graph, 0, 9001))
    Result: 4.83 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Timing from OP (see comments): this solution produces 12 Million paths in less than 20s, 
                               networkx takes in excess of 48s

修改以避免循環

盡管當前圖沒有循環,但可以進行簡單的修改來避免它們。

def dfs_stack_no_cycles(graph, start, goal):
    '''
        Depth First Search for all paths from start to goal
    '''
    graph = {k:set(v) for k, v in graph.items()}
    # Init stack to path with just starting vertex
    stack = [[start]]
    
    while stack:
        # Expand path at end of stack
        path = stack.pop()
        
        if path[-1] == goal:
            yield path                # reached goal
        else:
            # Add all paths of vertex to stack
            for start in graph[path[-1]] - set(path):
                stack.append(path + [start])

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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