Skiena的算法书包含以下对Floyd Warshall algorithm的解释:
floyd(adjacency_matrix *g)
{
int i,j; /* dimension counters */
int k; /* intermediate vertex counter */
int through_k; /* distance through vertex k */
for (k=1; k<=g->nvertices; k++)
for (i=1; i<=g->nvertices; i++)
for (j=1; j<=g->nvertices; j++) {
through_k = g->weight[i][k]+g->weight[k][j];
if (through_k < g->weight[i][j])
g->weight[i][j] = through_k;
}
}
Floyd-Warshall全对最短路径在O(n 3 )时间内运行,这是渐近的 没有比n调用Dijkstra算法更好的了。但是,循环是如此 这个项目非常简短,以至于它在实践中运行得更好。值得注意的是 罕见的图算法在邻接矩阵上比邻接更好 列表。
有人可以详细说明为什么大胆的部分是真的吗?
答案 0 :(得分:3)
但是,循环太紧,程序太短而无法运行 在实践中更好。
如果你看一下这个算法,就会有三个循环,只有两个语句嵌套在那些循环中。唯一的逻辑是在第三个嵌套循环中完成的。如果你运行了n Djikstra,那么逻辑将在第一个循环中完成,也可以在持续嵌套的情况下完成,这很容易让人觉得不干净。使用干净的三个循环,计算机也应该更容易管理内存。
答案 1 :(得分:2)
这个问题的区别与q-sort和heap-sort
之间的差异相同答案 2 :(得分:1)
让我们分解一下:
Floyd-Warshall全对最短路径在O(n 3 )时间内运行......
这是因为我们有一个三for
个循环,每个循环都有 n 次迭代
...渐渐地没有比调用Dijkstra算法的n更好。 ... 强>
回想一下,对Dijkstra算法的单次调用将告诉我们从一个特定节点 x1 到图中所有节点的所有最短路径。因此,我们可以对图中所有节点的Dijkstra算法进行 n 调用: x1 , x2 ,... xn 找到从 x1 到所有节点的最短路径, x2 到所有节点,... xn 到所有节点。换句话说,这给了我们所有对最短路径 - 就像Floyd Warshall一样!
Running Dijkstra n times:
time = number of nodes × O((n + e)lgn) [assuming binary heap used for Dijkstra]
= n × O((n + e)lgn)
= O(n(n + e)lgn)
确实如此,Floyd-Warshall的 O(n ^ 3)时间并不优于 O(n(n + e)lgn)强烈>向Dijkstra发出 n 电话的时间。
...然而,循环太紧,程序太短,以至于在实践中运行得更好。
这里的关键词是“ in practice ”。请记住,渐近分析并不完美。它是性能的数学抽象/近似。当我们实际运行代码时,有许多实际因素没有考虑到。处理器具有复杂的低级架构,用于获取和运行指令。它pipelines说明,pre-fetches说明,尝试predict指令,caches指令和数据,......这是非常不可预测的!然而,所有这些低级优化都会对算法的整体性能产生巨大影响。理论上较慢的算法可以获得提升,理论上快速的算法可能无法获得相同的提升。这有时被称为大符号的hidden constant factor。
事实证明,处理器喜欢优化嵌套的for
循环和多维数组!这就是Skiena在这里提到的。数组的for
循环充分利用了temporal and spacial locality,并且可以很好地处理低级处理器优化。另一方面,Dijkstra的算法也没有做到这一点,因此处理器优化也不起作用。因此,Dijkstra在实践中确实可能会变慢
Floyd-Warshall是一个“短程序”,意思是没有使用任何复杂的数据结构,重复的指令数量很少。这些事情以及处理器优化为Floyd-Warshall带来了一个隐藏的小因子。也就是说, O(k·n 3 )中的 k 很小。