【问题标题】:Lowest common ancestor in python's NetworkXpython NetworkX中最低的共同祖先
【发布时间】:2016-10-08 16:30:27
【问题描述】:

使用 NetworkX

我想从有向图中的 node1 和 node11 中得到最低的共同祖先。

以下是代码。

import networkx as nx

G = nx.DiGraph() #Directed graph
G.add_nodes_from([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
G.add_edges_from([(2,1),(3,1),(4,1),(5,2),(6,2),(7,3),(8,3),(9,3),(10,4),(10,12),(14,9),(15,8),(12,11),(13,11),(14,12),(15,13)])

ancestors1 = nx.ancestors(G, 1)
ancestors2 = nx.ancestors(G, 11)

src_set = set(ancestors1)
tag_set = set(ancestors2)
matched_list = list(src_set & tag_set)

dic = {}

for elem in matched_list:
    print elem
    length1 = nx.dijkstra_path_length(G, elem, 1)
    path1 = nx.dijkstra_path(G, elem, 1)
    dist1 = len(path1)
    length2 = nx.dijkstra_path_length(G, elem, 11)
    path2 = nx.dijkstra_path(G, elem, 11)
    dist2 = len(path2)
    dist_sum = dist1 + dist2
    dic[elem] = dist_sum

min_num = min(dic.values()) 
for k, v in sorted(dic.items(), key=lambda x:x[1]):
    if v != min_num:
        break
    else:
        print k, v

我的执行速度有问题,所以我想要更快的执行速度。

如果你有什么好的想法或算法,请告诉我。

对不起英语不好。

【问题讨论】:

    标签: python algorithm networkx lowest-common-ancestor


    【解决方案1】:

    在循环中重新运行 Dijkstra 确实看起来有点矫枉过正。

    假设我们构建了通过反转边缘获得的有向图:

    import networkx as nx
    
    G = nx.DiGraph() #Directed graph
    G.add_nodes_from([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
    edges = [(2,1),(3,1),(4,1),(5,2),(6,2),(7,3),(8,3),(9,3),(10,4),(10,12),(14,9),(15,8),(12,11),(13,11),(14,12),(15,13)]
    G.add_edges_from([(e[1], e[0]) for e in edges])
    

    现在我们从两个节点中的每一个运行 BFS:

    preds_1 = nx.bfs_predecessors(G, 1)
    preds_2 = nx.bfs_predecessors(G, 11)
    

    在反向图中找到两个节点都可达的公共顶点很容易:

    common_preds = set([n for n in preds_1]).intersection(set([n for n in preds_2]))
    

    现在您可以轻松查询上述内容。例如,要找到两者都可达的公共顶点,最接近 1,是:

    >>> min(common_preds, key=lambda n: preds_1[n])
    10
    

    【讨论】:

      【解决方案2】:

      您可以使用 networkx.algorithms.lowest_common_ancestor 中定义的函数来执行此操作。它有一个仅适用于树的更快版本,以及一个适用于任何有向无环图的较慢版本。由于您的示例是 DAG,我将使用后者。

      # Same as in your example
      import networkx as nx
      G = nx.DiGraph() #Directed graph
      G.add_nodes_from([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
      G.add_edges_from([(2,1),(3,1),(4,1),(5,2),(6,2),(7,3),(8,3),(9,3),(10,4),(10,12),(14,9),(15,8),(12,11),(13,11),(14,12),(15,13)])
      
      # Compute the lowest common ancestor of 1 and 11. This will print 15.
      print nx.algorithms.lowest_common_ancestor(G, 1, 11)
      

      请注意,这会计算由与图源的最大距离定义的最低共同祖先;根据这个定义,10 和 15 都是 (1, 11) 的最低共同祖先,而这恰好给出了 15。您的原始代码(和前面的答案)还最小化了从 1 和 11 到 LCA 的路径,这给出了 10 (总和距离为 6)而不是 15(总距离为 7)。如果您也需要该属性,则此模块可能不适合。

      使用这个模块,为单个节点对找到 LCA 的运行时间在图的大小上是 O(n log(n))。为所有节点对找到 LCA 的运行时间是 O(n^3)。

      【讨论】:

      • 我已经澄清了我的答案以提供更多详细信息并举例说明,但请注意,原始问题专门询问如何使用我链接的库进行操作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-24
      • 1970-01-01
      • 2013-10-01
      • 1970-01-01
      • 2011-05-03
      • 2012-11-08
      相关资源
      最近更新 更多