Чому алгоритм Dijkstra не працює для відхилення відхилення ваги?


121

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

Я говорю лише про ребра, а не про негативні цикли ваги.


3
Правильна відповідь з хорошим прикладом буде: stackoverflow.com/questions/6799172 / ...
Amitk

Відповіді:


175

Нагадаємо, що в алгоритмі Дейкстри, коли вершина позначена як "закрита" (і поза відкритим набором) - алгоритм знайшов найкоротший шлях до нього , і йому більше ніколи не доведеться розвивати цей вузол - він передбачає шлях, розроблений до цього шлях найкоротший.

Але з негативними вагами - це може бути неправдою. Наприклад:

       A
      / \
     /   \
    /     \
   5       2
  /         \
  B--(-10)-->C

V={A,B,C} ; E = {(A,C,2), (A,B,5), (B,C,-10)}

Дайкстра з А спочатку розвине С, а згодом не зможе його знайти A->B->C


РЕДАКТУЙТЕ трохи глибше пояснення:

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

Ідея його така: Якщо у нас вершина відкрита таким, що її вартість мінімальна - додаючи будь-яке додатне число до будь-якої вершини - мінімальність ніколи не зміниться.
Без обмеження на додатні числа - вищенаведене припущення не відповідає дійсності.

Оскільки ми «знаємо», що кожна вершина, яка була «закрита», мінімальна - ми можемо сміливо робити крок розслаблення - не «озираючись». Якщо нам потрібно "озирнутися назад" - Bellman-Ford пропонує рекурсивне (DP) рішення зробити це.


5
Вибачте, але я не отримую жодної помилки. Спочатку A->Bбуде 5 і A->Cбуде 2. Потім B->Cбуде -5. Значить, значення Cбуде таким -5же, як і bellman-ford. Як це не дає правильної відповіді?
Anirban Nag 'tintinmj'

5
По-перше, @tintinmj, Dijkstra "закриє" вузол Aзі значенням 0. Потім він подивиться на вузол з мінімальним значенням, який Bдорівнює 5 і Cдорівнює 2. Мінімальний - Cзначить, він закриється Cзі значенням 2 і ніколи не оглянеться назад, коли пізніше Bзакрито, воно не може змінити значення C, оскільки воно вже "закрите".
Аміт

4
@amit Як ​​алгоритм Dijkstra не знайде шлях A -> B -> C? Спочатку оновиться Cвідстань до 2, а потім Bвідстань до 5. Припускаючи, що у вашому графіку немає вихідних країв C, тоді ми нічого не робимо під час відвідування C(а його відстань досі 2). Потім ми відвідуємо Dсусідні вузли, і єдиний сусідній вузол C, нове відстань якого становить -5. Зауважте, що в алгоритмі Dijkstra ми також відслідковуємо батько, від якого ми Cдістаємось (і оновлюємо) вузол, і виконуючи це , ви отримаєте батьківський B, а потім A, що призводить до правильного результату. Що я пропускаю?
nbro

12
@amit Проблема ваших міркувань (я думаю), і я бачив інших людей, які це роблять (як не дивно), це те, що ви думаєте, що алгоритм не перегляне вузли, найкоротша відстань яких вже визначена (і що ми виконали), але це невірно, і тому у нас є крок "релаксації" ... Ми повторюємо всі вузли графіка, і для кожного з них ми повторюємо суміжні вузли, навіть якщо будь-який із сусідніх вузлів може наприклад, уже видалено, наприклад, з нашої черги з мінімальним пріоритетом.
nbro

10
@amit Перевірте цей відповідь на подібне питання, де приклад на самому справі має сенс: stackoverflow.com/a/6799344/3924118
nbro

37

Розгляньте наведений нижче графік із джерелом як Vertex A. Спершу спробуйте самостійно запустити алгоритм Dijkstra.

введіть тут опис зображення

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

Алгоритм Дейкстри

Отже, починаючи значення ( відстань від джерела до вершини ), спочатку присвоєні кожній вершині,

ініціалізація

Спочатку витягуємо вершину в Q = [A, B, C], яка має найменше значення, тобто A, після чого Q = [B, C] . Примітка A має спрямований край до B і C, і обидва вони знаходяться в Q, тому ми оновлюємо обидва ці значення,

перша ітерація

Тепер витягуємо C як (2 <5), тепер Q = [B] . Зауважте, що C пов'язаний ні з чим, тому line16цикл не працює.

друга ітерація

Нарешті витягуємо В, після чого Q - Phi. Примітка B має спрямований край до C, але C відсутня в Q, тому ми знову не вводимо цикл for line16,

3-й?

Отже, ми закінчуємо відстані як

хлопці не змінюються

Зверніть увагу, як це неправильно, оскільки найкоротша відстань від А до С становить 5 + -10 = -5, коли ви йдете від a до b до c.

Тож для цього графіка Алгоритм Дійкстри неправильно обчислює відстань від А до С.

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

Що line16робить цикл, це взяти вершину u і сказати: "Ей, схоже, ми можемо перейти до v від джерела через u , це те, що (альт або альтернатива) відстань краще, ніж поточний dist [v], який ми отримали? Якщо так, можна оновити dist [v] "

Зверніть увагу , що в line16вони перевіряють всі сусіди V (тобто спрямоване ребро існує від U до V ), з U які ще в Q . У line14них видалити відвідану замітку з Q. Таким чином , якщо х є відвідуваним сусідом U , шлях джерело до u до xбуде навіть не розглядаються як можливий короткий шлях від джерела до V .

У нашому прикладі вище C був відвідуваним сусідом B, тому шлях Від А до В до Сне вважався, залишаючи поточний найкоротший шлях Від А до Снезмінним.

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

Тому я кажу, що при запуску цього алгоритму, якщо x витягується з Q до y , то не можна знайти шлях - неможливоякий коротший. Дозвольте пояснити це на прикладі,

Оскільки y щойно був видобутий і x був видобутий раніше, тоді dist [y]> dist [x], оскільки в іншому випадку y був би вилучений до x . ( line 13хв відстань спочатку)

І як ми вже припускали, що крайові ваги позитивні, тобто довжина (x, y)> 0 . Таким чином, альтернативна відстань (alt) через y завжди впевнено більша, тобто dist [y] + довжина (x, y)> dist [x] . Таким чином, значення dist [x] не було б оновлене, навіть якби y розглядалося як шлях до x , тож ми робимо висновок, що має сенс розглядати лише сусідів y, які ще перебувають у Q (зверніть увагу на line16)

Але ця річ залежить від нашого припущення про позитивну довжину ребра, якщо довжина (u, v) <0, то залежно від того, наскільки негативним є цей край, ми можемо замінити dist [x] після порівняння в line18.

Отже, будь-який розрахунок dist [x], який ми робимо, буде невірним, якщо x буде видалено до того, як всі вершини v - такі, що x - сусід v з негативним ребром, що з'єднує їх, - буде видалено.

Оскільки кожен з цих v вершин є другою останньої вершиною на потенціалі «кращого» шлях від джерела до й , який відкидається алгоритмом Дейкстри.

Отже, у прикладі, який я наводив вище, помилка полягала в тому, що C було видалено до видалення B. Поки той С був сусідом Б з негативним краєм!

Просто для уточнення, B і C - сусіди А. B має одного сусіда C, а C не має сусідів. довжина (a, b) - довжина ребра між вершинами a і b.


2
Як ви вже говорили, кращий спосіб вирішити це - використовувати метод heapq.heappush після кожного порівняння. Відсуваємо оновлену відстань у чергу. За цієї умови, Dijkstra може працювати на негативних вагах. Я спробував, і результат вийшов 0,5, -5
нонсенс

1
"джерело шляху до x до u навіть не вважається"; ти мав на увазі джерело для u до x?
slmatrix

1
@slmatrix спасибі за те, що це зрозуміло, так, я мав на увазі, що шлях від джерела до u до x, тому що x - сусід u.
Aditya P

23

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

Це припущення робить алгоритм швидшим, ніж алгоритми, які повинні враховувати негативні ваги.


8

Правильність алгоритму Дейкстри:

У нас є 2 набори вершин на будь-якому кроці алгоритму. Множина A складається з вершин, до яких ми обчислили найкоротші шляхи. Множина B складається з вершин, що залишилися.

Індуктивна гіпотеза : На кожному кроці ми будемо вважати, що всі попередні ітерації є правильними.

Індуктивний крок : Коли ми додамо вершину V до множини A і встановимо відстань, яка має бути віддаленою [V], ми повинні довести, що ця відстань є оптимальною. Якщо це не є оптимальним, до вершини V має бути якийсь менший шлях.

Припустимо, це якийсь інший шлях проходить через деяку вершину X.

Тепер, оскільки dist [V] <= dist [X], тому будь-який інший шлях до V буде найменшою довжиною dist [V], якщо граф не має від'ємної довжини ребра.

Таким чином, для роботи алгоритму диккстри ваги ребер повинні бути негативними.


6

Спробуйте алгоритм Діккстри на наступному графіку, припустивши, що Aце вихідний вузол, щоб побачити, що відбувається:

Графік


6
Вибачте, але я не отримую жодної помилки. Перша A->Bволя 1і A->Cволя 100. Тоді B->Dбуде 2. Тоді C->Dбуде -4900. Значить, значення Dбуде таким -4900же, як і bellman-ford. Як це не дає правильної відповіді?
Анірбан Наг 'tintinmj'

9
@tintinmj Якщо у вас є вихід з D, він буде відвідуватися до того, як відстань D зменшиться, а отже, не буде оновлена ​​після нього. Це призведе до помилки точно. Якщо ви вважаєте D 2 як кінцеву відстань вже після сканування вихідних країв, навіть цей графік призводить до помилки.
Крістіан Шнорр

@ tb- Вибачте за запитання після такого тривалого часу, але я тут на правильному шляху? Спочатку A->Bбуде 1і A->Cбуде 100. Потім Bдосліджується і налаштовується B->Dна 2. Тоді D досліджується, оскільки в даний час він має найкоротший шлях до джерела? Чи був би я правильним, кажучи, що якби B->Dбув 100, Cто був би досліджений першим? Я розумію всі інші приклади, які наводять люди, крім вашого.
Пейман Пох

@PejmanPoh з мого розуміння, якщо B-> D було 100, оскільки A-> C вище в HeapStructure, яка буде використана, витяг min поверне A-> C першим, що означає, що наступним знайденим найкоротшим шляхом буде шлях до C, після цього шлях від C-> D, який має вагу -5000, стане очевидним вибором, що приведе нас до висновку, що найкоротший шлях буде від A-> C-> D, і я впевнений, що це буде бути нормальною поведінкою. Тому іноді, коли у нас є негативні цикли, ми все одно можемо отримати правильне значення для найкоротшого шляху, але, безумовно, не завжди, це приклад, коли ми цього не зробимо ..
Т.Дімітров

1

Нагадаємо, що в алгоритмі Дейкстри, коли вершина позначена як "закрита" (і поза відкритим набором) - вона передбачає, що будь-який вузол, що виходить з неї, призведе до більшої відстані , тому алгоритм знайшов найкоротший шлях до нього, і буде ніколи більше не потрібно розробляти цей вузол, але це не відповідає дійсності у випадку негативних ваг.


0

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

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

Врахуйте це:

A  <- 5 ->  B  <- (-1) ->  C <- 5 -> D

Який оптимальний шлях між A і D?

Будь-який алгоритм накладання маршрутів повинен постійно тримати цикл між B і C, оскільки це зменшить вагу загального шляху. Таким чином, якщо дозволити негативне зважування для з'єднання, буде відображено будь-який спір алгоритму pathfindig, можливо, за винятком випадків, коли ви обмежуєте використання кожного з'єднання лише один раз.


0

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

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

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