2

我正在使用相对较小的有向图(~10 个节点),每个有~10,000 个简单的路径和循环。我想维护一个总成本的排序列表,以遍历所有这些简单的路径和周期。我的边有几个不同的权重,但聚合函数对于所有这些都是可交换的/关联的(例如总和和乘积)。

现在,我正在使用 rethinkdb(一个 nosql 数据库)和 python。我正在预先计算所有可能的简单路径,将它们存储在哈希图中,并且每次更新边缘权重时,只需蛮力重新计算遍历就会花费它们。我的哈希图将给定的边(其权重刚刚更新)指向它所属的所有简单路径和循环。然后我去重新计算每一个的遍历成本。

好吧,我发现这非常慢并且无法扩展!我知道这是一个难题,但希望它适用于我相对较小的图表。

我最初的方法的一个低效率似乎在于每条路径的浪费冗余计算,即使有些是彼此的聚合。例如,A→B→C→D→E 的成本是 A→B→C 和 C→D→E 的组合。那么为什么不巧妙地计算它们呢?我想出了一种方法来做到这一点,但它似乎没有一点帮助,这让我觉得我真的需要退后一步。

所以我上网搜索了一下,偶然发现了这篇很有帮助的文章: https ://blog.blazegraph.com/?p=628 。它说:

大图反模式是“将所有内容放入大图中,然后使用为我们提供水平缩放以解决其他问题的相同工具:map/reduce 和键值存储。”</p>

令我震惊的是,这正是我一直在做的(错误的)。

似乎 GPU 是文章中提到的内存带宽问题的正确解决方案......除了我不确定如何并行处理这个问题。

问题:

  1. 如何并行处理这个问题?收集-应用-分散是正确的方向吗?以前在哪里做过?

  2. 我如何在不并行的情况下有效地优化当前方法?

作为参考,这是我当前算法的草图:

  1. 枚举我的图表中的所有简单路径和循环
  2. 保留边缘及其权重的字典。例如,如果('A','B') 是从节点A到节点的边B

    edges_weights[('A','B')] # -> {'x': 1.3, 'y': 32, 'z': 0.231232}
    
  3. 保留每条边所涉及的所有简单路径和循环的字典,例如:

    paths_containing_edges[('A','B')] 
    # ->
    # [
    #      (('A','B'), ('B','C')), 
    #      (('A','B'), ('B','C'), ('C','D')), 
    #      (('A','B'), ('B','C'), ('C','A')), 
    #      ... 
    #      (('A','B'), ('B','C'), ('C','D'), ('D','A'))
    # ] 
    
  4. 此外,初始化路径字典及其成本:

    paths_costs = {
            (('A','B'), ('B','C')): {'x': ..., 'y': ..., 'z': ...}
    }
    
  5. 更新边缘时:

    一世。更新其权重edges_weights

    ii. 查找包含这条边的所有简单路径并更新它们:

    fn = lambda p,q: p+q # the cost aggregation function        
    weight_keys = ['x','y','z']
    for path in paths_containing_edges[updated_edge]:
      # path is a tuple of edge tuples, i.e. (('A','B'),('B','C'),('C','D'))
      for w in weight_keys:
        paths_costs[path][w] = reduce(fn,[edges_weights[e][w] for e in path])
    

显然有很多嵌套循环和查找正在发生......但我正在努力了解如何以不同的方式做事。

4

1 回答 1

2

我不确定我是否正确理解了您的问题。我还是会试一试:

如果您有 n 个节点,则节点之间的最大 (n*nn)/2 个边。

如果 n 为 10,则意味着有 50 个可能的边。

在你得到一个循环之前,10 个节点的最大路径长度是 10。一个简单的边缘数组应该确保您可以访问边缘的重量信息。因此,如果例如有一条路径 A->B->C->D->E->G->H->I->J

您将不得不总结 (A->B) (B->C) (D->E) (E->F) (E->G) (HI) (I->J) 现在的问题是:什么更快 - 查找此子项的解决方案或简单地添加数组指针并保留这些指针的查找成本?保持 10.000 个指针并对数字求和应该非常快。特别是如果您将权重保持为 CPU 可以很好处理的数字。我假设它们是 int、long 而不是 float 或 double,即使使用 64 位 cpu,这也应该没有问题。

鉴于您的数字很小,我会尝试使用适当的编程语言(如 C / Assembler)简单地创建最有效的计算,这将降低计算的 CPU 周期。我的直觉是,这将比找到一个有效的算法更快(但仅适用于少数 n - 我想知道切换点在哪里......)

于 2018-01-06T18:35:08.437 回答