python 的 NetworkX 中的最低共同祖先
Lowest common ancestor in python's NetworkX
使用 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
我的执行速度有问题,所以我想要更快的执行速度。
如果你有什么好的idea或者算法,请告诉我思路
抱歉英语不好。
Re运行在循环中使用 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
您可以使用 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)。
使用 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
我的执行速度有问题,所以我想要更快的执行速度。
如果你有什么好的idea或者算法,请告诉我思路
抱歉英语不好。
Re运行在循环中使用 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
您可以使用 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)。