algorithm - 针对对称邻接矩阵优化 Floyd-Warshall

标签 algorithm floyd-warshall

如果保证具有对称邻接矩阵,是否存在降低 Floyd-Warshall 运行时常数因子的优化?

最佳答案

经过一番思考,我想到了:

for (int k = 0; k < N; ++k)
    for (int i = 0; i < N; ++i)
        for (int j = 0; j <= i; ++j)
            dist[j][i] = dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);

当然,现在我们都需要证明它是正确且更快的。

正确性更难证明,因为它依赖于 Floyd-Warshall 的非平凡证明。这里给出了一个很好的证明:Floyd-Warshall proof

输入矩阵是symmetric .现在,其余证明使用修改后的 Floyd-Warshall 证明来表明 2 个内部循环中的计算顺序无关紧要,并且图保持对称每一步之后。如果我们证明这两个条件都成立,那么两种算法都会做同样的事情。

让我们将 dist[i][j][k] 定义为从 ij 的距离,仅使用集合中的顶点{0, ..., k} 作为从 ij 路径上的中间顶点。

dist[i][j][k-1] 定义为从 ij 的边的权重。如果两者之间没有边,则该权重被视为无穷大。

现在使用与上面链接的证明相同的逻辑:

dist[i][j][k] = min(dist[i][j][k-1], dist[i][k][k-1] + dist[k][j][k-1])

现在在计算 dist[i][k][k](对于 dist[k][i][k] 也是如此):

dist[i][k][k] = min(dist[i][k][k-1], dist[i][k][k-1] + dist[k][k][k-1])

现在由于 dist[k][k][k-1] 不能为负(或者我们在图中有一个 negative loop),这意味着 dist[ i][k][k] = 距离[i][k][k-1]。因为如果 dist[k][k][k-1] = 0 则两个参数相同,否则选择 min() 的第一个参数。

所以现在,因为dist[i][k][k] = dist[i][k][k-1],在计算dist[i][j]时[k] dist[i][k]dist[k][j] 是否已经允许 k 并不重要code> 在他们的路径中。由于dist[i][j][k-1]仅用于dist[i][j][k]的计算,dist[ i][j] 将在矩阵中保持 dist[i][j][k-1] 直到 dist[i][j][k] 计算得出。如果 ij 等于 k 则上述情况适用。

因此,计算的顺序无关紧要。

现在我们需要在算法的所有步骤之后证明 dist[i][j] = dist[j][i]

我们从一个对称网格开始,因此 dist[a][b] = dist[b][a],对于所有 ab.

dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
           = min(dist[j][i], dist[k][i] + dist[j][k])
           = min(dist[j][i], dist[j][k] + dist[k][i])
           = dist[j][i]

因此,我们的赋值既为真,也将保持 dist[a][b] = dist[b][a] 不变。因此 dist[i][j] = dist[j][i] 在算法的所有步骤之后

因此,两种算法都会产生相同的正确结果。

速度更容易证明。内部循环的调用次数是正常调用次数的一半多一点,因此该函数的速度大约是正常调用次数的两倍。只是稍微慢了一点,因为您仍然分配了相同的次数,但这并不重要,因为 min() 占用了您大部分时间。

如果您发现我的证明有任何问题,无论技术性如何,请随时指出,我会尝试修复它。

编辑:

您可以通过这样更改循环来加快速度并节省一半的内存:

for (int k = 0; k < N; ++k) {
    for (int i = 0; i < k; ++i)
        for (int j = 0; j <= i; ++j)
            dist[i][j] = min(dist[i][j], dist[i][k] + dist[j][k]);
    for (int i = k; i < N; ++i) {
        for (int j = 0; j < k; ++j)
            dist[i][j] = min(dist[i][j], dist[k][i] + dist[j][k]);
        for (int j = k; j <= i; ++j)
            dist[i][j] = min(dist[i][j], dist[k][i] + dist[k][j]);
    }
}

这只是将优化算法的上述 for 循环分开,所以它仍然是正确的,并且它可能会获得相同的速度,但使用一半的内存。

感谢 Chris Elion 的想法。

关于algorithm - 针对对称邻接矩阵优化 Floyd-Warshall,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2037735/

相关文章:

php - 多边形算法/伪代码中的最短路径

algorithm - 有向图上的 Floyd-Warshall 算法,其中每条边的长度为 -1、0 或 1

algorithm - 如何找到顶点 i 和 j 之间最多有 S 个顶点的最小路径

Java:引用图中的边

Floyd Warshall 算法的 Java 实现

algorithm - Dijkstra 与 Floyd-Warshall : Finding optimal route on all node pairs

ruby - 如何按定义的顺序对数组进行划分和排序

java - 穿过房间的多种方式的编码算法

algorithm - 可执行文件中包含的排序算法是否被信号 11 命令终止?

c# - 人类语言测试算法