针对对称邻接矩阵优化 Floyd-Warshall
Posted
技术标签:
【中文标题】针对对称邻接矩阵优化 Floyd-Warshall【英文标题】:Optimise Floyd-Warshall for symmetric adjacency matrix 【发布时间】:2011-01-03 12:14:16 【问题描述】:如果保证有对称邻接矩阵,是否有降低 Floyd-Warshall 运行时常数因子的优化?
【问题讨论】:
不是总是对称的吗? O_o 你有时可能有有向边,但它不是对称的。 【参考方案1】:经过一番思考,我想出了:
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]
定义为从i
到j
的距离,仅使用集合0, ..., k
中的顶点作为从i
到j
的路径上的中间顶点。
dist[i][j][k-1]
定义为从i
到j
的边的权重。如果中间没有边,则该权重被视为无穷大。
现在使用与上面链接的证明相同的逻辑:
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] = dist[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
在它们的路径中并不重要。由于dist[i][j][k-1]
仅用于dist[i][j][k]
的计算,dist[i][j]
将在矩阵中保留dist[i][j][k-1]
,直到dist[i][j][k]
被计算出来。如果 i
或 j
等于 k
则适用上述情况。
因此,计算的顺序无关紧要。
现在我们需要在算法的所有步骤之后显示dist[i][j] = dist[j][i]
。
我们从一个对称网格开始,因此dist[a][b] = dist[b][a]
,对于所有a
和b
。
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 的创意。
【讨论】:
请注意,上面的两个代码在实验上不会产生相同的结果。 第二个代码中的第一个更新应该是:dist[i][j] = min(dist[i][j], dist[k][i] + dist[k][j ]);第二次更新应该是:dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);第三次更新是正确的。 在假设无向和无权重的情况下,是否可以使用第二个代码进行其他改进?【参考方案2】:(使用***文章中伪代码中的符号)我相信(但尚未测试)如果 edgeCost 矩阵是对称的,那么每次迭代后路径矩阵也将是对称的。因此,您只需在每次迭代时更新一半的条目。
在较低的层次上,只需要存储一半的矩阵(因为 d(i,j) = d(j,i)),这样可以减少使用的内存量,并有望减少缓存未命中,因为您将多次访问相同的数据。
【讨论】:
以上是关于针对对称邻接矩阵优化 Floyd-Warshall的主要内容,如果未能解决你的问题,请参考以下文章