Floyd warshall 实施似乎缺少一条最短路径
Posted
技术标签:
【中文标题】Floyd warshall 实施似乎缺少一条最短路径【英文标题】:Floyd warshall implementation appears to be missing a shortest path 【发布时间】:2016-03-16 22:02:18 【问题描述】:我正在收集弗洛伊德战争找到的最短路径的计数。对于这个特定的图,1 -> 3 的最短路径是 5,并且有两个具有此权重的路径:1->4->2->3 和 1->4->3。
我不确定显示图表的最佳方式,所以我将使用矩阵,如果您知道更好的选择,请随时提出其他方式。
//i = infinity, no path exists initially
//for u==v, 0
1 2 3 4
1| 0 i 8 2
2| i 0 2 i
3| i 7 0 6
4| i 1 3 0
所以当我运行我的代码时,我得到的最短路径数从 1 -> 3 仅为 1,但正如我之前提到的,肯定有 2 种方式。
下面是算法的实现:
//count[][] is initialized with a 0 if no path between [u][v], and 1 at [u][v] if there is a weight at [u][v].
for (int k = 1; k <= N; k++)
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
if (dist[i][j] > dist[i][k] + dist[k][j])
dist[i][j] = dist[i][k] + dist[k][j];
counts[i][j] = 1;
else if (dist[i][j] == dist[i][k] + dist[k][j] && k != i && k != j)
counts[i][j] ++;
我基本上是从***页面复制/粘贴代码并进行修改以保持计数。
更新:我应该提到,我得到了所有顶点的正确最短长度,并且除了 [1][3] 之外,我得到的所有顶点的计数都是正确的。
完整输出的打印输出:
// Shortest paths // counts
1 2 3 4 1 2 3 4
1 0 3 5 2 1 1 1 1 1
2 i 0 2 8 2 0 1 1 1
3 i 7 0 6 3 0 2 1 1
4 i 1 3 0 4 0 1 2 1
更新:逐行遍历代码,当k = 4,i = 1,j = 3时,我们找到了从1->3的最短路径,权重为5。
更新:阅读 Floyd-Warshall 算法的***条目,我收集到当 k = 4 时,我们正在检查通过顶点 1、2、3、4 的路径。但是,在 k 的每次迭代中,我们只会查看 [1][3] 一次。我想也许这就是问题所在。
【问题讨论】:
【参考方案1】:如果您使用二维 int 数组来存储数据,最好将双循环更改为从 0 运行到 N-1 以避免任何潜在错误。我这样做了,结果是正确的(从 1->3 的最短距离是 5)。这是更新后的代码和打印输出:
//count[][] is initialized with a 0 if no path between [u][v], and 1 at [u][v] if there is a weight at [u][v].
int N = 4;
int max = 1000000;
int[][] dist = new int[N][N];
int[][] counts = new int[N][N];
dist[0][0] = 0; dist[0][1] = max; dist[0][2] = 8; dist[0][3] = 2;
dist[1][0] = max; dist[1][1] = 0; dist[1][2] = 2; dist[1][3] = max;
dist[2][0] = max; dist[2][1] = 7; dist[2][2] = 0; dist[2][3] = 6;
dist[3][0] = max; dist[3][1] = 1; dist[3][2] = 3; dist[3][3] = 0;
//initialize counts
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
if (dist[i][j]<max)
counts[i][j]=1;
for (int k = 0; k < N; k++)
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
if (dist[i][j] > dist[i][k] + dist[k][j])
dist[i][j] = dist[i][k] + dist[k][j];
counts[i][j] = 1;
else if (dist[i][j] == dist[i][k] + dist[k][j] && k != i && k != j)
counts[i][j] ++;
System.out.println("i 1 2 3 4");
for (int i=0; i<N; i++)
System.out.print(i+1 + ": ");
for (int j=0; j<N; j++)
System.out.print(dist[i][j]>=max ? "i ":dist[i][j] + " ");
System.out.println();
System.out.println();
System.out.println("i 1 2 3 4");
for (int i=0; i<N; i++)
System.out.print(i+1 + ": ");
for (int j=0; j<N; j++)
System.out.print(counts[i][j] + " ");
System.out.println();
打印输出: 我 1 2 3 4 1:0 3 5 2 2:我 0 2 8 3:我 7 0 6 4:我 1 3 0
我 1 2 3 4 1:1 1 1 1 2:0 1 1 1 3:0 2 1 1 4:0 1 2 1
【讨论】:
【参考方案2】:您提出的算法存在问题。不是代码。你的更新表明你已经接近它了。
从 4 到 3,您有 2 条最短路径:
4-->3
和 4-->2-->3
。两者的长度都是 3(当 k 增加到 4 时,您的算法将建立的东西)。
所以在最后一次迭代k = 4
时,你的程序会问,如果我去via 4,从 1 到 3 的路径会更短吗?然后它将决定Is 8 > 2+3
- 是的!
您需要将最短路径数设置为 1。但从 4 到 3 有 2 条子路径您不会再考虑。
这是因为 Floyd Warshall 一次只考虑一个顶点 (k),并检查路径是否通过该点而缩短。所以你的程序只计算具有不同倒数第二个顶点的最短路径的数量(在目的地之前的那些)。它没有考虑到即使到达倒数第二点,您也有 2 条最短路径可用。
下面的例子应该清楚了:
您有多少条路径可以到达“V”形图形中的底部(最底点)。您的程序将正确地查看 2 处的基数。但是当被问及形状为“Y”的图形时,您的程序将再次查看基数并错误地得出只有一条路径通向基数的结论。
你很幸运,你选择的测试用例发现了这个问题!
不幸的是,我不认为一个小的编辑会解决它。这是因为 Floyd Warshall 算法保证所有对之间的最短路径。不是所有对之间的所有最短路径。但是,如果您只对该算法找到的路径感兴趣,而不是将计数初始化为 1 并将它们递增 1,而是初始化并递增已为您要经过的顶点找到的最短子路径的数量. (在这种特殊情况下为 2)。
【讨论】:
以上是关于Floyd warshall 实施似乎缺少一条最短路径的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Floyd-Warshall 算法中找到最短路径和最短成本