如何解决计算是否存在巨型组件 代码分析
我有一组整数,它通过连接集合中的整数来创建一个图,这些整数的二进制表示仅在一个位置上有所不同,例如:
set={0,3,16} --> their binary representation are {00000,00011,10000}
这将是一个图,其中两个节点相连(0 和 16)而 3 未相连。现在我想计算,如果集合创建了一个完全连接的图。换句话说,如果图的巨型组件包含所有节点。目前,我只使用networkx解决了这个问题,首先在networkx中创建一个图,然后使用nx.is_connected(G)
G = nx.Graph()
for key in set:
G.add_node(key)
for n1 in G.nodes(data=True):
for n2 in G.nodes(data=True):
if bin(n1[0]^n2[0]).count("1") == 1: #compare if binary rep. differs at one position
G.add_edge(n1[0],n2[0],weight=1)
if nx.is_connected(G):
这是有问题的,因为它很慢,我宁愿不使用 networkx。你能帮助我吗?谢谢!
解决方法
如果您关心速度,那么 C++ 是您的最佳选择。
要确定一个图是否完全连通,只需确定最大集团的数量正好为 1。
这是寻找最大团的伪代码
LOOP
CONSTRUCT empty current set
SELECT V arbitrary vertex
add V to current set
remove V from graph
LOOP // while set is growing
added_to_set = false
LOOP V over vertices in graph
LOOP Vset over current set
IF Vset connected to V
add V to current set
remove V from graph
added_to_set = true
break;
IF added_to_set == false
break; // the set is maximal
ADD current set to list of sets
IF graph has no remaining vertices
OUTPUT sets found
STOP
有关此的 C++ 实现,请参阅 https://github.com/JamesBremner/PathFinder2/blob/dbd6ff06edabd6a6d35d5eb10ed7972dc2d779a6/src/cPathFinder.cpp#L483
中的代码这段代码可以在不到一秒的时间内处理数千个节点的图形。您使用 networkx 获得了什么性能?
,您的 Graph
实例化有点低效。 Graph
基本上是字典的字典。如果图足够大,则逐一添加边会导致子字典的复制。如果 Graph
对象使用预先计算的所有边进行实例化,则此问题将消失。通过一些小的改动,绝大多数的执行时间都花在了“边缘检测”上,即这里:
bin(n1[0]^n2[0]).count("1") == 1
代码
假设脚本 binary_graph.py
具有以下两个版本的代码:
#!/usr/bin/env python
import networkx as nx
from itertools import combinations
@profile
def v1(nodes):
G = nx.Graph()
for key in nodes:
G.add_node(key)
for n1 in G.nodes(data=True):
for n2 in G.nodes(data=True):
if bin(n1[0]^n2[0]).count("1") == 1: #compare if binary rep. differs at one position
G.add_edge(n1[0],n2[0],weight=1)
return nx.is_connected(G)
@profile
def v2(nodes):
edges = [(n1,n2) for (n1,n2) in combinations(nodes,2) if bin(n1 ^ n2).count("1") == 1]
G = nx.Graph(edges)
return nx.is_connected(G)
if __name__ == '__main__':
nodes = range(1000)
print(v1(nodes))
print(v2(nodes))
分析
通过运行来分析脚本:
kernprof -l binary_graph.py
python -m line_profiler binary_graph.py.lprof
这会产生以下分析信息:
Timer unit: 1e-06 s
Total time: 0.888128 s
File: binary_graph.py
Function: v1 at line 5
Line # Hits Time Per Hit % Time Line Contents
==============================================================
5 @profile
6 def v1(nodes):
7 1 9.0 9.0 0.0 G = nx.Graph()
8 1001 326.0 0.3 0.0 for key in nodes:
9 1000 1457.0 1.5 0.2 G.add_node(key)
10 1001 348.0 0.3 0.0 for n1 in G.nodes(data=True):
11 1001000 312677.0 0.3 35.2 for n2 in G.nodes(data=True):
12 1000000 548470.0 0.5 61.8 if bin(n1[0]^n2[0]).count("1") == 1: #compare if binary rep. differs at one position
13 9864 22631.0 2.3 2.5 G.add_edge(n1[0],weight=1)
14 1 2210.0 2210.0 0.2 return nx.is_connected(G)
Total time: 0.175228 s
File: binary_graph.py
Function: v2 at line 16
Line # Hits Time Per Hit % Time Line Contents
==============================================================
16 @profile
17 def v2(nodes):
18 1 160153.0 160153.0 91.4 edges = [(n1,2) if bin(n1 ^ n2).count("1") == 1]
19 1 12890.0 12890.0 7.4 G = nx.Graph(edges)
20 1 2185.0 2185.0 1.2 return nx.is_connected(G)
换句话说,通过更优化的 networkx Graph
实例化,很明显您的绝大多数执行时间都与 networkx 无关。
这可以在 base python 中使用字典来构建图形,然后进行广度优先搜索以确定图形是否完全连接。这是一个没有 networkx 的实现。
def giantComponentExists(nums):
# Construct a graph as a dictionary
graph = {n:[] for n in nums}
# Add edges between nodes
for n1 in nums:
for n2 in nums:
if bin(n1^n2).count("1") == 1: #compare if binary rep. differs at one position
graph[n1].append(n2)
# BFS search to determine if graph is fully connected
fringe = [nums.pop()]
visited = set()
while fringe:
for edge in graph[fringe[0]]:
if edge not in visited:
fringe += [edge]
visited.add(fringe[0])
fringe.pop(0)
return len(visited) == len(graph.keys())
example1 = {0,1,16}
example2 = {0,3,16}
print(giantComponentExists(example1))
print(giantComponentExists(example2))
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。