我使用相对较小的有向图(~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和键值存储。“
这让我觉得这正是我一直在做的(错误的)。
似乎GPU是文章中提到的内存带宽问题的正确解决方案......除了我不确定如何并行处理这个问题。
问题:
如何并行处理此问题?聚集应用分散正确的方向?以前在哪里做过?
如何在不平行的情况下有效优化当前方法?
作为参考,这是我当前算法的草图:
保留边缘及其权重的字典。例如,如果('A','B')
是从节点A
到节点B
的边缘,
edges_weights[('A','B')] # -> {'x': 1.3, 'y': 32, 'z': 0.231232}
保留每个边缘所涉及的所有简单路径和周期的字典,例如:
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'))
# ]
此外,初始化路径字典及其成本:
paths_costs = {
(('A','B'), ('B','C')): {'x': ..., 'y': ..., 'z': ...}
}
更新边缘时:
我。在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])
显然,有很多嵌套循环和查找正在发生......但我很难看到我能以不同方式做事。
答案 0 :(得分:1)
我不确定我是否理解你的问题。我还有一个尝试:
如果您有n个节点,那么节点之间会有max(n * n-n)/ 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 - 我想知道切换点会在哪里......)