【问题标题】:Floyd-Warshall with negative cycles. How do I find all undefined paths?Floyd-Warshall 负循环。如何找到所有未定义的路径?
【发布时间】:2013-03-20 12:15:21
【问题描述】:

我已经实现了 Floyd Warshall 算法并且它可以工作,但问题是我不知道如何找到所有未定义的路径。我在网上搜索过,但只能找到如何检测图表是否有负循环的答案。

vector< vector <int> > floyd_warshall(vector< vector<int> > d, int n){
    for(int i = 0; i < n; i++) d[i][i] = 0;

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            for(int k = 0; k < n; k++){
                if(d[j][i] + d[i][k] < d[j][k] and d[j][i] != INF and d[i][k] != INF){
                    d[j][k] = d[j][i] + d[i][k];
                }
            }
        }
    }

    return d;
}

在图上运行算法后:

from: to:   weight:
0     1      1
1     2     -1
2     1     -1
1     3      1
4     0      1

我得到邻接矩阵:

  | 0     1     2     3     4
--|----------------------------
0 | 0    -1    -2    -2     INF
1 | INF  -2    -3    -3     INF
2 | INF  -3    -4    -4     INF
3 | INF   INF   INF   0     INF
4 | 1    -2    -3    -7     0 

我知道如果节点 i 是负循环的一部分,它在矩阵中的位置 d[i][i] 处具有负值。因此,如果我检查矩阵的对角线,我可以找到属于负循环的所有节点。因此,如果我们查看上面的示例,我们可以看到节点 1 和 2 是负循环的一部分。 问题是我想找出哪些路径已定义,哪些未定义。如果您可以通过负循环从 A 到 B,那么路径的长度应该是未定义的,因为它可以任意小。

所以问题是,我怎样才能找到所有未定义的路径?

我希望算法返回矩阵:(而不是上面的那个)

  | 0     1     2     3     4
--|----------------------------
0 | 0    -INF   -INF    -INF  INF
1 | INF  -INF   -INF    -INF  INF
2 | INF  -INF   -INF    -INF  INF
3 | INF   INF    INF     0    INF
4 | 1    -INF   -INF    -INF  0 

其中 d[i][j] = INF 表示 i 和 j 之间没有路径,-INF 表示 i 和 j 之间存在任意小路径(路径在某处通过负循环),否则为d[i][j] i 和 j 之间的最短长度。

我正在考虑测试每条路径,但这可能太慢了。必须有一些标准的方法来解决这个问题,对吧?

谢谢

【问题讨论】:

    标签: java shortest-path floyd-warshall


    【解决方案1】:

    嗯,我找到了解决我自己问题的方法。

    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)    // Go through all possible sources and targets
    
            for(int k = 0; d[i][j] != -INF && k < n; k++)
                if( d[i][k] != INF && // Is there any path from i to k?
                    d[k][j] != INF && // Is there any path from k to j?
                    d[k][k] < 0)      // Is k part of a negative loop?
    
                    d[i][j] = -INF;   // If all the above are true
                                      // then the path from i to k is undefined
    

    我认为它应该有效,而且似乎也有效。

    【讨论】:

      【解决方案2】:

      我有一个video,它准确地解释了 Floyd-Warshall 算法的工作原理。本质上,Floyd-Warshall 算法用于在具有正边或负边权的加权图中找到所有节点对之间的最短路径。

      以下算法接受一个邻接矩阵,其中 Double.POSITIVE_INFINITY 用于表示两个节点不连接。对于每个节点,您可能还希望为其自身初始化权重 0。

      此方法更新初始矩阵以指示所有节点对之间的最小距离。如果最短路径任意小,则答案存储为 Double.NEGATIVE_INFINITY。如果两个节点不能互相到达,那么它仍然是 Double.POSITIVE_INFINITY。此实现运行 Floyd Warshall 两次,如果路径长度小于之前,则我们处于负循环。

      static void floydWarshall(double[][] dist) {
      
        int n = dist.length;
      
        // Compute shortest paths
        for (int k = 0; k < n; k++)
          for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
              if (dist[i][k] + dist[k][j] < dist[i][j])
                dist[i][j] = dist[i][k] + dist[k][j];
      
        // Identify negative cycles
        for (int k = 0; k < n; k++)
          for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
              if (dist[i][k] + dist[k][j] < dist[i][j])
                dist[i][j] = Double.NEGATIVE_INFINITY;
      
      }
      

      【讨论】:

      • 查看我对上述答案的评论,这也可能下溢。
      • 请注意,如果节点的边的权重为负,则上述代码不起作用,因为(在某些情况下)它不会正确传播负无穷大值。在这里查看我的问题:github.com/williamfiset/Algorithms/issues/260
      【解决方案3】:

      只要没有负数,Floyd-Warshall 算法就会输出正确的结果 输入图中存在循环。在存在负循环的情况下,计算最短(简单)路径是一个 NP-hard 问题,并且 Floyd-Warshall 算法不会输出正确的结果。

      但是可以通过检查矩阵的对角线中是否存在负项来检测负循环的存在。这是在该算法的第 8 行和第 9 行中完成的:

      1. M[i, j] := ∞ ∀i 6= j
      2. M[i, i] := 0 ∀i
      3. M[i, j] := c((i, j)) ∀(i, j) ∈ E(G)
      
      4. for i := 1 to n do
      5.   for j := 1 to n do
      6.     for k := 1 to n do
      7.       if M[j, k] > M[j, i] + M[i, k] 
                then M[j, k] := M[j, i] + M[i, k]
      
      8. for i := 1 to n do
      9. if M[i, i] < 0 then return(’graph contains a negative cycle’)
      

      Source

      【讨论】:

      • 我们真的需要等到三重“for”循环结束来检查对角线吗?一旦我们发现对角元素在第 k 次迭代后变为负数,我们就可以退出。这也是 wiki 中所说的:“应该在算法的内部 for 循环中检查路径矩阵对角线上的负数”
      • 这真的很糟糕,如果图表包含负循环,则距离可能下溢。这个条件应该在算法的内部循环中进行测试。 researchgate.net/publication/…
      猜你喜欢
      • 2012-07-07
      • 1970-01-01
      • 2014-05-12
      • 1970-01-01
      • 2011-04-30
      • 2015-04-26
      • 2016-01-18
      • 2014-07-18
      • 2011-05-11
      相关资源
      最近更新 更多