Чому в алгоритмі Дейкстри використовується клавіша зменшення?


93

Алгоритм Дейкстри мене навчили таким чином

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 Дейкстри, а не з версією зменшення клавіші.
templatetypedef

Відповіді:


68

Причиною використання клавіші зменшення, а не повторного вставлення вузлів, є збереження невеликої кількості вузлів у черзі пріоритетів, таким чином, загальна кількість вилучень черг пріоритетів невелика, а вартість кожного балансу черги пріоритетів низькою.

У реалізації алгоритму Дейкстри, який повторно вставляє вузли в чергу пріоритетів зі своїми новими пріоритетами, один вузол додається до черги пріоритетів для кожного з m ребер на графіку. Це означає, що в пріоритетній черзі є m операцій черги та m операцій виведення черги, що дає загальний час виконання O (m T e + m T d ), де T e - час, необхідний для черги в пріоритетну чергу, а T d - час, необхідний для зняття з черги пріоритетів.

У реалізації алгоритму Дейкстри, який підтримує клавішу зменшення, пріоритетна черга, що утримує вузли, починається з n вузлів у ній, і на кожному кроці алгоритму видаляється по одному вузлу. Це означає, що загальна кількість декуерів купи є n. Кожен вузол буде мати натиснутий клавішу зменшення потенційно один раз для кожного ребра, що в нього входить, тому загальна кількість виконаних клавіш зменшення становить не більше m. Це дає час виконання (n T e + n T d + m T k ), де T k - час, необхідний для виклику клавіші зменшення.

То який вплив це впливає на час роботи? Це залежить від того, яку чергу пріоритетів ви використовуєте. Ось коротка таблиця, яка показує різні черги пріоритетів та загальний час виконання різних алгоритмів реалізації Дейкстри:

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)

Як бачите, у більшості типів пріоритетних черг насправді немає різниці в асимптотичному виконанні, і версія із зменшувальним ключем, швидше за все, не набагато краще. Однак якщо ви використовуєте реалізацію купи Фібоначчі для черги пріоритетів, тоді алгоритм Дейкстри буде асимптотично ефективнішим при використанні клавіші зменшення.

Коротше кажучи, використання клавіші зменшення плюс черги з хорошим пріоритетом може вивести асимптотичний час виконання Дейкстри за межі можливого, якщо ви продовжуєте робити черги та зміни.

Окрім цього, деякі більш досконалі алгоритми, такі як Алгоритм найкоротших шляхів Габова, використовують алгоритм Дейкстри як підпрограму і значною мірою покладаються на реалізацію клавіші зменшення. Вони використовують той факт, що якщо ви знаєте діапазон допустимих відстаней заздалегідь, ви можете створити надзвичайно ефективну чергу пріоритетів на основі цього факту.

Сподіваюся, це допомагає!


1
+1: я забув врахувати купу. Одна примха, оскільки купа версії вставки має вузол на ребро, тобто O (m), чи не повинен час її доступу бути O (log m), що дає загальний час роботи O (m log m)? Я маю на увазі, що в нормальному графіку m не перевищує n ^ 2, тому це зменшується до O (m log n), але в графіку, де два вузли можуть бути з'єднані безліччю ребер різної ваги, m необмежена (звичайно , ми можемо стверджувати, що мінімальний шлях між двома вузлами використовує лише мінімальні ребра, і зводимо це до нормального графіка, але для цього випадку це цікаво).
чемпіон

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).

Якщо ви хочете знати, що трапиться, якщо запустити їх на реальних графіках, ви можете перевірити цей документ, як запропонував Марк Мекетон у своїй відповіді.

Результати експериментів покажуть, що «простіша» купа дасть найкращі результати в більшості випадків.

Насправді, серед реалізацій, які використовують клавішу зменшення, Дейкстра працює ефективніше, коли використовує просту купку двійкового файлу або купу пари, ніж коли вона використовує купу Фібоначчі. Це пов’язано з тим, що купи Фібоначчі залучають більші постійні фактори, і фактична кількість операцій із зменшувальними ключами, як правило, набагато менша, ніж прогнозує найгірший випадок.

З подібних причин купа, яка не повинна підтримувати операцію із зменшенням клавіші, має ще менш постійні коефіцієнти і насправді працює найкраще. Особливо, якщо графік розріджений.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.