简体   繁体   English

有向无环图的高效遍历

[英]Efficient traversal of a directed acyclic graph

Input:输入:

nd[0];
nd[1, 2];
nd[3, 4];
nd[0, 1];
nd[2, 3];
nd[0, 4];
nd[1, 3];
nd[0, 2];
nd[1, 4];
nd[3];
nd[1, 4];

Resulting Tree:结果树:

上面代码的结果树

Output:输出:

total_time = sum of all individual wait_time //without overlap

Rules:规则:

  • Input code will always result a directed acyclic graph输入代码总是会产生一个有向无环图
  • Each node has some wait_time value每个节点都有一些wait_time
  • A complete graph traversal should calculate the total wait_time of whole graph一次完整的图遍历应该计算整个图的总wait_time
  • All independent nodes must be traversed in parallel (or at least time calculation should be in this way)所有独立节点必须并行遍历(或者至少时间计算应该是这样)
  • If overlapping of wait_time of two different nodes occur then maximum value will be considered and traversal of a node with lesser time will move to the next independent node如果两个不同节点的wait_time发生重叠,则将考虑最大值,并且遍历时间较短的节点将移动到下一个独立节点
  • All single and paired nodes will have exactly 1 and 2 in-coming and out-going edges except roots and leaves (there can be multiple roots and leaves)除了根和叶(可以有多个根和叶)之外,所有单个和成对的节点都将恰好有 1 条和 2 条传入和传出边

Problem 1: How to convert given input-code into the graph so it can be traversed?问题 1:如何将给定的输入代码转换为图形以便遍历? (pseudo-code or any kind of guidance might help) (伪代码或任何类型的指导可能会有所帮助)

Problem 2: How to achieve the successful traversal based on mentioned rules?问题二:如何根据上述规则实现成功遍历?

Previous Effort:以前的努力:

  • I was able to sort the nodes in a linear way using Topological sort , but I am not sure how can I achieve these specific goals.我能够使用Topological sort以线性方式对节点进行排序,但我不确定如何实现这些特定目标。

  • I drew this graph based on my intuition from code.我根据代码的直觉绘制了这张图。

  • Digits on the edges are just to clarify what number is causing the dependency.边缘上的数字只是为了澄清导致依赖的数字。

  • I am using python 3我正在使用 python 3

  • My previous code does not seem to make any sense in relevance to these problems so I did not include that.我以前的代码似乎与这些问题没有任何意义,所以我没有包含它。

  1. Creating the graph创建图表

Let a, b, c three nodes which have a 0 property/depandancy in common (to node 0).a, b, c三个节点,它们共同具有0属性/依赖关系(到节点 0)。 If a is discovered before b , and b discovered before c , edge should be a->b and b->c .如果ab之前被发现, bc之前被发现,则边应该是a->bb->c

To address above rule, consider a cache for every discovered digit.为了解决上述规则,请考虑为每个发现的数字缓存。 If a node includes 0 , then you cache it as cache[0] .如果节点包含0 ,则将其缓存为cache[0] Later on if an other node b includes 0 , you must make an edge from cache[0] to b , and update that cache so that subsequent node including 0 get an edge from b .稍后,如果其他节点b包含0 ,则必须从cache[0]b一条边,并更新该缓存,以便包含0后续节点从b获得一条边。

foreach input as node
  forall dependancy of node
    if discovered[dependancy]
      make_edge(discovered[dependancy], node)
    fi
    discovered[dependancy] = node // apply dependancy "transitivity"

Now to build the graph as "layers" you can start from the leaves (layer 0 ), here [1,4],[0,2],[0,3] only.现在要将图形构建为“层”,您可以从叶子(层0 )开始,这里仅[1,4],[0,2],[0,3] Then take the nodes whose output edge link to any of the leaves, here only [1,4] .然后取其输出边链接到任何叶子的节点,这里只有[1,4] (because [1,3] while linking to [3] which is a leaf, also links to [1,4] which is not. So excluded). (因为[1,3]在链接到[3]是叶子的同时,也链接到[1,4]不是。所以排除了)。 You then have built the layer 1 .然后,您已经构建了第1层。

While building layer n, you can only add nodes having deps to layer 0 to n-1在构建第 n 层时,您只能将具有 deps 的节点添加到第 0 层到 n-1 层

A more compact way of saying this is for a node, to compute its eccentricity (the longest path (here to the leaves)).一种更紧凑的说法是对于一个节点,计算它的离心率(最长的路径(这里是叶子))。

To print the graph as you did, notice you can just plot the nodes by eccentricity which would show the "level" of dependancy (how far they are from the leaves/end, see plotByLayers in code below).要像您一样打印图形,请注意您可以通过偏心率绘制节点,这将显示依赖的“级别”(它们离叶子/末端有多远,请参阅下面代码中的plotByLayers )。

  1. Computing the total time计算总时间

We don't need to go to the hassle of building layers as above.我们不需要像上面那样去构建层的麻烦。

A node can start only when all of its ancestors end.一个节点只有在它的所有祖先都结束时才能开始。

The ancestors have already been determined when we made the graph.当我们制作图表时,祖先已经确定了。

The recursion is thus递归是这样的

n.endAt = max(n.endAt for n in n.ancestors) + n.wait

Initialization being for the root nodes (which have no dependancy) and where we can just keep the wait time.初始化是针对根节点(没有依赖关系)以及我们可以保持等待时间的地方。

nroot.endAt = nroot.wait

Regarding the node.id notation i)x,y .关于 node.id 符号i)x,y i is the ith node as read in input. i 是在输入中读取的第 i 个节点。 (its true id somehow. x,y is just garbage to help us visualize). (不知何故x,y它的真实id。x x,y只是帮助我们形象化的垃圾)。

 const input = ` nd[0]; nd[1, 2]; nd[3, 4]; nd[0, 1]; nd[2, 3]; nd[0, 4]; nd[1, 3]; nd[0, 2]; nd[1, 4]; nd[3]; nd[1, 4];` function makeGraph(input) { const nodes = input.trim().split('\\n').map((x,i) => { const deps = x.match(/\\[(.+)\\]/)[1] return { id: i+')'+deps, v: deps.split(',').map(x => x.trim()) } }) const edges = {} const discovered = {} nodes.forEach((node, i) => { node.v.forEach(digit => { if (discovered[digit]) { // there is an edge edges[discovered[digit].id] = edges[discovered[digit].id] || [] edges[discovered[digit].id].push(node) } // apply transitivity a->b->c discovered[digit] = node }) }) return {nodes, edges} } function computeEccentricity(n, edges) { if (typeof(n.eccentricity) !== 'undefined') { return n.eccentricity } if (!edges[n.id]) {// a leaf has no outgoing edges n.eccentricity = 0 return 0 } const distances = Object.values(edges[n.id]).map(n => computeEccentricity(n, edges)) const min = Math.max(...distances) n.eccentricity = 1 + min return n.eccentricity } function plotByLayers(nodes) { const lvls = [] let m = 0; nodes.forEach(n => { const i = n.eccentricity lvls[i] = lvls[i] || [] lvls[i].push(n) m = i > m ? i: m }) for(let i = m; i >=0 ; --i) { console.log(lvls[i].map(x => x.id)) } return lvls } const { nodes, edges } = makeGraph(input) nodes.forEach(n => computeEccentricity(n, edges)) plotByLayers(nodes) // for any node, compute its ancestors. nodes.forEach((n, i) => { if (edges[n.id]) { edges[n.id].forEach(v => { v.ancestors = v.ancestors || [] v.ancestors.push(n) }) } n.wait = 2**i // say arbitrarily that i node waits 2^i some time }) function computeEndAt(node) { if (typeof(node.endAt) !== 'undefined') { return node.endAt } if (!node.ancestors) { node.endAt = 0 + node.wait return node.endAt } const maxEnd = Math.max(...node.ancestors.map(computeEndAt)) node.endAt = maxEnd + node.wait return node.endAt } nodes.forEach(computeEndAt) const longest = nodes.sort((a,b)=>b.endAt - a.endAt)[0] console.log('awaited: ', longest.endAt, longest.id)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM