【问题标题】:How to Estimate Roughly The Computation Time While Using Module NetworkX如何在使用 Module NetworkX 时粗略估计计算时间
【发布时间】:2018-08-26 05:18:18
【问题描述】:

您好,我正在处理一个有点 (对不起,含糊不清的术语)图,它有 29,981 个节点150,000 个有向边 在里面。

我正在使用模块 networkx 处理它,该模块现在在图论者中被广泛使用。

我今天早上在 Jupyter 中执行了以下脚本,但无法估计何时完成:

import netowkx as nx
import pickle

# to read the graph
with open ('/home/zachary/H{}'.format("29981"), 'rb') as fp:
    H = pickle.load(fp)     

print(list(nx.simple_cycles(H)))

我怎样才能大致猜出这个脚本的完成时间?

我有点知道big Osmall O 的.. 但通常这种理论知识在我的脑海中还没有成熟,无法在工业上使用这些知识来计算和估计计算时间。

【问题讨论】:

    标签: python time-complexity networkx scalability


    【解决方案1】:

    NetworkX documentation 中所述,simple_cycles 使用约翰逊算法来查找基本循环。算法的复杂度是O((V+E).(1+C)) 其中

    • V 是顶点数;
    • E 是边数;
    • C 周期数。

    在您的情况下V+E ~= 150,000,因此假设python进程没有过载,我们可以预期运行时间为150,000.K.C

    要尝试找到 K 的估计值,您可以在较小的图上运行算法,使用 10 的幂 (V+E = 10, 100, 1000 ...) 以确保 simple_cycles 的运行时间与 (V+E)(1+C) 保持成比例,得到K 的粗略值,并根据您期望找到的周期数估计图表的运行时间。更准确地说,如果我们记下 R(V+E,C) 每个实验小图的实际运行时间,以及 C0, C1, ...Cn 它们各自的周期数,那么我们预计会有

    R(100,C1)  / R(10,C0)  ~= 10.K.[(1+C1) / (1+C0)]
    R(1000,C1) / R(100,C0) ~= 10.K.[(1+C2) / (1+C1)]
    ...
    

    如果simple_cycles 的运行时间没有表现出 Johnson 算法的复杂性,那么就有一个非算法因素正在减慢/阻止计算 - 这需要进行调查。

    跟进 这些是使用您提供的图表进行的一些调查的结果。我尝试使用 NetworkX 库计算较小子图的周期数,并在下面复制了一些有趣的结果。每个子图都有节点数和边数以及计算的循环数。

    \#Nodes  | \#Edges | \#Cycles (computed)
    ----------------------------------------
       1,000 |     186 |                17
       2,000 |     675 |                37
       3,000 |   1,460 |                72
       4,000 |   2,538 |             2,147   
       4,250 |   2,881 |         2,351,883
    

    我在#Nodes = 4000 停了下来,几分钟内我没有得到任何结果。

    让我们计算每个值的值

    log10(C)/E with C = \#Cycles and E = \#Edges.
    
    E = \#Edges | C = \#Cycles (computed) |  log(C)/E  | 
    ----------------------------------------------------
            186 |                      17 |     0.0067 |
            675 |                      37 |     0.0023 |
          1,460 |                      72 |     0.0013 |
          2,538 |                   2,147 |     0,0013 |
          2,881 |               2,351,883 |     0,0022 |
    

    正如我们所见,至少对于具有少于~2,500 边的G 的子图,周期数大致遵循以下幂律

    log10(C) = 0.0013.E => C = 1.003^E
    

    经验值 1.003 来自图的拓扑结构(作为旁注,maximum theoretical number of cycles given the number of edges 估计为1.443^E

    请注意,我们不知道这个常数是否随着图形变大而保持不变 - 这将是一件有趣的事情,但使用与这种蛮力方法不同的方法(我们已经有一千个当我们达到 5000 条边时,十亿个周期)。

    在这种情况下(并且仅在这种情况下)常数不会随着图形变大直到 G 的 150,000 条边而改变,循环的近似数将是...~10^359

    => 看来您实际上遇到了算法复杂性墙。考虑到这一点,我不知道您希望选择哪种替代方案 - 也许存在非指数近似算法?

    注意
    为了试验G 的子图,我使用了以下命令 - 指定目标节点数,例如 3,000 个节点:

     H = G.copy()
     H.remove_nodes_from(list(nodes)[3000:])
     len(list(nx.simple_cycles(H)))
    

    【讨论】:

    • 你的意思是. 标量乘法?
    • 是的——150,000*K*C
    • 在调查后添加了一些结果 - 周期数呈指数增长,运行时间也是如此,因为它与周期数成正比。所以你实际上遇到了算法复杂性墙!
    • 感谢您的解释 - 我将检查结果是否因用于选择节点的方法而有偏差(我假设是任意排序的)。如果我理解正确,您只会对“短”周期感兴趣吗?现在这样的周期的长度是多少?如果这个数字“足够小”,那么朴素的蛮力算法可能会提供一些结果——如果边的数量在图中均匀分布,那么朴素算法的复杂度将为V.(V/E)^kk寻找的周期长度。所以“exist”->“be”->“exist”这样的循环是可行的。
    • 理解第 2 点) - 所以基本上你需要对算法找到的循环进行更全面的描述 - 你可以列出所有这些循环并在之后对它们的来源进行分类(后处理),或者分类找到循环之前的节点并在您使用之前讨论的 ad-hoc 算法查找循环时捕获这些类别。
    猜你喜欢
    • 2013-02-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 2018-10-20
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 2017-10-12
    相关资源
    最近更新 更多