调整 Floyd-Warshall 算法以检测循环

Tweaking Floyd-Warshall Algorithm to detect cycles

干杯,我正在尝试解决 有向 图中的最小长度循环问题,我遇到了一个建议我应该调整 Floyd-Warshall 算法的解决方案解决这个问题。它说我应该设置 path[i][i] = INFINITY 而不是设置 path[i][i] = 0,但我不完全明白为什么会这样!我发现 Floyd-Warshall 使用的阵列的主对角线没有变化,那么它如何帮助我看到循环的路径?我知道算法生成的数组可以帮助我找到一对中的最短路径。例如path[i][j] 给了我从 ij 的最短路径但是,尽管直觉保持不变,但我发现没有任何变化,并且我拿不出想要的结果。

我什至尝试可视化这个过程,使用这个网站 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 矩阵,而不是存储从 ij 的最短路径的长度,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 更改为周期长度。

如果您修改了一个实现但它没有这样做,那么该实现可能已经过优化以完全忽略对角线。