Dijkstra의 알고리즘이 감소 키를 사용하는 이유는 무엇입니까?


93

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)

그러나 나는 알고리즘에 대해 약간의 독서를 해왔고, 많은 버전에서 삽입과는 반대로 감소 키를 사용하는 것으로 보입니다.

그 이유는 무엇이며 두 접근 방식의 차이점은 무엇입니까?


14
Downvoter-이 질문의 문제점을 설명해 주시겠습니까? 나는 그것이 완벽하게 공평하다고 생각하고 많은 사람들 (나를 포함하여)이 감소 키 버전이 아닌 OP 버전의 Dijkstra 's를 처음 소개 받았습니다.
templatetypedef

답변:


68

노드를 다시 삽입하는 대신 감소 키를 사용하는 이유는 우선 순위 대기열의 노드 수를 작게 유지하여 우선 순위 대기열에서 빼는 총 수를 줄이고 각 우선 순위 대기열 균형의 비용을 낮게 유지하기 위해서입니다.

노드를 새로운 우선 순위로 우선 순위 대기열에 다시 삽입하는 Dijkstra 알고리즘의 구현에서 그래프의 m 개 에지 각각에 대한 우선 순위 대기열에 하나의 노드가 추가됩니다. 즉, 우선 순위 대기열에는 m 개의 대기열에 넣기 작업과 m 개의 대기열에서 빼기 작업이있어 ​​총 런타임이 O (m T e + m T d )가됩니다. 여기서 T e 는 우선 순위 대기열에 추가하는 데 필요한 시간이고 T d 는 우선 순위 대기열에서 대기열을 빼는 데 필요한 시간입니다.

감소 키를 지원하는 Dijkstra의 알고리즘 구현에서 노드를 보유하는 우선 순위 대기열은 n 개의 노드로 시작하고 알고리즘의 각 단계에서 하나의 노드를 제거합니다. 이는 힙 큐에서 빼는 총 수가 n임을 의미합니다. 각 노드에는 잠재적으로 각 에지에 대해 한 번 호출되는 감소 키가 있으므로 수행 된 감소 키의 총 수는 최대 m입니다. 이것은 (n T e + n T d + m T k ) 의 런타임을 제공하며 , 여기서 T k 는 감소 키를 호출하는 데 필요한 시간입니다.

그렇다면 이것이 런타임에 어떤 영향을 미칩니 까? 사용하는 우선 순위 대기열에 따라 다릅니다. 다음은 다양한 우선 순위 대기열과 다양한 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)

보시다시피 대부분의 우선 순위 대기열 유형에서 점근 적 런타임에는 실제로 차이가 없으며 키 감소 버전이 훨씬 더 잘할 가능성은 없습니다. 그러나 우선 순위 대기열의 피보나치 힙 구현 을 사용하는 경우 실제로 Dijkstra의 알고리즘은 감소 키를 사용할 때 점근 적으로 더 효율적입니다.

간단히 말해서, 감소 키와 좋은 우선 순위 대기열을 사용하면 대기열에 추가 및 대기열에서 빼기를 계속하면 가능한 것 이상으로 Dijkstra의 점근 적 런타임을 삭제할 수 있습니다.

이 점 외에도 Gabow의 최단 경로 알고리즘과 같은 일부 고급 알고리즘은 Dijkstra의 알고리즘을 서브 루틴으로 사용하고 감소 키 구현에 크게 의존합니다. 그들은 유효한 거리의 범위를 미리 알고 있다면 그 사실을 기반으로 매우 효율적인 우선 순위 대기열을 만들 수 있다는 사실을 사용합니다.

도움이 되었기를 바랍니다!


1
+1 : 나는 힙을 설명하는 것을 잊었습니다. 삽입 버전의 힙에는 에지 당 노드 (예 : O (m))가 있기 때문에 액세스 시간이 O (log m)이어야하며 총 실행 시간이 O (m log m)가되어야합니다. 즉, 일반 그래프에서 m은 n ^ 2보다 크지 않으므로 O (m log n)로 감소하지만 두 노드가 서로 다른 가중치의 여러 모서리로 결합 될 수있는 그래프에서는 m이 제한되지 않습니다 (물론 , 우리는 두 노드 사이의 최소 경로가 최소 에지 만 사용한다고 주장 할 수 있으며이를 일반 그래프로 줄일 수 있지만 nonce의 경우 흥미 롭습니다).
rampion 2012

2
@ rampion- 당신은 요점이 있지만 일반적으로 알고리즘을 시작하기 전에 평행 가장자리가 감소되었다고 가정하기 때문에 O (log n) 대 O (log m)가 그다지 중요하지 않다고 생각합니다. 보통 m은 O (n ^ 2)라고 가정합니다.
templatetypedef

27

2007 년에는 감소 키 버전과 삽입 버전 사용의 실행 시간 차이를 연구 한 논문이있었습니다. 참조 http://www.cs.utexas.edu/users/shaikat/papers/TR-07-54.pdf를

그들의 기본적인 결론은 대부분의 그래프에 감소 키를 사용하지 않는 것입니다. 특히 희소 그래프의 경우 비 감소 키가 감소 키 버전보다 훨씬 빠릅니다. 자세한 내용은 논문을 참조하십시오.


7
cs.sunysb.edu/~rezaul/papers/TR-07-54.pdf 는 해당 논문에 대한 작업 링크입니다.
eleanora

경고 : 링크 된 문서에 버그가 있습니다. 16 페이지, 함수 B.2 : if k < d[u]이어야합니다 if k <= d[u].
Xeverous

2

Dijkstra를 구현하는 방법에는 두 가지가 있습니다. 하나는 감소 키를 지원하는 힙을 사용하고 다른 하나는이를 지원하지 않는 힙을 사용합니다.

둘 다 일반적으로 유효하지만 일반적으로 후자가 선호됩니다. 다음에서는 'm'을 사용하여 가장자리 수를 나타내고 'n'을 사용하여 그래프의 정점 수를 나타냅니다.

최악의 경우 가장 복잡한 복잡성을 원한다면 감소 키를 지원하는 피보나치 힙을 사용하면됩니다. 좋은 O (m + nlogn)을 얻을 수 있습니다.

평균적인 경우에 관심이 있다면 바이너리 힙도 사용할 수 있습니다. O (m + nlog (m / n) logn)을 얻게됩니다. 증거는 여기 , 페이지 99/100입니다. 그래프가 조밀하면 (m >> n),이 그래프와 이전 그래프는 모두 O (m) 경향이 있습니다.

실제 그래프에서 실행하면 어떻게되는지 알고 싶다면 Mark Meketon이 그의 답변에서 제안한 것처럼 문서를 확인할 수 있습니다 .

실험 결과가 보여주는 것은 "단순한"힙이 대부분의 경우 최상의 결과를 제공한다는 것입니다.

실제로 감소 키를 사용하는 구현 중에서 Dijkstra는 간단한 이진 힙 또는 페어링 힙을 사용할 때 피보나치 힙을 사용할 때보 다 더 잘 수행됩니다. 이는 피보나치 힙이 더 큰 상수 인자를 포함하고 실제 감소 키 연산 수는 최악의 경우가 예측하는 것보다 훨씬 적기 때문입니다.

비슷한 이유로 키 감소 작업을 지원할 필요가없는 힙은 더 적은 상수 요소를 가지며 실제로 가장 잘 수행됩니다. 특히 그래프가 희소 한 경우.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.