Негативні ваги за допомогою алгоритму Дікстра


113

Я намагаюся зрозуміти, чому алгоритм Дейкстри не буде працювати з негативними вагами. Читаючи приклад на Найкоротші Шляхи , я намагаюся з'ясувати такий сценарій:

    2
A-------B
 \     /
3 \   / -2
   \ /
    C

З веб-сайту:

Якщо припустити, що ребра всі спрямовані зліва направо, якщо ми почнемо з A, алгоритм Дейкстри вибере край (A, x), мінімізуючи d (A, A) + довжину (край), а саме (A, B). Потім він встановлює d (A, B) = 2 і вибирає інший край (y, C), мінімізуючи d (A, y) + d (y, C); єдиний вибір - (A, C) і він встановлює d (A, C) = 3. Але він ніколи не знаходить найкоротшого шляху від А до В через С, загальною довжиною 1.

Я не можу зрозуміти, чому використання наступної реалізації Dijkstra, d [B] не буде оновлено до 1(Коли алгоритм досягне вершини C, він запустить релакс на B, побачимо, що d [B] дорівнює 2, і тому оновити його значення для 1).

Dijkstra(G, w, s)  {
   Initialize-Single-Source(G, s)
   S ← Ø
   Q ← V[G]//priority queue by d[v]
   while Q ≠ Ø do
      u ← Extract-Min(Q)
      S ← S U {u}
      for each vertex v in Adj[u] do
         Relax(u, v)
}

Initialize-Single-Source(G, s) {
   for each vertex v  V(G)
      d[v] ← ∞
      π[v] ← NIL
   d[s] ← 0
}

Relax(u, v) {
   //update only if we found a strictly shortest path
   if d[v] > d[u] + w(u,v) 
      d[v] ← d[u] + w(u,v)
      π[v] ← u
      Update(Q, v)
}

Дякую,

Мейр


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

4
Для тих, хто має такі сумніви, ви можете знайти найкоротший шлях у графі, який не має негативних циклів ваги. Наведений вище алгоритм спрацював би, якщо функція Relax повернула значення "справжнє", коли релакс був фактично успішним, і в цьому випадку сусідня вершина "v" буде додана до черги пріоритетів, якщо її немає, або оновлена, якщо вона вже є. Це означає, що відвідані вузли знову можуть бути додані до черги пріоритетів, оскільки вони не розслабляються.
goelakash

Відповіді:


202

Запропонований вами алгоритм дійсно знайде найкоротший шлях у цьому графіку, але не всі графіки взагалі. Наприклад, розглянемо цей графік:

Рисунок графіка

Припустимо, що краї спрямовані зліва направо, як у вашому прикладі,

Ваш алгоритм буде працювати наступним чином:

  1. По- перше, ви встановили d(A)в zeroі інші відстані до infinity.
  2. Потім ви розгортаєте вузол A, встановлюючи d(B)до 1, d(C)до zeroта d(D)до 99.
  3. Далі ви розгортаєтесь C, без чистих змін.
  4. Потім ви розгортаєтесь B, що не має ефекту.
  5. Нарешті, ви розширюєте D, що змінюється d(B)на -201.

Зауважте, що в кінці цього, однак, d(C)це все ще є 0, хоча найкоротший шлях до Cнього має довжину -200. Таким чином, ваш алгоритм не вдається точно обчислити відстані в деяких випадках. Більше того, навіть якби ви зберігали вказівники, що говорять про те, як дістатися з кожного вузла до початкового вузла A, ви закінчили б неправильний шлях назад Cдо A.


35
Щоб додати до вашої чудової відповіді: Dijkstra є жадібним алгоритмом - причина його короткозорого вибору.
блякання

4
Я хотів би зазначити, що технічно всі шляхи в цьому графіку мають вартість негативної нескінченності ввічливості негативного циклу A, D, B, A.
Нейт

2
@ Nate- Для уточнення всі ребра в графіці спрямовані зліва направо. Як би важко було зобразити стріли в моєму високоякісному мистецтві ASCII. :-)
templatetypedef

2
Для тих, хто раніше не бачив графіків із негативними гранями, я вважаю корисною інтерпретацію цього графіка мережею платних доріг, де вагова межа краю дає плату, яку ви платите. Дорога -300 - це шалена дорога платної дороги, де замість цього вони дають вам 300 доларів.
D Coetzee

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

25

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

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

Якщо використовується альтернативний критерій зупинки, який зупиняє алгоритм, коли черга пріоритету (купа) працює порожньою (цей критерій зупинки також був використаний у запитанні), то dijkstra знайде правильну відстань навіть для графіків з негативними вагами, але без негативні цикли.

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


2. Незрозуміло, чому ви вважаєте, що час би мені "більше подобався Беллман-Форд", а не експоненціальним (що гірше, ніж Беллман-Форд). Чи є у вас конкретний алгоритм і доказ?
Гасса

3
До 1.: як ви можете використовувати точно таку ж реалізацію dijkstra із згаданим критерієм зупинки, яка зупиняється, коли чергу працює порожньою (див. Псевдокод у вихідному питанні), це все ще алгоритм dijkstras для найкоротших шляхів, хоча він поводиться інакше осідання вузлів кілька разів (виправлення міток).
infty10000101

1
До 2.: Це була лише здогадка, тому я збираюся її видалити. Я думаю, що ти маєш рацію з експоненціальним часом, оскільки існує експоненціально багато шляхів, які треба вивчити.
infty10000101

11

ви ніде не використовували S у своєму алгоритмі (окрім модифікації). ідея dijkstra коли вершина на S, вона більше не буде змінена. у цьому випадку, коли B знаходиться всередині S, ви знову не досягнете його через C.

цей факт забезпечує складність O (E + VlogV) [в іншому випадку ви будете повторювати ребра більше одного разу, а вершини більше, ніж один раз]

Іншими словами, алгоритм, який ви опублікували, може бути не в O (E + VlogV), як обіцяв алгоритм dijkstra.


Також немає необхідності змінювати вершину без
відрізків

це припущення саме те, що дозволяє нам використовувати S, і «знаючи», коли вершина знаходиться в S, вона ніколи більше не буде змінена.
Аміт

Ваше останнє твердження неправильне. Опублікований алгоритм має часову складність O (E + VlogV), коли він працює на графіках без негативних ребер. Немає необхідності перевіряти, що ми вже відвідали вузол, оскільки той факт, що він був відвіданий, гарантує процедуру релаксації, не додасть його ще раз у чергу.
Піксар

7

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


Жадібний алгоритм , як підказує назва, завжди робить вибір , який , здається, кращий на даний момент. Припустимо, що у вас є об'єктивна функція, яку необхідно оптимізувати (максимально або мінімізувати) у певній точці. Жадний алгоритм робить жадібний вибір на кожному кроці, щоб забезпечити оптимізацію цільової функції. Алгоритм Жадібний має лише один кадр для обчислення оптимального рішення, щоб він ніколи не повертався назад і не змінював рішення.


4

TL; DR: Відповідь залежить від вашої реалізації. Для опублікованого вами псевдокоду він працює з негативними вагами.


Варіанти алгоритму Дейкстри

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

  1. Використання вкладеної for-loop для розслаблення вершин. Це найпростіший спосіб реалізації алгоритму Діккстри. Часова складність становить O (V ^ 2).
  2. Реалізація на основі пріоритетних черг / купи + НЕ допускається повторний вхід, коли повторний вхід означає розслаблену вершину, яку можна знову перенести в чергу пріоритетів, щоб потім знову бути розслабленою .
  3. Реалізація на основі пріоритетної черги / купи + повторний вхід дозволений.

Версія 1 та 2 не матимуть графіків із негативними вагами (якщо ви отримаєте правильну відповідь у таких випадках, це просто збіг), але версія 3 все ще працює .

Псевдокод, розміщений під початковою проблемою, є версією 3 вище, тому він працює з негативними вагами.

Ось хороша довідка з Алгоритму (4-е видання) , де йдеться (і містить реалізацію Java версії 2 та 3, яку я згадав вище):

В. Чи працює алгоритм Дейкстри з негативними вагами?

А. Так і ні. Існує два найкоротші алгоритми алгоритму, відомі як алгоритм Діккстри, залежно від того, чи може вершина в черзі пріоритетів заступатись не один раз. Коли ваги невід’ємні, два варіанти збігаються (оскільки жодна вершина не буде задіяна більше одного разу). Версія, реалізована в DijkstraSP.java (яка дозволяє запускати вершину не один раз), є правильною за наявності негативних ваг ребра (але немає негативних циклів), але час його роботи експоненціальний у гіршому випадку. (Ми зауважимо, що DijkstraSP.java викидає виняток, якщо у зваженого на краю диграфа є край з негативною вагою, так що програміст не дивується цій експоненціальній поведінці.) Якщо ми модифікуємо DijkstraSP.java, щоб вершина не могла бути задіяна не раз (наприклад, використовуючи позначений масив [] для позначення тих вершин, які були розслаблені),


Щоб отримати докладніші відомості про впровадження та підключення версії 3 з алгоритмом Bellman-Ford, дивіться цю відповідь від zhihu . Це також моя відповідь (але по-китайськи). На даний момент я не встигаю перекласти це англійською мовою. Я дуже вдячний, якщо хтось міг би це зробити і відредагувати цю відповідь на stackoverflow.


1

Поміркуйте, що станеться, якщо ви переходите вперед і назад між B і C ... вуаля

(актуально лише, якщо графік не спрямований)

Відредаговано: Я вважаю, що проблема пов'язана з тим, що шлях з AC * може бути кращим, ніж AB з наявністю граней негативної ваги, тому не має значення, куди ви йдете після змінного струму, з припущенням, що це не з від’ємними ваговими гранями неможливо знайти шлях краще, ніж АВ, коли ви вирішили досягти B після переходу AC.


це неможливо, графік спрямований.
Аміт

@amit: хороший момент, я це пропустив. Час переглянути проблему
prusswan

1

"2) Чи можемо ми використовувати алгоритм Діжкра для найкоротших шляхів для графіків з негативними вагами - одна ідея може бути, обчислити мінімальне значення ваги, додати позитивне значення (рівне абсолютній величині мінімальної вагової величини) до всіх ваг і запустити алгоритм Дікскри для модифікованого графіка. Чи буде працювати цей алгоритм? "

Це абсолютно не працює, якщо всі найкоротші шляхи не мають однакової довжини. Наприклад, з урахуванням найкоротшого шляху довжиною два ребра і після додавання абсолютного значення до кожного ребра, то загальна вартість шляху збільшується на 2 * | максимум негативної ваги | З іншого боку, інший шлях довжиною три ребра, тому вартість шляху збільшується на 3 * | максимум негативної ваги |. Отже, всі окремі шляхи збільшуються на різну кількість.


0

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

У цьому випадку практично я бачив, що краще використовувати алгоритм SPFA, який має нормальну чергу і може обробляти негативні краї.

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