假设我们有一个增量构建的DAG。这意味着:
添加节点后,我想有效地计算其所有唯一祖先(包括当前节点)的总和。
例如,当添加标记为“ 7”的节点时,其唯一祖先的总和为29。一种方法(效率低下)是遍历所有祖先,并将节点的值添加到总和中(如果有)尚未被访问。
另一种方法是跟踪每个节点的祖先总和。添加新节点后,我可以使用父节点的总和,但是我需要确保某些节点不会被计数两次(节点5和节点10)。
最有效的方法是什么?
答案 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轴-时间以毫秒为单位):