最短加权路径算法 - 频繁变换边缘

时间:2012-06-18 04:40:59

标签: algorithm data-structures graph shortest-path

我正在尝试解决图形问题。图表是加权和无向的。
图表大小:

no. of vertices upto  200,000 
no. of edges upto  200,000 

我要在图中的给定2个节点(S& D)之间找到最短路径。我正在使用Dijkstra's algorithm来查找此内容。
现在问题是图表经常变化。我要找到S& S之间的最短路径。 D,如果从图中删除特定边。我正在使用Dijkstra算法再次计算新路径,在删除边缘后将图形视为新图形。然而,这种方法太慢,因为可能有200,000个边缘,并且我将为每个边缘移除计算最短路径200,000次。
我正在考虑使用任何记忆技术,但如果从图中删除特定边缘,则无法确定最短路径可能会完全改变。
//更多细节
在整个问题中,来源和目的地都是固定的 每个边缘移除将有多达200,000个查询。每次只有一个边缘从每个测试用例的初始图表中删除

4 个答案:

答案 0 :(得分:4)

由于没有添加边缘,删除后的最短路径将始终大于(或等于)原始路径。 除非删除的边是原始最短路径的一部分,否则结果不会改变。 如果它是原始最短路径的一部分,那么再次运行算法是最糟糕的解决方案。

如果您没有寻找确切的答案,可以尝试使用近似的局部方法来填补缺失的边缘。


您可以扩充Dijkstra算法来存储信息,这些信息将允许您在初始搜索期间回溯到特定状态。

这意味着每次最终在放松期间更改最短路径时,记录对数据结构(包括堆)所做的更改。这允许您在第一次运行期间将算法的状态恢复到任何点。

当您移除最短路径上的边缘时,您需要返回到边缘放松之前的点,然后重新启动算法,就像删除的边缘永远不存在一样。

答案 1 :(得分:4)

我的建议:

首先使用Dijkstra找到从源到目的地的最短路径,但同时从源和目的地走(使用负距离数字来表示你已经走过目的地的距离),总是扩展一个距离最短(来自源或目的地)。一旦遇到具有反向节点值的节点,那么到达该节点的路径就是最短的。

然后移除边缘,如果边缘不是最短路径的一部分,则返回当前已知的最短路径

如果删除的边缘是最短路径的一部分,则再次执行搜索,其中已知绝对距离大于(正或负),而不是被删除的任何一个节点。将以前已知的最短路径添加到已知结果中,从开始行走时为正,从末端行走到断裂段时为负。现在从两个方向的起始点进行搜索,如果您点击一个具有值集(正面或负面)的节点,或者是前一个最短路径的一部分,那么您将找到新的最短路径。

这样做的主要好处是:

  1. 你从源和目的地走过去,所以除非你的源节点是一个边缘,否则整个节点的漫游越少,
  2. 您不会放弃整个搜索结果,即使删除的边是上一条最短路径中的第一条边,您只需找到以最短路径重新连接的路径。
  3. 每次执行粗暴重新计算的性能都相当可观,即使删除的节点是以前已知的最短路径的一部分。

    有关其工作原理,请考虑以下图表:

            I
           /
      B---E
     /   /   H
    A   D   /|
     \ / \ / |
      C---F--G
    

    我们希望从A转到H,以便轻松让我们假设每条边都值1(但它可能是任何东西)

    我们从A:

    开始
            I
           /
      B---E
     /   /   H
    0   D   /|
     \ / \ / |
      C---F--G
    

    现在将H的值设置为从0开始:

            I
           /
      B---E
     /   /   (0)
    0   D   / |
     \ / \ /  |
      C---F---G
    

    并展开:

            I
           /
      1---E
     /   /   (0)
    0   D   / |
     \ / \ /  |
      1---F---G
    

    现在我们展开下一个最低值H

            I
           /
      1---E
     /   /     (0)
    0   D     /  |
     \ / \   /   |
      1---(-1)--(-1)
    

    现在我们随意挑选B,因为它来自CFG(它们具有相同的绝对值):

            I
           /
      1---2
     /   /     (0)
    0   D     /  |
     \ / \   /   |
      1---(-1)--(-1)
    

    然后C

            I
           /
      1---2
     /   /        (0)
    0   2        /  |
     \ / \      /   |
      1---2 & (-1)--(-1)
    

    现在我们有一个节点知道它的正值和它的负值,因此我们知道它与AH的距离和因为我们首先扩展最短节点,所以这必须是最短路径,因此我们可以说从AH的最短路径为A->C->F->H且成本为ABS(2)+ABS(-1) = 3

    现在假设我们删除了C-> F行             一世            /       1 --- 2      //(0)     0 2 / |      \ / \ / |       1 2& (-1) - ( - 1)

    然后我们删除所有已知值,其绝对值高于C和F的较小值(在这种情况下为1),留下:

            I
           /
      1---E
     /   /     (0)
    0   D     /  |
     \ / \   /   |
      1   (-1)--(-1)
    

    现在我们再次扩展,从B开始:             一世            /       1 --- 2      //(0)     0 D / |      \ / \ / |       1(-1) - ( - 1)

    然后C

            I
           /
      1---2
     /   /     (0)
    0   2     /  |
     \ / \   /   |
      1   (-1)--(-1)
    

    现在F:

            I
           /
      1---2
     /   /         (0)
    0   2&(-2)    /  |
     \ /    \    /   |
      1      (-1)---(-1)
    

    因此,我们知道从AH的最短路径现在是:A-> C-> D-> F-> H和费用ABS(2)+ABS(-2) = 4

    这将适用于任意数量的节点,边和边权重,如果您没有其他节点要扩展,那么您将返回" No Route"响应。

    您可以通过不重置前一个最短路径中的节点的节点值来进一步优化它,这样做会失去简单的性质,但它不会过于复杂。

    在上面的示例中,它最初不会产生影响,但如果我们之后删除了A->C链接,那将会有所不同,因为我们会记住C和其他 I / 1---E / / H 0 D /| \ / \ / | 1 F--G 的成本链中的节点(为负)

    仅使用单面Dijkstra并在移除边缘之前回滚的好处如下所示:

    B

    现在我们展开 I / 1---2 / / H 0 D /| \ / \ / | 1 F--G

            I
           /
      1---2
     /   /   H
    0   2   /|
     \ / \ / |
      1   F--G
    

    C:

            I
           /
      1---2
     /   /   H
    0   2   /|
     \ / \ / |
      1   3--G
    

    d

            3
           /
      1---2
     /   /   H
    0   2   /|
     \ / \ / |
      1   3--G
    

    E:

            3
           /
      1---2
     /   /   4
    0   2   /|
     \ / \ / |
      1   3--4
    

    F:

    A->C->D->F->H

    然后我们确定路径现在为H,费用为4.注意我们需要在此处执行5个扩展步骤,将其与我们所需的3个方向进行比较。

    当移除的边缘越来越朝向路径的中间时,我们将通过使用双向图形步行算法重新计算新路径来大大提高节省。除非有50个节点挂起A,但只有一条路径从HH,但这是一个边缘情况,不太可能发生在普通网络,如果它这样做,平均来说仍然可以正常工作,相反,只有一条直接路径从AA但是50条边连接到{{1} }。

    鉴于你有大约200,000个边缘,可能有多达200,000个节点,与我只有9个节点和11个边缘的示例图相比,你可能会看到相当大的节省。这是基于我们正在寻找具有最少节点扩展的算法的想法,因为它们可能是大部分计算时间将用于循环的地方。

答案 2 :(得分:0)

我有个主意:

  • 第一次做Dijkstra并记住从源到目的地的最短路径的所有边缘。
  • 当您进行删除时,检查是否从最短路径中删除了边缘。如果没有,结果是一样的。如果是的话,你会做另一个Dijkstra。

另一个想法:

  • 首先执行Dijkstra,并为每个顶点记住依赖该顶点的所有元素。
  • 执行删除操作时,您应该执行类似Topological Sorting的操作,并且对于依赖于您的顶点的所有顶点执行更新,并使用这些顶点执行部分Dikstra。

答案 3 :(得分:0)

如果移除的边缘不是最远路径,则路径将保持不变。否则可能没有很好的精确解,因为问题是单调的 - 使用节点C的最短路径sp从A到B(sp(A,B))由所有最短路径组成,使得 sp(A,B)= sp(A,C)+ sp(C,B)(对于所有C)。

通过删除一个(非常好的)边缘,您可能会破坏所有这些路径。最好的解决方案(但不精确)可能是使用Floyd-Warshall算法计算所有节点对之间的所有最短路径,并且在从路径移除边缘后尝试使用最短的绕行修复路径。