Zaawansowane algorytmy i struktury danych/Wykład 5: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Sank (dyskusja | edycje)
m Zastępowanie tekstu – „<math> ” na „<math>”
 
(Nie pokazano 19 wersji utworzonych przez 3 użytkowników)
Linia 28: Linia 28:


{{kotwica|problem_sssp|'''W problemie najkrótszych ścieżek z jednego wierzchołka'''}} chcemy obliczyć odległości <math>\delta(s,v)</math> dla wszystkich wierzchołków <math>v\in V</math> wraz z drzewem najkrótszych ścieżek z <math>s</math>. {{kotwica|drzewo_najkrótszych_ścieżek|'''Drzewem najkrótszych ścieżek'''}} o korzeniu w <math>s</math>  nazywamy podgraf skierowany <math>G' = (V', E')</math>, w którym <math>V' \subseteq V</math>, <math>E' \subseteq E</math> taki, że:
{{kotwica|problem_sssp|'''W problemie najkrótszych ścieżek z jednego wierzchołka'''}} chcemy obliczyć odległości <math>\delta(s,v)</math> dla wszystkich wierzchołków <math>v\in V</math> wraz z drzewem najkrótszych ścieżek z <math>s</math>. {{kotwica|drzewo_najkrótszych_ścieżek|'''Drzewem najkrótszych ścieżek'''}} o korzeniu w <math>s</math>  nazywamy podgraf skierowany <math>G' = (V', E')</math>, w którym <math>V' \subseteq V</math>, <math>E' \subseteq E</math> taki, że:
* V' jest zbiorem wierzchołków w <math>G</math> do których istnieje ścieżka z <math>s</math>,
* <math>V'</math> jest zbiorem wierzchołków w <math>G</math> do których istnieje ścieżka z <math>s</math>,
* <math>G'</math> jest drzewem którego korzeniem jest <math>s</math>,
* <math>G'</math> jest drzewem którego korzeniem jest <math>s</math>,
* dla każdego wierzchołka <math>v\in V'</math> jedyna ścieżka z <math>s</math>
* dla każdego wierzchołka <math>v\in V'</math> jedyna ścieżka z <math>s</math> do <math>v</math> w grafie <math>G'</math> jest najkrótszą ścieżką z <math>s</math> do <math>v</math> w grafie <math>G</math>.
do <math>v</math> w grafie <math>G'</math> jest najkrótszą ścieżką z <math>s</math> do <math>v</math> w grafie <math>G</math>.


W naszych algorytmach drzewo najkrótszych ścieżek będziemy reprezentować jako {{kotwica|funkcja_poprzedników|'''funkcję poprzedników'''}} <math>\pi:V \to V</math> określającą poprzednika wierzchołka <math>v</math> w drzewie najkrótszych ścieżek. Drzewo najkrótszych ścieżek <math>G'=(V',E')</math> możemy uzyskać z <math>\pi</math> w następujący sposób:
W naszych algorytmach drzewo najkrótszych ścieżek będziemy reprezentować jako {{kotwica|funkcja_poprzedników|'''funkcję poprzedników'''}} <math>\pi:V \to V</math>, określającą poprzednika wierzchołka <math>v</math> w drzewie najkrótszych ścieżek. Drzewo najkrótszych ścieżek <math>G_{\pi}=(V_{\pi},E_{\pi})</math> możemy uzyskać z <math>\pi</math> w następujący sposób:


{{wzor2|1=
<math>V_{\pi} = \{v \in V : \pi(v) \neq NIL\} \cup \{s\}</math>,
}}


<center><math> V' = \{v \in V : \pi(v) \neq NIL\} \cup \{s\},
{{wzor2|1=<math>
</math></center>
E_{\pi} = \{(\pi(v),v) \in E: v \in V_{\pi} - \{s\}\}.
 
</math>
<center><math>
}}
E' = \{(\pi(v),v) \in E: v \in V_{\pi} - \{s\}\}.
</math></center>
 
== Historia Problemu ==
 
W dalszej części tego wykładu zajmiemy się przedstawieniem algorytmu Bellmana-Forda, który jest jednym z kilku rozwiązań dla problemu obliczania najkrótszych ścieżek z jednego wierzchołka w grafie. Metoda ta jest oparta na dwóch oddzielnych algorytmach podanych przez Bellmana <!--<ref name=Bellman58>R. Bellman, On a Routing Problem, ''Quarterly of Applied Mathematics'', 16(1):87--90, 1958</ref>--> oraz Forda <!--<ref name=Ford56>L.R. Ford Jr, Network Flow Theory, Paper P-923, The RAND Corporation, Santa Monica, California, August 1956.</ref>--> Bellman opisał związek między problemem najkrótszych ścieżek a problemem ograniczeń liniowych (zobacz [[Zaawansowane_algorytmy_i_struktury_danych/ZASD_Ćwiczenia_5#Zadanie_1|Zadania 1]]), natomiast Ford podał jak rozwiązać ten drugi problem. Algorytm Bellmana-Forda jest najszybszym znanym algorytmem w pełni wielomianowym. Istnieje kilka szybszych algorytmów, które działają jednak tylko w przypadku całkowitoliczbowych wag krawędzi, a ich złożoność zależy od maksymalnej wagi krawędzi w grafie. Wszystkie te algorytmy przedstawione są w poniższej tabeli. Co ciekawe mimo, że jest to jeden z najbardziej klasycznych problemów algorytmiki, to jest on nadal żywy i najnowsze z tych algorytmów powstały w ostatnich latach.
 
{| border="1" style="width:500px;"
|+ Znane algorytmy dla problemu obliczania najkrótszych ścieżek z jednego wierzchołka
! Złożoność !! Autor
|-
| <math>O(n^4)</math>
| Shimbel (1995)<!--<ref name=Shimbel55>
A. Shimbel, Structure in Communication Nets, In ''In Proceedings of the Symposium on Information Networks'', pages 199--203. Polytechnic Press of the Polytechnic Institute of Brooklyn, Brooklyn, 1955.
</ref>-->
|-
| <math>O(nm)</math>
| Ford (1956), Bellman (1958)
|-
| <math>O(n^{\frac{3}{4}} m \log W)</math>
| Gabow (1983)
|-
| <math>O(\sqrt{n} m \log (n W))</math>
| Gabow and Tarjan (1989)
|-
| <math>O(\sqrt{n}m \log(W))</math>
| Goldberg (1993)
|-
| <math>O(n^{2.38}W)</math>
| Yuster and Zwick (2005), Sankowski (2005)
|}


== Algorytm Bellmana-Forda ==
== Algorytm Bellmana-Forda ==


'''Algorytm Bellmana-Forda''' służy do rozwiązania problemu znalezienia najkrótszych ścieżek w grafie, w którym wagi krawędzi mogą być ujemne. W problemie tym mamy dany graf <math>G=(V,E)</math> i funkcję wagową <math>w:E \to \mathcal{R}</math>. Algorytm Bellmana-Forda wylicza dla zadanego wierzchołka <math>s</math>, czy istnieje w grafie <math>G</math> cykl o ujemnej wadze osiągalny z <math>s</math>. Jeżeli taki cykl nie istnieje to algorytm oblicza najkrótsze ścieżki z <math>s</math> do wszystkich pozostałych
'''Algorytm Bellmana-Forda''' służy do rozwiązania problemu znalezienia najkrótszych ścieżek w grafie, w którym wagi krawędzi mogą być ujemne. W problemie tym mamy dany graf <math>G=(V,E)</math> i funkcję wagową <math>w:E \to \mathcal{R}</math>. Algorytm Bellmana-Forda wylicza dla zadanego wierzchołka <math>s</math>, czy istnieje w grafie <math>G</math> cykl o ujemnej wadze osiągalny z <math>s</math>. Jeżeli taki cykl nie istnieje, algorytm oblicza najkrótsze ścieżki z <math>s</math> do wszystkich pozostałych
wierzchołków wraz z ich wagami.
wierzchołków wraz z ich wagami.


=== Relaksacja ===
=== Relaksacja ===


Podobnie ja to było w [[algorytm_dijkstry|Algorytmie Dijkstry]] użyjemy metody relaksacji. Metoda ta polega na tym, że w trakcie działania algorytmu dla każdego wierzchołka <math>v \in V</math> utrzymujemy wartość <math>d(v)</math> będącą górnym ograniczeniem wagi najkrótszej ścieżki ze <math>s</math> do <math>v</math>. W algorytmie utrzymywać będziemy także dla każdego wierzchołka <math>v</math> wskaźnik <math>\pi(v)</math> wskazujący na poprzedni wierzchołek przez który prowadzi dotychczas znaleziona najkrótsza ścieżka. Na początku, wielkości te inicjujemy przy pomocy następującej procedury:
Podobnie jak to było w [[Algorytmy i struktury danych/ASD Moduł 11#algorytm_dijkstry|Algorytmie Dijkstry]], użyjemy metody relaksacji. W metodzie tej utrzymujemy dla każdego wierzchołka <math>v \in V</math> wartość <math>d(v)</math>, będącą górnym ograniczeniem wagi najkrótszej ścieżki z <math>s</math> do <math>v</math>. W algorytmie utrzymywać będziemy także dla każdego wierzchołka <math>v</math> wskaźnik <math>\pi(v)</math> wskazujący na poprzedni wierzchołek, przez który prowadzi dotychczas znaleziona najkrótsza ścieżka. Na początku wielkości te inicjujemy przy pomocy następującej procedury:




Linia 94: Linia 65:




Ustalone przez tą procedurę wartości <math>d(v)</math> są dobrymi ograniczeniami górnymi na odległości.
Ustalone przez tą procedurę wartości <math>d(v)</math> są dobrymi ograniczeniami górnymi na odległości w grafie.


{{kotwica|relaksacja|'''Relaksacja'''}} krawędzi <math>(u,v)</math> polega na sprawdzeniu, czy przechodząc krawędzią <math>(u,v)</math> z <math>u</math> do <math>v</math>, nie otrzymamy krótszej ścieżki z <math>s</math> do <math>v </math>niż ta dotychczas znaleziona. Jeżeli tak, to aktualizowane są także wartości <math>d(v)</math> i <math>\pi(v)</math>. W celu relaksacji krawędzi <math>(u,v)</math> używamy procedury RELAKSUJ.
{{kotwica|relaksacja|'''Relaksacja'''}} krawędzi <math>(u,v)</math> polega na sprawdzeniu, czy przechodząc krawędzią <math>(u,v)</math> z <math>u</math> do <math>v</math>, nie otrzymamy krótszej ścieżki z <math>s</math> do <math>v</math> niż ta dotychczas znaleziona. Jeżeli tak, to aktualizowane są także wartości <math>d(v)</math> i <math>\pi(v)</math>. W celu relaksacji krawędzi <math>(u,v)</math> używamy następującej procedury nazwanej tutaj RELAKSUJ.


{{algorytm|Relaksacja krawędzi|algorytm_relaksacja_krawędzi|
{{algorytm|Relaksacja krawędzi|algorytm_relaksacja_krawędzi|
Linia 109: Linia 80:
=== Algorytm ===
=== Algorytm ===


Po przypomnieniu czym była relaksacja gotowi jesteśmy na zapisanie algorytmu Bellmana-Forda, a następnie udowodnienie jego poprawności.
Po przypomnieniu czym była relaksacja, gotowi jesteśmy na zapisanie algorytmu Bellmana-Forda, a następnie udowodnienie jego poprawności.


{{algorytm|Bellmana-Forda|algorytm_Bellmana-Forda|
{{algorytm|Bellmana-Forda|algorytm_Bellmana-Forda|
Linia 119: Linia 90:
   5  '''for''' każda krawędź <math>(u,v) \in E</math> '''do'''
   5  '''for''' każda krawędź <math>(u,v) \in E</math> '''do'''
   6    '''if''' '<math>d(v)>d(u) + w(u,v)</math> '''then'''
   6    '''if''' '<math>d(v)>d(u) + w(u,v)</math> '''then'''
   7      '''return''' NIL
   7      '''return''' <math>NIL</math>
   8  '''return''' (d,\pi)
   8  '''return''' <math>(d,\pi)</math>
}}
}}


Poniższa animacja przedstawia działanie algorytmu dla grafu o pięciu wierzchołkach.
Poniższa animacja przedstawia działanie algorytmu dla grafu o pięciu wierzchołkach. <center><flash>file=Zasd_animacja_bellman_ford1.swf|width=660|height=250</flash></center>
<center>
Algorytm ten działa w czasie <math>O(|V||E|)</math>, co łatwo pokazać, gdyż:
<flash>file=Zasd_animacja_bellman_ford.swf|width=660|height=300</flash>
</center>
Algorytm ten działa w czasie <math>O(|V||E|)</math>, co łatwo pokazać gdyż:
* proces inicjacji w linii 1 zajmuje czas <math>O(|V|)</math>,
* proces inicjacji w linii 1 zajmuje czas <math>O(|V|)</math>,
* w każdym z <math>|V|</math> przebiegów głównej pętli w linii 2 algorytmu przeglądane są wszystkie krawędzie grafu w linii 3 , co zajmuje czas <math>O(|V||E|)</math>,
* w każdym z <math>|V|</math> przebiegów głównej pętli w linii 2 algorytmu przeglądane są wszystkie krawędzie grafu w linii 3 , co zajmuje czas <math>O(|V||E|)</math>,
Linia 137: Linia 105:


{{lemat|1|bf_poprawnosc_1|3=
{{lemat|1|bf_poprawnosc_1|3=
Niech <math>G = (V,E)</math> będzie grafem skierowanym i niech funkcja <math>w:E \to \mathcal{R}</math> zadaje wagi krawędzi. Niech <math>s</math> będzie wierzchołkiem z którego liczymy odległości algorytmem Bellmana-Forda. Jeżeli w grafie nie ma cykli o ujemnej wadze osiągalnych z <math>s</math>, to algorytm poprawnie oblicza odległości, tzn. na koniec działania algorytmu dla każdego <math>v \in V</math> wartość <math>d(v)</math> jest odległością w <math>G</math> z <math>s</math> do <math>v</math>.
Niech <math>G = (V,E)</math> będzie grafem skierowanym i niech funkcja <math>w:E \to \mathcal{R}</math> zadaje wagi krawędzi. Niech <math>s</math> będzie wierzchołkiem, z którego liczymy odległości algorytmem Bellmana-Forda. Jeżeli w grafie nie ma cykli o ujemnej wadze osiągalnych z <math>s</math>, to algorytm poprawnie oblicza odległości, tzn. na koniec działania algorytmu dla każdego <math>v \in V</math> wartość <math>d(v)</math> jest odległością w <math>G</math> z <math>s</math> do <math>v</math>.
}}
}}


{{dowod|||3=Oznaczmy przez <math>\delta(v,u)</math> odległość z wierzchołka <math>v</math> do <math>u</math> w grafie <math>G</math>. Niech <math>v</math> będzie wierzchołkiem osiągalnym ze źródła <math>s</math> i niech <math>p = (v_0, v_1, \ldots, v_k)</math> oznacza najkrótszą ścieżkę z <math>s</math> do <math>v</math>, gdzie <math>v_0 = s</math> oraz <math>v_k = v</math>. Ścieżka ta jest ścieżką prostą, bo najkrótsze ścieżki muszą być proste, więc <math>k\le |V|-1</math>. Pokażemy teraz indukcyjnie, że poczynając od <math>i</math>-tego przebiegu zachodzi <math>d(v_i) = \delta(s,v_i)</math> dla <math>i = 0,1,\ldots, k</math>. W algorytmie wykonujemy <math>|V|-1</math> obrotów pętli oraz <math>k \le |V|-1</math>, co oznacza, że z tej tezy indukcyjnej wynika poprawność algorytmu.
{{dowod|||3=Oznaczmy przez <math>\delta(v,u)</math> odległość z wierzchołka <math>v</math> do <math>u</math> w grafie <math>G</math>. Niech <math>v</math> będzie wierzchołkiem osiągalnym ze źródła <math>s</math> i niech <math>p = (v_0, v_1, \ldots, v_k)</math> oznacza najkrótszą ścieżkę z <math>s</math> do <math>v</math>, gdzie <math>v_0 = s</math> oraz <math>v_k = v</math>. Ścieżka ta jest ścieżką prostą, bo najkrótsze ścieżki muszą być proste, więc <math>k\le |V|-1</math>. Pokażemy teraz indukcyjnie, że poczynając od <math>i</math>-tego przebiegu zachodzi <math>d(v_i) = \delta(s,v_i)</math> dla <math>i = 0,1,\ldots, k</math>. W algorytmie wykonujemy <math>|V|-1</math> obrotów pętli oraz <math>k \le |V|-1</math>, co oznacza, że z tej tezy indukcyjnej wynika poprawność algorytmu.


Zauważmy, że teza indukcyjna zachodzi po inicjacji algorytmu, gdyż <math>d(v_0) = d(s) = 0 \delta(s,s) = \delta(s,v_0).</math> Załóżmy, że teza indukcyjna zachodzi dla kroku <math>k</math>'tego. Ponieważ ścieżki <math>p = (v_0, v_1, \ldots, v_i)</math> dla <math>i \le k</math> są najkrótsze jako podścieżki ścieżki <math>p</math>, to po <math>k+1</math> wykonaniu pętli wartości <math>d(v_i)</math> dla <math>i \le k</math> się nie zmienią. Pozostaje nam więc do pokazania to, że wartość <math>d(v_{k+1})</math> będzie dobrze policzona. W <math>k+1</math> przebiegu wykonujemy między innymi relaksację krawędzi <math>(v_{k}, v_{k+1})</math>. Ponieważ <math>d(v_k)</math> jest dobrze policzone, to po tej relaksacji wyznaczona będzie także poprawnie wartość <math>d(v_{k+1})</math>, bo założyliśmy, że najkrótsza ścieżka do <math>v_{k+1}</math> przechodzi przez <math>v_k</math>.
Zauważmy, że teza indukcyjna zachodzi po inicjacji algorytmu, gdyż <math>d(v_0) = d(s) = 0</math> i <math>\delta(s,s) = \delta(s,v_0)</math>. Załóżmy, że teza indukcyjna zachodzi dla kroku <math>k</math>-tego. Ponieważ ścieżki <math>p = (v_0, v_1, \ldots, v_i)</math> dla <math>i \le k</math> są najkrótsze jako podścieżki ścieżki <math>p</math>, to po <math>k+1</math> wykonaniu pętli wartości <math>d(v_i)</math> dla <math>i \le k</math> się nie zmienią. Pozostaje nam więc do pokazania to, że wartość <math>d(v_{k+1})</math> będzie dobrze policzona. W <math>k+1</math> przebiegu wykonujemy między innymi relaksację krawędzi <math>(v_{k}, v_{k+1})</math>. Ponieważ <math>d(v_k)</math> jest dobrze policzone, po tej relaksacji wyznaczona będzie także poprawnie wartość <math>d(v_{k+1})</math>, bo założyliśmy, że najkrótsza ścieżka do <math>v_{k+1}</math> przechodzi przez <math>v_k</math>.


Pozostaje nam jedynie zastanowić się co się dzieje gdy wierzchołek <math>v</math> nie jest osiągalny z <math>s</math>. Musi wtedy zachodzić <math>d(v) = \infty</math> pod koniec działania algorytmu. Gdyby tak nie było to oznaczałoby, z właściwości procedury [[#algorytm_relaksacja_krawędzi|RELAKSUJ]], że istnieje ścieżka od <math>s</math> do <math>v</math>. Sprzeczność.
Pozostaje nam jedynie zastanowić się, co się dzieje, gdy wierzchołek <math>v</math> nie jest osiągalny z <math>s</math>. Musi wtedy zachodzić <math>d(v) = \infty</math> pod koniec działania algorytmu. Gdyby tak nie było, oznaczałoby to, z właściwości procedury [[#algorytm_relaksacja_krawędzi|RELAKSUJ]], że istnieje ścieżka od <math>s</math> do <math>v</math>, co daje sprzeczność.
}}
}}


 
{{twierdzenie|2|bf_poprawnosc|3=
{{dowod|||
Niech <math>G = (V,E)</math> będzie grafem skierowanym i niech funkcja <math>w:E \to \mathcal{R}</math> zadaje wagi krawędzi. Załóżmy, że algorytm Bellmana-Forda został wykonany dla wierzchołka <math>s</math>. Jeżeli graf zawiera cykl o ujemnej wadze osiągalny ze źródła <math>s</math>, to algorytm zwraca wartość NIL, w przeciwnym wypadku <math>d(v)</math> jest odległością z <math>s</math> do <math>v</math>, a <math>\pi(v)</math> wyznacza drzewo najkrótszych ścieżek o korzeniu w <math>s</math>.
Dowód ten można przeprowadzić w podobny sposób do dowodu [[#bf_poprawnosc_1|Lematu 1]].
}}
 
{{twierdzenie|3|bf_poprawnosc|3=
Niech <math>G = (V,E)</math> będzie grafem skierowanym i niech funkcja <math>w:E \to \mathcal{R}</math> zadaje wagi krawędzi. Załóżmy, że algorytm Bellmana-Forda został wykonany dla wierzchołka <math>s</math>. Jeżeli graf zawiera cyklu o ujemnej wadze osiągalny ze źródła <math>s</math> to algorytm zwraca wartość NIL, w przeciwnym wypadku <math>d(v)</math> jest odległością z <math>s</math> do <math>v</math>, a <math>\pi(v)</math> wyznacza drzewo najkrótszych ścieżek o korzeniu w <math>s</math>.
}}
}}




{{dowod|||3=Załóżmy najpierw, że graf nie zawiera cykli o ujemnej wadze, które byłyby osiągalne z <math>s</math>. Wtedy z [[#bf_poprawnosc_1|Lematu 1]] wiemy, że <math>d(v)</math> są poprawnie policzonymi odległościami. Jeżeli odległości <math>d(v)</math> zostały poprawnie policzone przez [[#algorytm_relaksacja_krawędzi| funkcję RELAKSUJ]] to <math>\pi(v)</math> koduje najkrótsze ścieżki w grafie. Wynika to z właściwości [[#algorytm_relaksacja_krawędzi|funkcji RELAKSUJ], która wyliczając odległość wyznaczą jednocześnie przez jaki wierzchołek prowadzi ta najkrótsza ścieżka.
{{dowod|||3=Załóżmy najpierw, że graf nie zawiera cykli o ujemnej wadze, które byłyby osiągalne z <math>s</math>. Wtedy z [[#bf_poprawnosc_1|Lematu 1]] wiemy, że <math>d(v)</math> są poprawnie policzonymi odległościami. Jeżeli odległości <math>d(v)</math> zostały poprawnie policzone przez [[#algorytm_relaksacja_krawędzi| funkcję RELAKSUJ]], to <math>\pi(v)</math> koduje najkrótsze ścieżki w grafie. Wynika to z właściwości [[#algorytm_relaksacja_krawędzi|funkcji RELAKSUJ]], która wyliczając odległość wyznacza jednocześnie, przez jaki wierzchołek prowadzi ta najkrótsza ścieżka.


Musimy teraz pokazać, że algorytm poprawnie wykrywa, czy w grafie <math>G</math> istnieje cykl ujemnej długości osiągalny z <math>s</math>. Jeżeli nie ma takiego cyklu to wtedy <math>d(v)</math> są poprawnie policzone przed wykonaniem testu w liniach 5-8 [[#algorytm_Bellmana-Forda|algorytmu Bellmana-Forda]]. W takim razie zachodzi:
Musimy teraz pokazać, że algorytm poprawnie wykrywa, czy w grafie <math>G</math> istnieje cykl ujemnej długości osiągalny z <math>s</math>. Jeżeli nie ma takiego cyklu, wtedy <math>d(v)</math> są poprawnie policzone przed wykonaniem testu w liniach 5-8 [[#algorytm_Bellmana-Forda|algorytmu Bellmana-Forda]]. W takim razie zachodzi:




Linia 167: Linia 130:




Powyższa nierówność zachodzi ponieważ <math>s \to u \to v</math> jest ścieżką w grafie, a więc jest nie krótsza niż najkrótsza ścieżka <math>s \to v</math>. Widzimy więc, że w tym przypadku żaden z testów w linijce 6 algorytmu nie będzie spełniony i algorytm nie zwróci NIL.
Powyższa nierówność zachodzi, ponieważ <math>s \to u \to v</math> jest ścieżką w grafie, a więc jest nie krótsza niż najkrótsza ścieżka <math>s \to v</math>. Widzimy więc, że w tym przypadku żaden z testów w linijce 6 algorytmu nie będzie spełniony i algorytm nie zwróci <math>NIL</math>.


Załóżmy teraz, że w grafie <math>G</math> istnieje cykl  o ujemnej wadze osiągalny z <math>s</math>. Oznaczmy ten cykl jako <math>c =(v_0, v_1,\ldots, v_k)</math>, gdzie <math>v_0 = v_k</math>. Dla cyklu tego mamy:
Załóżmy teraz, że w grafie <math>G</math> istnieje cykl  o ujemnej wadze osiągalny z <math>s</math>. Oznaczmy ten cykl jako <math>c =(v_0, v_1,\ldots, v_k)</math>, gdzie <math>v_0 = v_k</math>. Dla cyklu tego mamy:
Linia 173: Linia 136:
{{wzor|wzor_cykl|1|
{{wzor|wzor_cykl|1|
<math>
<math>
\sum_{i=0}^{k} w(v_{i},v_{i+1}) <0.
\sum_{i=0}^{k-1} w(v_{i},v_{i+1}) <0.
</math>}}
</math>}}


Gdyby w tej sytuacji [[#algorytm Bellmana-Forda| algorytm Bellmana-Forda]] nie zwrócił wartości NIL to dla każdej krawędzi <math>(v_i, v_{i+1})</math> musiałaby zachodzić nierówność <math>d(v_{i}) + w(v_i, v_{i+1}) \ge d(v_{i+1}) </math>. Sumując nierówność stronami po wszystkich <math>i = 0,\ldots, k-1</math> otrzymujemy.
Gdyby w tej sytuacji [[#algorytm Bellmana-Forda| algorytm Bellmana-Forda]] nie zwrócił wartości <math>NIL</math>, to dla każdej krawędzi <math>(v_i, v_{i+1})</math> musiałaby zachodzić nierówność <math>d(v_{i}) + w(v_i, v_{i+1}) \ge d(v_{i+1})</math>. Sumując nierówność stronami po wszystkich <math>i = 0,\ldots, k-1</math>, otrzymujemy:




{{wzor2|1=<math>
{{wzor2|1=<math>
\sum_{i=0}^{k-1} \left[d(v_i) + w(v_i, v_{i+1})\right] \ge \sum_{i=0}^{k-1} d(v_{i+1}),
\sum_{i=0}^{k-1} \left[d(v_i) + w(v_i, v_{i+1})\right] \ge \sum_{i=0}^{k-1} d(v_{i+1})</math>,}}
</math>}}


{{wzor2|1=<math>
{{wzor2|1=<math>
\sum_{i=0}^{k-1} d(v_i) + \sum_{i=0}^{k-1} w(v_i, v_{i+1}) \ge \sum_{i=1}^{k} d(v_{i}),
\sum_{i=0}^{k-1} d(v_i) + \sum_{i=0}^{k-1} w(v_i, v_{i+1}) \ge \sum_{i=1}^{k} d(v_{i})</math>,}}
</math>}}




Linia 196: Linia 157:




Wiemy, że cykl <math>c</math> jest osiągalny a zatem dla każdego <math>i = 0,\ldots,k</math> mamy <math>d(v)<\infty</math>. Możemy więc skrócić <math>\sum_{i=0}^{k-1} d(v_i)</math> po obydwu stronach równania i otrzymujemy:
Wiemy, że cykl <math>c</math> jest osiągalny z <math>s</math>, a zatem dla każdego <math>i = 0,\ldots,k</math> mamy <math>d(v)<\infty</math>. Możemy więc skrócić <math>\sum_{i=0}^{k-1} d(v_i)</math> po obydwu stronach nierówności otrzymując:




{{wzor2|1=<math>
{{wzor2|1=<math>
\sum_{i=0}^{k-1} w(v_i, v_{i+1}) \ge 0,
\sum_{i=0}^{k-1} w(v_i, v_{i+1}) \ge 0</math>,}}
</math>}}




co stoi w sprzeczności z [[#wzor_cykl|nierównością (1)]]. Jeżeli więc w grafie istnieje cykl o ujemnej wadze osiągalny z <math>s</math>, to algorytm zwróci NIL.
co stoi w sprzeczności z [[#wzor_cykl|nierównością (1)]]. Jeżeli więc w grafie istnieje cykl o ujemnej wadze osiągalny z <math>s</math>, to algorytm zwróci <math>NIL</math>.
}}
}}

Aktualna wersja na dzień 22:16, 11 wrz 2023

Abstrakt

Pierwsza część tego wykładu poświęcona będzie problemowi obliczania najkrótszych ścieżek w grafie z jednego źródła w przypadku, w którym wagi krawędzi mogą być ujemne. Zaprezentujemy algorytm Bellmana-Forda, który rozwiązuje ten problem w czasie O(|V||E|). W drugiej części zajmiemy się problemem obliczania odległości między wszystkimi parami wierzchołków. Pokażemy związki tego problemu z mnożeniem macierzy.

Definicja problemu

W wykładzie tym zajmiemy się problemem obliczania najkrótszych ścieżek w grafie wychodzących z jednego wierzchołka. Załóżmy, że mamy dany graf G=(V,E), funkcję w:E przypisującą wagi krawędziom oraz jeden wybrany wierzchołek s. Wagę ścieżki p=(v0,v1,,vk) definiujemy jako wagę tworzących ją krawędzi:


w(p)=i=0k1w(vi,vi+1).


Odległość z wierzchołka u do wierzchołka v definiujemy jako

δ(u,v)={min{w(p):p ścieżka z u do v},jeżeli istnieje ścieżka z u do v,w przeciwnym przypadku.

Najkrótszą ścieżką z wierzchołka u do wierzchołka v jest każda ścieżka p z u do v, której waga w(p) jest równa odległości δ(u,v) z u do v.

W problemie najkrótszych ścieżek z jednego wierzchołka chcemy obliczyć odległości δ(s,v) dla wszystkich wierzchołków vV wraz z drzewem najkrótszych ścieżek z s. Drzewem najkrótszych ścieżek o korzeniu w s nazywamy podgraf skierowany G=(V,E), w którym VV, EE taki, że:

  • V jest zbiorem wierzchołków w G do których istnieje ścieżka z s,
  • G jest drzewem którego korzeniem jest s,
  • dla każdego wierzchołka vV jedyna ścieżka z s do v w grafie G jest najkrótszą ścieżką z s do v w grafie G.

W naszych algorytmach drzewo najkrótszych ścieżek będziemy reprezentować jako funkcję poprzedników π:VV, określającą poprzednika wierzchołka v w drzewie najkrótszych ścieżek. Drzewo najkrótszych ścieżek Gπ=(Vπ,Eπ) możemy uzyskać z π w następujący sposób:

Vπ={vV:π(v)NIL}{s},
Eπ={(π(v),v)E:vVπ{s}}.

Algorytm Bellmana-Forda

Algorytm Bellmana-Forda służy do rozwiązania problemu znalezienia najkrótszych ścieżek w grafie, w którym wagi krawędzi mogą być ujemne. W problemie tym mamy dany graf G=(V,E) i funkcję wagową w:E. Algorytm Bellmana-Forda wylicza dla zadanego wierzchołka s, czy istnieje w grafie G cykl o ujemnej wadze osiągalny z s. Jeżeli taki cykl nie istnieje, algorytm oblicza najkrótsze ścieżki z s do wszystkich pozostałych wierzchołków wraz z ich wagami.

Relaksacja

Podobnie jak to było w Algorytmie Dijkstry, użyjemy metody relaksacji. W metodzie tej utrzymujemy dla każdego wierzchołka vV wartość d(v), będącą górnym ograniczeniem wagi najkrótszej ścieżki z s do v. W algorytmie utrzymywać będziemy także dla każdego wierzchołka v wskaźnik π(v) wskazujący na poprzedni wierzchołek, przez który prowadzi dotychczas znaleziona najkrótsza ścieżka. Na początku wielkości te inicjujemy przy pomocy następującej procedury:


Algorytm Inicjacja algorytmu najkrótszych ścieżek


 INICJACJA(G,s)
 1  for każdy wierzchołek vV do
 2  begin
 3    d(v)=
 4    π(v)=NIL
 5  end
 6  d(s)=0
 7  return (d,π)


Ustalone przez tą procedurę wartości d(v) są dobrymi ograniczeniami górnymi na odległości w grafie.

Relaksacja krawędzi (u,v) polega na sprawdzeniu, czy przechodząc krawędzią (u,v) z u do v, nie otrzymamy krótszej ścieżki z s do v niż ta dotychczas znaleziona. Jeżeli tak, to aktualizowane są także wartości d(v) i π(v). W celu relaksacji krawędzi (u,v) używamy następującej procedury nazwanej tutaj RELAKSUJ.

Algorytm Relaksacja krawędzi


 RELAKSUJ(u,v,w,d,π)
 1  if d(v)>d(u)+w(u,v) then
 3  begin
 4    d(v)=d(u)+w(u,v)
 5    π(v)=u
 6  end

Algorytm

Po przypomnieniu czym była relaksacja, gotowi jesteśmy na zapisanie algorytmu Bellmana-Forda, a następnie udowodnienie jego poprawności.

Algorytm Bellmana-Forda


 BELLMAN-FORD(G,w,s)
 1  (d,π)=INICJUJ(G,s)
 2  for i=1 to |V|1 do
 3    for każda krawędź (u,v)E do
 4      RELAKSUJ(u,v,w,d,π)
 5  for każda krawędź (u,v)E do
 6    if 'd(v)>d(u)+w(u,v) then
 7      return NIL
 8  return (d,π)

Poniższa animacja przedstawia działanie algorytmu dla grafu o pięciu wierzchołkach.

<flash>file=Zasd_animacja_bellman_ford1.swf|width=660|height=250</flash>

Algorytm ten działa w czasie O(|V||E|), co łatwo pokazać, gdyż:

  • proces inicjacji w linii 1 zajmuje czas O(|V|),
  • w każdym z |V| przebiegów głównej pętli w linii 2 algorytmu przeglądane są wszystkie krawędzie grafu w linii 3 , co zajmuje czas O(|V||E|),
  • końcowa pętla algorytmu w liniach 5-7 działa w czasie O(|E|).

Poprawność

Dowód poprawności algorytmu Bellmana-Forda zaczniemy od pokazania, że algorytm działa poprawnie przy założeniu, że w grafie nie ma cykli o ujemnych wagach.

Lemat 1

Niech G=(V,E) będzie grafem skierowanym i niech funkcja w:E zadaje wagi krawędzi. Niech s będzie wierzchołkiem, z którego liczymy odległości algorytmem Bellmana-Forda. Jeżeli w grafie nie ma cykli o ujemnej wadze osiągalnych z s, to algorytm poprawnie oblicza odległości, tzn. na koniec działania algorytmu dla każdego vV wartość d(v) jest odległością w G z s do v.

Dowód

Oznaczmy przez δ(v,u) odległość z wierzchołka v do u w grafie G. Niech v będzie wierzchołkiem osiągalnym ze źródła s i niech p=(v0,v1,,vk) oznacza najkrótszą ścieżkę z s do v, gdzie v0=s oraz vk=v. Ścieżka ta jest ścieżką prostą, bo najkrótsze ścieżki muszą być proste, więc k|V|1. Pokażemy teraz indukcyjnie, że poczynając od i-tego przebiegu zachodzi d(vi)=δ(s,vi) dla i=0,1,,k. W algorytmie wykonujemy |V|1 obrotów pętli oraz k|V|1, co oznacza, że z tej tezy indukcyjnej wynika poprawność algorytmu.

Zauważmy, że teza indukcyjna zachodzi po inicjacji algorytmu, gdyż d(v0)=d(s)=0 i δ(s,s)=δ(s,v0). Załóżmy, że teza indukcyjna zachodzi dla kroku k-tego. Ponieważ ścieżki p=(v0,v1,,vi) dla ik są najkrótsze jako podścieżki ścieżki p, to po k+1 wykonaniu pętli wartości d(vi) dla ik się nie zmienią. Pozostaje nam więc do pokazania to, że wartość d(vk+1) będzie dobrze policzona. W k+1 przebiegu wykonujemy między innymi relaksację krawędzi (vk,vk+1). Ponieważ d(vk) jest dobrze policzone, po tej relaksacji wyznaczona będzie także poprawnie wartość d(vk+1), bo założyliśmy, że najkrótsza ścieżka do vk+1 przechodzi przez vk.

Pozostaje nam jedynie zastanowić się, co się dzieje, gdy wierzchołek v nie jest osiągalny z s. Musi wtedy zachodzić d(v)= pod koniec działania algorytmu. Gdyby tak nie było, oznaczałoby to, z właściwości procedury RELAKSUJ, że istnieje ścieżka od s do v, co daje sprzeczność.

Twierdzenie 2

Niech G=(V,E) będzie grafem skierowanym i niech funkcja w:E zadaje wagi krawędzi. Załóżmy, że algorytm Bellmana-Forda został wykonany dla wierzchołka s. Jeżeli graf zawiera cykl o ujemnej wadze osiągalny ze źródła s, to algorytm zwraca wartość NIL, w przeciwnym wypadku d(v) jest odległością z s do v, a π(v) wyznacza drzewo najkrótszych ścieżek o korzeniu w s.


Dowód

Załóżmy najpierw, że graf nie zawiera cykli o ujemnej wadze, które byłyby osiągalne z s. Wtedy z Lematu 1 wiemy, że d(v) są poprawnie policzonymi odległościami. Jeżeli odległości d(v) zostały poprawnie policzone przez funkcję RELAKSUJ, to π(v) koduje najkrótsze ścieżki w grafie. Wynika to z właściwości funkcji RELAKSUJ, która wyliczając odległość wyznacza jednocześnie, przez jaki wierzchołek prowadzi ta najkrótsza ścieżka.

Musimy teraz pokazać, że algorytm poprawnie wykrywa, czy w grafie G istnieje cykl ujemnej długości osiągalny z s. Jeżeli nie ma takiego cyklu, wtedy d(v) są poprawnie policzone przed wykonaniem testu w liniach 5-8 algorytmu Bellmana-Forda. W takim razie zachodzi:


d(v)=δ(s,v)δ(s,u)+w(u,v)=d(u)+w(u,v).


Powyższa nierówność zachodzi, ponieważ suv jest ścieżką w grafie, a więc jest nie krótsza niż najkrótsza ścieżka sv. Widzimy więc, że w tym przypadku żaden z testów w linijce 6 algorytmu nie będzie spełniony i algorytm nie zwróci NIL.

Załóżmy teraz, że w grafie G istnieje cykl o ujemnej wadze osiągalny z s. Oznaczmy ten cykl jako c=(v0,v1,,vk), gdzie v0=vk. Dla cyklu tego mamy:

i=0k1w(vi,vi+1)<0.      (1)

Gdyby w tej sytuacji algorytm Bellmana-Forda nie zwrócił wartości NIL, to dla każdej krawędzi (vi,vi+1) musiałaby zachodzić nierówność d(vi)+w(vi,vi+1)d(vi+1). Sumując tę nierówność stronami po wszystkich i=0,,k1, otrzymujemy:


i=0k1[d(vi)+w(vi,vi+1)]i=0k1d(vi+1),
i=0k1d(vi)+i=0k1w(vi,vi+1)i=1kd(vi),


ponieważ v0=vk to


i=0k1d(vi)+i=0k1w(vi,vi+1)i=0k1d(vi).


Wiemy, że cykl c jest osiągalny z s, a zatem dla każdego i=0,,k mamy d(v)<. Możemy więc skrócić i=0k1d(vi) po obydwu stronach nierówności otrzymując:


i=0k1w(vi,vi+1)0,


co stoi w sprzeczności z nierównością (1). Jeżeli więc w grafie istnieje cykl o ujemnej wadze osiągalny z s, to algorytm zwróci NIL.