调整 Floyd-Warshall 算法以检测循环
Tweaking Floyd-Warshall Algorithm to detect cycles
干杯,我正在尝试解决 有向 图中的最小长度循环问题,我遇到了一个建议我应该调整 Floyd-Warshall 算法的解决方案解决这个问题。它说我应该设置 path[i][i] = INFINITY
而不是设置 path[i][i] = 0
,但我不完全明白为什么会这样!我发现 Floyd-Warshall 使用的阵列的主对角线没有变化,那么它如何帮助我看到循环的路径?我知道算法生成的数组可以帮助我找到一对中的最短路径。例如path[i][j]
给了我从 i 到 j 的最短路径但是,尽管直觉保持不变,但我发现没有任何变化,并且我拿不出想要的结果。
我什至尝试可视化这个过程,使用这个网站 here,我生成了一个包含许多循环的图表,但是尽管对角线被初始化为无穷大,但它并没有改变。任何人都可以解释我缺少什么或我可以做些什么来解决我的问题吗?
动画网站的Floyd-Warshall算法编码方式中有一个实现细节;它会阻止您看到预期的结果。
下载源代码并查看Floyd.js
,看看不应该存在的条件:
for (var k = 0; k < this.size; k++) {
for (var i = 0; i < this.size; i++) {
for (var j = 0; j < this.size; j++) {
if (i != j && j != k && i != k) // <<== This is the problem
...
该算法从不计算从一个节点到自身(即 i == j
时)通过第三个节点的路径,因此它从不检测循环。本质上,该条件假设无法改进对自身的传递,这在主对角线设置为 INFINITY
.
的情况下是不正确的
对于有向图,想法是您只是更改 path
矩阵,而不是存储从 i
到 j
的最短路径的长度,path[i][j]
存储最短的 non-empty 路径的长度,即它只包括至少有一条边的路径。当然,这只会影响从顶点到自身的路径。
所以现在,我们用 infinity
而不是 0 来初始化 path[i][i]
,因为那时我们还没有找到从顶点到它自身的任何 non-empty 路径。
然后我们在根据边初始化矩阵的其余部分后进行正常的 Floyd-Warshall 迭代:
for k in |V|:
for j in |V|:
for i in |V|:
path[i][j] = min(path[i][j], path[i][k] + path[k][j])
假设有一个简单的循环 1 -> 2 -> 1。然后,当 (i,j,k) == (1,1,2)
时,我们执行 path[1][1] = min(path[1][1], path[1][2] + path[2][1])
这会将 path[1][1]
从 infinity
更改为周期长度。
如果您修改了一个实现但它没有这样做,那么该实现可能已经过优化以完全忽略对角线。
干杯,我正在尝试解决 有向 图中的最小长度循环问题,我遇到了一个建议我应该调整 Floyd-Warshall 算法的解决方案解决这个问题。它说我应该设置 path[i][i] = INFINITY
而不是设置 path[i][i] = 0
,但我不完全明白为什么会这样!我发现 Floyd-Warshall 使用的阵列的主对角线没有变化,那么它如何帮助我看到循环的路径?我知道算法生成的数组可以帮助我找到一对中的最短路径。例如path[i][j]
给了我从 i 到 j 的最短路径但是,尽管直觉保持不变,但我发现没有任何变化,并且我拿不出想要的结果。
我什至尝试可视化这个过程,使用这个网站 here,我生成了一个包含许多循环的图表,但是尽管对角线被初始化为无穷大,但它并没有改变。任何人都可以解释我缺少什么或我可以做些什么来解决我的问题吗?
动画网站的Floyd-Warshall算法编码方式中有一个实现细节;它会阻止您看到预期的结果。
下载源代码并查看Floyd.js
,看看不应该存在的条件:
for (var k = 0; k < this.size; k++) {
for (var i = 0; i < this.size; i++) {
for (var j = 0; j < this.size; j++) {
if (i != j && j != k && i != k) // <<== This is the problem
...
该算法从不计算从一个节点到自身(即 i == j
时)通过第三个节点的路径,因此它从不检测循环。本质上,该条件假设无法改进对自身的传递,这在主对角线设置为 INFINITY
.
对于有向图,想法是您只是更改 path
矩阵,而不是存储从 i
到 j
的最短路径的长度,path[i][j]
存储最短的 non-empty 路径的长度,即它只包括至少有一条边的路径。当然,这只会影响从顶点到自身的路径。
所以现在,我们用 infinity
而不是 0 来初始化 path[i][i]
,因为那时我们还没有找到从顶点到它自身的任何 non-empty 路径。
然后我们在根据边初始化矩阵的其余部分后进行正常的 Floyd-Warshall 迭代:
for k in |V|:
for j in |V|:
for i in |V|:
path[i][j] = min(path[i][j], path[i][k] + path[k][j])
假设有一个简单的循环 1 -> 2 -> 1。然后,当 (i,j,k) == (1,1,2)
时,我们执行 path[1][1] = min(path[1][1], path[1][2] + path[2][1])
这会将 path[1][1]
从 infinity
更改为周期长度。
如果您修改了一个实现但它没有这样做,那么该实现可能已经过优化以完全忽略对角线。