基于Bellman ford队列的方法来自Sedgewick和Wayne - Algorithms,第4版

时间:2015-04-23 07:56:33

标签: algorithm graph-algorithm shortest-path bellman-ford

我正在研究queue-based针对来自Bellman-Ford algorithm的单一来源最短路径的Robert Sedgewick and Kevin Wayne - Algorithms, 4th edition方法 来自book的算法的源代码出现在此链接http://algs4.cs.princeton.edu/44sp/BellmanFordSP.java

我有两点是怀疑,另一点是代码改进建议。

  1. 在上面的方法中,下面是用于放松距离顶点的松弛方法的代码。

    for (DirectedEdge e : G.adj(v)) {
        int w = e.to();
        if (distTo[w] > distTo[v] + e.weight()) {
            distTo[w] = distTo[v] + e.weight();
            edgeTo[w] = e;
            if (!onQueue[w]) {
                queue.enqueue(w);
                onQueue[w] = true;
            }
        }
        if (cost++ % G.V() == 0){
            findNegativeCycle();
        }
    }
    
  2. 在此方法中,在查找负循环之前使用以下条件。

    if (cost++ % G.V() == 0){
         findNegativeCycle();
    }
    

    上面他们将费用除以图表中vertices的数量,以检查negative cycle。可能是因为 放松时间为V-1次,如果放松时间为Vth,则表示它有周期,其中V是顶点数。

    但我认为在基于队列的方法中,他们使用除数来定期检查周期是否已经发生。可以发生循环 在上述条件之前或之后是真的。如果在上述条件为真后发生循环,则算法必须等到下一个条件为止 如果(cost++ % G.V() == 0)为真,则检测周期为true,没有必要完全发生周期。所以我认为除数可以是足够小的任何数,以便我们可以在适当的时间间隔后检查周期。 我是否正确地想到了这一点?

    1. 代码改进建议是findNegativeCycle()方法可用于在循环发生时返回true。从而。这种情况 -

      if (cost++ % G.V() == 0) { findNegativeCycle(); }

    2. 可以更改为 -

      if (cost++ % G.V() == 0)
          if(findNegativeCycle()){
              return;
          }
      

      即使findNegativeCycle()方法找到了一个循环,代码for循环也会继续运行。所以我们可以在循环发生时返回,而不是处理for循环的其余部分。

      请分享您对以上2点的想法。 提前谢谢。

2 个答案:

答案 0 :(得分:3)

  1. 你是对的。在他们的online materials中,作者解释说他们检查每个Vth调用,以便分摊构建相关边缘加权有向图并在其中找到周期的成本:
  2.   

    因此,要实现negativeCycle()BellmanFordSP.java构建一个   edgeTo []中边缘的边加权有向图,并寻找一个循环   在那个有向图。为了找到循环,它使用   EdgeWeightedDirectedCycle.java,一个版本的DirectedCycle.java来自   第4.3节,适用于边缘加权有向图。 我们摊销   只有在每个Vth之后执行此检查才能获得此检查的成本   打电话给放松()

    换句话说,你可以更频繁地检查,但是你会冒失去性能的风险。

    1. 再次,你是对的。目前在构造函数的while循环中检查负循环的存在。但是,在最坏的情况下,relax方法可以通过检查for循环中的第一个边缘来检测周期,而不是退出它将继续并检查其他边缘(最大V-2 )。使用您建议的改进可以节省大量时间。

答案 1 :(得分:0)

非常高兴看到Miljen Mikic的答案。理解算法确实很有帮助。但是,我还有另一个问题。在文中,它说“要完成实现,我们需要确保算法终止 V过后。实现这一目标的一种方法是明确地跟踪传球。“在这里,我相信变量”成本“是传球的数量,但不应该是线

if (cost++ % G.V() == 0)
    findNegativeCycle();

至少在for循环之外?像

private void relax(EdgeWeightedDigraph G, int v)
    {
        for (DirectedEdge e : G.adj(v)
             {
                  int w = e.to();
                  if (distTo[w] > distTo[v] + e.weight())
                       {
                           distTo[w] = distTo[v] + e.weight();
                           edgeTo[w] = e;
                           if (!onQ[w])
                                 {
                                      q.enqueue(w);
                                      onQ[w] = true;
                                 }
                        }
              }
         if (cost++ % G.V() == 0)
              findNegativeCycle();
      }

实际上,即使它在for循环之外,它也不是最好的解决方案,因为在每次传递过程中,可能会有多个顶点要放松。因此,通过记住每次传递中要放松的顶点数,可以在BellmanFordSP的构造函数中更好地设计它。我对么?谢谢!