微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

计算是否存在巨型组件 代码分析

如何解决计算是否存在巨型组件 代码分析

我有一组整数,它通过连接集合中的整数来创建一个图,这些整数的二进制表示仅在一个位置上有所不同,例如:

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 举报,一经查实,本站将立刻删除。