计算依赖图偏序的算法

Posted

技术标签:

【中文标题】计算依赖图偏序的算法【英文标题】:Algorithm for computing partial orderings of dependency graphs 【发布时间】:2011-06-27 15:49:30 【问题描述】:

我正在尝试计算依赖图的部分“拓扑排序”,准确地说,它实际上是一个 DAG(有向无环图);以便在没有冲突依赖的情况下并行执行任务。

我想出了这个简单的算法,因为我在 Google 上找到的并不是那么有用(我一直在寻找那些本身并行运行以计算正常拓扑排序的算法)。

visit(node)

    maxdist = 0;
    foreach (e : in_edge(node))
    
        maxdist = max(maxdist, 1 + visit(source(e)))
    
    distance[node] = maxdist;
    return distance[node];


make_partial_ordering(node)

    set all node distances to +infinite;
    num_levels = visit(node, 0);
    return sort(nodes, by increasing distance) where distances < +infinite;

(请注意,这只是伪代码,肯定会有一些可能的小优化)

至于正确性,这似乎很明显:对于叶子(:= 没有进一步依赖关系的节点),到叶子的最大距离始终为 0(正确,因为由于 0 个边缘而跳过了循环)。 对于连接到节点 n1,..,nk 的任何节点,到叶的最大距离为 1 + maxdistance[n1],..,distance[nk]。

我写下算法后确实找到了这篇文章:http://msdn.microsoft.com/en-us/magazine/dd569760.aspx 但老实说,他们为什么要做所有的列表复制等等,看起来效率真的很低..?

无论如何,我想知道我的算法是否正确,如果正确,那么它叫什么,以便我可以阅读一些关于它的内容。

更新:我在我的程序中实现了该算法,它似乎对我测试的所有内容都很有效。 在代码方面它看起来像这样:

  typedef boost::graph_traits<my_graph> my_graph_traits;
  typedef my_graph_traits::vertices_size_type vertices_size_type;
  typedef my_graph_traits::vertex_descriptor vertex;
  typedef my_graph_traits::edge_descriptor edge;

  vertices_size_type find_partial_ordering(vertex v,
      std::map<vertices_size_type, std::set<vertex> >& distance_map)
  
      vertices_size_type d = 0;

      BOOST_FOREACH(edge e, in_edges(v))
      
          d = std::max(d, 1 + find_partial_ordering(source(e), distance_map));
      

      distance_map[d].insert(v);

      return d;
  

【问题讨论】:

您可能希望通过记忆 find_partial_ordering 的返回值将最坏情况从二次减至线性... 您是否需要预先为图表找到一组图层,或者您可以在任务执行时逐步完成?如果您有不同的任务执行时间,创建一个算法来挑选一个满足依赖关系的元素,然后在线程空闲时运行它是很简单的。 【参考方案1】:

您的算法 (C++) 有效,但它的最坏情况时间复杂度非常差。它在一个节点上计算find_partial_ordering 的路径,该路径与该节点的路径一样多。在树的情况下,路径的数量是 1,但在一般的有向无环图中,路径的数量可以是指数的。

您可以通过memoizing 您的find_partial_ordering 结果来解决此问题,并在您已经计算出特定节点的值时不递归地返回。但是,这仍然会给您留下一个堆栈破坏递归解决方案。

An efficient (linear) algorithm for topological sorting is given on Wikipedia。这不符合您的需求吗?

编辑:啊,我明白了,您想知道深度边界在哪里,以便您可以在给定深度并行化所有内容。您仍然可以为此使用 Wikipedia 上的算法(从而避免递归)。

首先,使用***上的算法进行拓扑排序。 现在通过按拓扑顺序访问节点来计算深度:

depths : array 1..n
for i in 1..n
    depths[i] = 0
    for j in children of i
        depths[i] = max(depths[i], depths[j] + 1)
return depths

请注意,上面没有递归,只是一个普通的O(|V| + |E|) 算法。这与 Wikipedia 上的算法具有相同的复杂性。

【讨论】:

以上是关于计算依赖图偏序的算法的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法:图

拓扑排序

简单依赖算法的问题

用于回溯变量的使用和依赖性的算法

Spark中文手册9:spark GraphX编程指南

R语言使用DALEX包的model_profile函数对h2o包生成的多个算法模型的连续变量进行分析使用偏依赖图(Partial Dependence Plots)解释连续特征和目标值y的关系