Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 为什么Dijkstra';s算法使用减少密钥?_Algorithm_Data Structures_Priority Queue_Graph Algorithm_Dijkstra - Fatal编程技术网

Algorithm 为什么Dijkstra';s算法使用减少密钥?

Algorithm 为什么Dijkstra';s算法使用减少密钥?,algorithm,data-structures,priority-queue,graph-algorithm,dijkstra,Algorithm,Data Structures,Priority Queue,Graph Algorithm,Dijkstra,Dijkstra教授给我的算法如下 while pqueue is not empty: distance, node = pqueue.delete_min() if node has been visited: continue else: mark node as visited if node == target: break for each neighbor of node: p

Dijkstra教授给我的算法如下

while pqueue is not empty:
    distance, node = pqueue.delete_min()
    if node has been visited:
        continue
    else:
        mark node as visited
    if node == target:
        break
    for each neighbor of node:
         pqueue.insert(distance + distance_to_neighbor, neighbor)
但是我已经读了一些关于算法的书,我看到很多版本都使用decrease键而不是insert键


这是为什么?这两种方法之间的区别是什么?

使用减少键而不是重新插入节点的原因是为了保持优先级队列中的节点数量较小,从而保持优先级队列出列的总数较小,并且每个优先级队列平衡的成本较低

在Dijkstra算法的一个实现中,将节点与其新优先级重新插入优先级队列中,为图中的m条边中的每一条添加一个节点到优先级队列中。这意味着优先级队列上有m个排队操作和m个出列操作,总运行时间为O(mte+mtd),其中Te是排队进入优先级队列所需的时间,Td是从优先级队列出列所需的时间

在支持减少密钥的Dijkstra算法的实现中,持有节点的优先级队列从其中的n个节点开始,在算法的每个步骤上移除一个节点。这意味着堆出列的总数为n。每个节点上的每个边都可能调用一次减少关键点,因此完成的减少关键点总数最多为m。这给出了一个运行时(n Te+n Td+m Tk),其中Tk是调用reduce key所需的时间

那么这对运行时有什么影响呢?这取决于您使用的优先级队列。下面是一个快速表格,显示了不同优先级队列和不同Dijkstra算法实现的总体运行时:

Queue          |  T_e   |  T_d   |  T_k   | w/o Dec-Key |   w/Dec-Key
---------------+--------+--------+--------+-------------+---------------
Binary Heap    |O(log N)|O(log N)|O(log N)| O(M log N)  |   O(M log N)
Binomial Heap  |O(log N)|O(log N)|O(log N)| O(M log N)  |   O(M log N)
Fibonacci Heap |  O(1)  |O(log N)|  O(1)  | O(M log N)  | O(M + N log N)
正如您所看到的,对于大多数类型的优先级队列,渐进运行时实际上没有什么区别,而减少密钥版本也不太可能做得更好。然而,如果您使用优先级队列的实现,那么当使用decrease key时,Dijkstra算法的效率确实会逐渐提高

简言之,使用reduce键,加上一个好的优先级队列,可以使Dijkstra的渐近运行时间下降到超出您继续排队和退队所能达到的程度

除此之外,一些更先进的算法,如Gabow的最短路径算法,使用Dijkstra的算法作为子例程,并且严重依赖于减少密钥实现。他们利用这样一个事实,即如果您事先知道有效距离的范围,就可以基于该事实构建超级高效的优先级队列


希望这有帮助

2007年,有一篇论文研究了使用reduce key版本和insert版本在执行时间上的差异。看


他们的基本结论是对大多数图不使用decrease键。特别是对于稀疏图,非减少键的速度明显快于减少键版本。有关更多详细信息,请参阅本文。

有两种实现Dijkstra的方法:一种使用支持reduce key的堆,另一种使用不支持reduce key的堆

它们通常都是有效的,但后者通常是首选。 在下文中,我将使用“m”表示图的边数,“n”表示图的顶点数:

如果您希望获得尽可能最好的最坏情况复杂性,那么可以使用支持reduce key的Fibonacci堆:您将得到一个很好的O(m+nlogn)

如果您关心平均情况,那么也可以使用二进制堆:您将得到O(m+nlog(m/n)logn)。证据是,第99/100页。如果图是稠密的(m>>n),则此图和前一图都趋向于O(m)

若你们想知道若你们在真实的图形上运行它们会发生什么,你们可以检查纸张,正如马克·梅克顿在他的回答中所建议的

实验结果将表明,在大多数情况下,“更简单”的堆将给出最好的结果

事实上,在使用reduce键的实现中,Dijkstra在使用简单二进制堆或配对堆时比使用Fibonacci堆时性能更好。这是因为斐波那契堆涉及更大的常数因子,实际减少键操作的数量往往比最坏情况预测的要小得多


出于类似的原因,不必支持减少键操作的堆具有更少的常量因子,并且实际上性能最好。尤其是当图表稀疏时。

Downvoter-您能解释一下这个问题的错误吗?我认为这是完全公平的,很多人(包括我)第一次被介绍给OP的Dijkstra版本,而不是decrease key版本。一个疑问是,既然插入版本的堆每边有一个节点,即O(m),那么它的访问时间不应该是O(logm),总运行时间应该是O(mlogm)?我的意思是,在正规图中,m不大于n^2,因此这减少到O(m logn),但在两个节点可能由不同权重的多条边连接的图中,m是无界的(当然,我们可以声称两个节点之间的最小路径只使用最小边,并将其减少到正规图,但对于nonce,这很有趣)@rampion-你确实有一点,但由于我认为通常假设在启动该算法之前,平行边已减少,因此我认为O(logn)与O(logm)的关系不大。通常m被假定为O(n^2)。‎ 是该纸张的工作链接。警告:链接的纸张中存在错误。第16页,函数B.2:
如果k
应该是
如果k