Algorithm 我们能改变迪克斯特拉吗;使用负权重的s算法?

Algorithm 我们能改变迪克斯特拉吗;使用负权重的s算法?,algorithm,dijkstra,directed-graph,single-source,Algorithm,Dijkstra,Directed Graph,Single Source,取自维基百科的伪代码: function Dijkstra(Graph, source): 2 for each vertex v in Graph: // Initializations 3 dist[v] := infinity ; // Unknown distance function from source to v 4

取自维基百科的伪代码:

function Dijkstra(Graph, source):
 2      for each vertex v in Graph:                                // Initializations
 3          dist[v] := infinity ;                                  // Unknown distance function from source to v
 4          previous[v] := undefined ;                             // Previous node in optimal path from source
 5      end for ;
 6      dist[source] := 0 ;                                        // Distance from source to source
 7      Q := the set of all nodes in Graph ;                       // All nodes in the graph are unoptimized - thus are in Q
 8      while Q is not empty:                                      // The main loop
 9          u := vertex in Q with smallest distance in dist[] ;    // Start node in first case
10          if dist[u] = infinity:
11              break ;                                            // all remaining vertices are inaccessible from source
12          end if ;
13          remove u from Q ;
14          for each neighbor v of u:                              // where v has not yet been removed from Q.
15              alt := dist[u] + dist_between(u, v) ;
16              if alt < dist[v]:                                  // Relax (u,v,a)
17                  dist[v] := alt ;
18                  previous[v] := u ;
19                  decrease-key v in Q;                           // Reorder v in the Queue
20              end if ;
21          end for ;
22      end while ;
23      return dist[] ;
24  end Dijkstra.
函数Dijkstra(图,源):
图中的每个顶点v有2个://初始化
3距离[v]:=无穷大;//源到v的未知距离函数
4以前的[v]:=未定义;//来自源的最佳路径中的前一个节点
5.结束通话;
6距离[来源]:=0;//源到源的距离
7 Q:=图中所有节点的集合;//图中的所有节点都是未优化的-因此在Q中
8而Q不是空的://主循环
9 u:=Q中距离最小的顶点[];//在第一种情况下启动节点
10如果距离[u]=无穷大:
11休息;//所有剩余顶点都无法从源访问
12如果结束,则结束;
13从Q中去除u;
14对于u://的每个相邻v,其中v尚未从Q中移除。
15 alt:=距离[u]+距离(u,v)之间的距离;
16如果alt
现在,在第14行中,我们看到松弛仅应用于
u
的邻居,这些邻居尚未从
Q
中删除。但是,如果我们也考虑从
Q
中删除的
u
的邻居,那么在我看来,算法确实在负权重下工作。我没有发现任何与这一说法相矛盾的实例


那么为什么Dijkstra的算法没有这样改变呢?

不,正如前面所说的不可能。该算法对于负权重没有意义,除非严格限制提供的图类型

假设一个图的节点a、B、C和边的权重AB=-1、BA=0、BC=1

现在,a和C之间不再存在最短路径,您可以通过在a和B之间来回一次来创建一条较短的路径


当然,该算法仍将运行,但会给出错误的答案。

只要确保不遍历任何节点或边两次,就可以确保Dijkstra的算法在负值下运行。这里所说的工作,我指的是终止。然而,结果可能不是最优的

Dijkstra的算法有一种贪婪的感觉。想象下图:

A --- 1 --- B
|           |
3          -4
|           |
C -- -1 --- D
如果我们想从A到B,最好的路径是A-C-D-B,但是Dijkstra的算法找到了A-B。你不能让Dijkstra的算法预测未来,因为它是一个贪婪的算法。通过预测未来,我的意思是知道以后,路径的成本可能会降低。请注意,这意味着您的修改如果应用于Dijkstra算法的一个版本,并且该版本在看到目的地后立即终止,则将无法正常工作。在您发布的版本中,除了有更有效的方法来处理负面边缘外,您的修改是有效的(请参见注释)


顺便说一句,带负值的无向图或带负循环的有向图中的最短路径根本没有意义

Dijkstra可以访问每个节点一次,因为当它选择一个新节点进行访问时,他会选择从根节点开始具有最短路径的未访问节点。因此,他可以安全地假设,通过另一个未访问的节点到该节点没有更短的路径(因为如果您知道的从a到B的最佳路径成本为2,从a到C的最佳路径成本为3,则没有机会找到从a到B的更好路径,例如a>C>B)

如果你加上负权重,你会突然打破这个假设


当然,您可以使用建议的修改,但这样您将失去只访问每个节点一次的好处;因此,与福特贝尔曼(Ford Bellman)等其他算法相比,它将失去其性能增益。您基本上有两种选择

  • 您可以根据自己的建议更改算法。若你们有并没有负循环的有向图,那个么这是一个正确的算法,但它可能非常慢(因为你们会多次访问每个节点)。我认为当这个算法具有指数时间复杂度时,就存在这种情况

  • 你可以通过使用效价来改变成本。每个顶点都有一些势h(v),边u->v的权重为w(u,v)+h(u)-h(v)。请注意,这并不影响给定两个顶点(s,t)之间的哪条路径是最短的,只是其代价由h(s)-h(t)改变。 但你需要计算效价。这里建议这样做的好方法:


  • 好吧,仅仅放松修改后的Dijkstra版本中已经访问过的节点是不够的(在包含负边的图中,这会给你一个错误的答案)。此外,您需要将任何松弛的边放入容器中(例如,优先级队列或仅队列)。因此,您可以将第19行中的代码修改为:

    if v is in priority queue
        then decrease-key(v)
    else
        add v into priority queue
    
    在这种情况下,您的算法可以工作。此外,对于正加权图,Dijkstra改进版的效率不会降低。这很容易被证明,因为这自然是一个贪婪的算法

    然而,对于含有负边的图,新算法在理论分析方面可能会变得缓慢,但在实际应用中效果良好

    实际上,你可以看看段定凡(1994)在中国发表的一个名为SPFA(最短路径更快算法)的算法。很多OIer(奥林匹克信息)都知道这个算法,因为它有时可能会打败Dijkstra

    是的,你的