在增量DAG中求和所有唯一祖先值的有效方法

时间:2019-03-18 15:13:13

标签: graph directed-acyclic-graphs

假设我们有一个增量构建的DAG。这意味着:

  • 一个节点正在被添加
  • 添加节点后,其所有祖先节点必须已经存在于图中。

添加节点后,我想有效地计算其所有唯一祖先(包括当前节点)的总和。 Sample graph

例如,当添加标记为“ 7”的节点时,其唯一祖先的总和为29。一种方法(效率低下)是遍历所有祖先,并将节点的值添加到总和中(如果有)尚未被访问。

另一种方法是跟踪每个节点的祖先总和。添加新节点后,我可以使用父节点的总和,但是我需要确保某些节点不会被计数两次(节点5和节点10)。

最有效的方法是什么?

1 个答案:

答案 0 :(得分:0)

好吧,也许我的计算器坏了,但是它表明7 + 18 +(19-15)+ 7 = 36而不是29,这使我们认识到您的问题是要计算所有节点祖先的和。有两种计算方法:

占用CPU量大,但节省内存且易于阅读:

在Python库networkx中,可以使用以下这种单行代码进行解决:

sum(DAG.nodes[n]['cost'] for n in nx.ancestors(DAG, your_node) | {your_node})

cost是每个节点的属性,代表其值。

这是完整的工作流程,您可以将其复制到Jupyter笔记本中并进行交互式检查:

import networkx as nx
from random import randint

# Create DAG
G = nx.gnp_random_graph(10,0.3,directed=True)
DAG = nx.DiGraph([(u,v) for (u,v) in G.edges() if u<v])

# Fill 'cost' attributes
for n in DAG.nodes:
    DAG.nodes[n]['cost'] = randint(1,10)

# Set the start node
node = 8

# Print the ancestors sum
print(sum(DAG.nodes[n]['cost'] for n in nx.ancestors(DAG, node) | {node}))

请注意,ancestors相当占用CPU资源(据我了解,对于非加权图中的每个节点, O(n ^ 2)),不应用于大型图中

节省CPU,但占用内存,很难阅读:

如果所有节点都需要祖先信息,则无需为每个节点重新计算所有可能的祖先,则可以重新使用前辈的祖先。因此,您应该手动实现此逻辑:

def set_node_ancestors(DAG, node):
    ancestors = {node}
    for n in DAG.predecessors(node):
        ancestors = ancestors | DAG.nodes[n]['ancestors']
    DAG.nodes[node]['ancestors'] = ancestors

此算法检查给定节点的所有前任,获取其ancestors参数,将它们连接在一起,然后将结果写入给定节点的ancestors参数中。然后,对于每个节点,您可以在ancestors参数中汇总所有节点成本。

您要一个接一个地添加节点,因此可以确保每个节点父节点都填充了ancestors参数。但是,如果要计算给定图中的所有祖先,则应对其进行拓扑排序并按其顺序运行此函数。

第二种算法比第一种算法快得多。这是每个图节点的祖先计算总时间的比较(X轴包含图中的节点数,Y轴-时间以毫秒为单位):

enter image description here