用于计算依赖图的部分排序的算法

我试图计算依赖图的部分“拓扑排序”,这实际上是一个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 max {distance [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;
  }
你的算法(C)是有效的,但它具有非常糟糕的最坏情况时间复杂度.它计算节点上的find_partial_ordering与该节点一样多的路径.在树的情况下,路径的数量为1,但是在一般的有向无环图中,路径的数量可以是指数的.

您可以通过memoizing的find_partial_ordering结果修复此问题,并在您已经计算了特定节点的值时返回而不进行递归.然而,这仍然给你一个堆栈破坏的递归解决方案.

这不符合你的需要吗?

编辑:啊,我明白,你想知道深度边界在哪里,这样你可以在给定的深度平行一切.您仍然可以使用维基百科的算法(因此避免递归).

首先,使用维基百科上的算法进行拓扑排序.现在通过以拓扑顺序访问节点来计算深度:

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 |)算法.这与维基百科的算法具有相同的复杂性.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结