Zaawansowane algorytmy i struktury danych/Wykład 5: Różnice pomiędzy wersjami
Nie podano opisu zmian |
|||
Linia 84: | Linia 84: | ||
{{algorytm|Inicjacja algorytmu najkrótszych ścieżek|algorytm_inicjacja_sssp| | {{algorytm|Inicjacja algorytmu najkrótszych ścieżek|algorytm_inicjacja_sssp| | ||
INICJACJA<math>(G,s)</math> | INICJACJA<math>(G,s)</math> | ||
1 '''for''' każdy wierzchołek <math>v\in V</math> '''do''' | |||
2 <math>d(v) = \infty</math> | |||
3 <math>\pi(v) = NIL</math> | |||
4 <math>d(s) = 0</math> | |||
5 '''return''' <math>(d,\pi)</math> | |||
}} | }} | ||
Linia 96: | Linia 97: | ||
{{algorytm|Relaksacja krawędzi|algorytm_relaksacja_krawędzi| | {{algorytm|Relaksacja krawędzi|algorytm_relaksacja_krawędzi| | ||
RELAKSUJ<math>(u,v,w)</math> | RELAKSUJ<math>(u,v,w,d,\pi)</math> | ||
'''if''' <math>d(v) > d(u) + w(u,v)</math> '''then''' | '''if''' <math>d(v) > d(u) + w(u,v)</math> '''then''' | ||
<math>d(v) = d(u) + w(u,v)</math> | <math>d(v) = d(u) + w(u,v)</math> | ||
Linia 108: | Linia 109: | ||
{{algorytm|Bellmana-Forda|algorytm_Bellmana-Forda| | {{algorytm|Bellmana-Forda|algorytm_Bellmana-Forda| | ||
BELLMAN-FORD<math>(G,w,s)</math> | BELLMAN-FORD<math>(G,w,s)</math> | ||
1 [[#algorytm_inicjacja_sssp|INICJUJ]]<math>(G,s)</math> | 1 <math>(d,\pi)=</math>[[#algorytm_inicjacja_sssp|INICJUJ]]<math>(G,s)</math> | ||
2 '''for''' <math>i=1</math> '''to''' <math>|V|-1</math> '''do''' | 2 '''for''' <math>i=1</math> '''to''' <math>|V|-1</math> '''do''' | ||
3 '''for''' każda krawędź <math>(u,v) \in E</math> '''do''' | 3 '''for''' każda krawędź <math>(u,v) \in E</math> '''do''' | ||
4 [[#algorytm_relaksacja_krawędzi|RELAKSUJ]]<math>(u,v,w)</math> | 4 [[#algorytm_relaksacja_krawędzi|RELAKSUJ]]<math>(u,v,w,d,\pi)</math> | ||
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''' | 7 '''return''' NIL | ||
8 '''return''' | 8 '''return''' (d,\pi) | ||
}} | }} | ||
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. | ||
Algorytm ten działa w czasie <math>O(|V||E|)</math>, co łatwo pokazać gdyż: | 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 146: | Linia 147: | ||
{{twierdzenie|3|bf_poprawnosc|3= | {{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 | 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>. | ||
}} | }} | ||
Linia 160: | Linia 161: | ||
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 zwróci | 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. | ||
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 169: | Linia 170: | ||
</math>}} | </math>}} | ||
Gdyby w tej sytuacji [[#algorytm Bellmana-Forda| algorytm Bellmana-Forda]] zwrócił | 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 tą nierówność stronami po wszystkich <math>i = 0,\ldots, k-1</math> otrzymujemy. | ||
Linia 197: | Linia 198: | ||
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 | 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. | ||
}} | }} |
Wersja z 19:07, 23 lip 2006
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 . 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 , funkcję przypisującą wagi krawędziom oraz jeden wybrany wierzchołek . Wagę ścieżki definiujemy jako wagę tworzących ją krawędzi:
Odległość z wierzchołka do wierzchołka definiujemy jako
Najkrótszą ścieżką z wierzchołka do wierzchołka jest każda ścieżka z do , której waga jest równa odległości z do .
W problemie najkrótszych ścieżek z jednego wierzchołka chcemy obliczyć odległości dla wszystkich wierzchołków wraz z drzewem najkrótszych ścieżek z . Drzewem najkrótszych ścieżek o korzeniu w nazywamy podgraf skierowany , w którym , taki, że:
* V' jest zbiorem wierzchołków w do których istnieje ścieżka z , * jest drzewem którego korzeniem jest , * dla każdego wierzchołka jedyna ścieżka z
do w grafie jest najkrótszą ścieżką z do w grafie .
W naszych algorytmach drzewo najkrótszych ścieżek będziemy reprezentować jako funkcję poprzedników określającą poprzednika wierzchołka w drzewie najkrótszych ścieżek. Drzewo najkrótszych ścieżek możemy uzyskać z w następujący sposób:
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 oraz Forda Bellman opisał związek między problemem najkrótszych ścieżek a problemem ograniczeń liniowych (zobacz 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.
Złożoność | Autor |
---|---|
Shimbel (1995) | |
Ford (1956), Bellman (1958) | |
Gabow (1983) | |
Gabow and Tarjan (1989) | |
Goldberg (1993) | |
Yuster and Zwick (2005), Sankowski (2005) |
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 i funkcję wagową . Algorytm Bellmana-Forda wylicza dla zadanego wierzchołka , czy istnieje w grafie cykl o ujemnej wadze osiągalny z . Jeżeli taki cykl nie istnieje to algorytm oblicza najkrótsze ścieżki z do wszystkich pozostałych wierzchołków wraz z ich wagami.
Relaksacja
Podobnie ja to było w Algorytmie Dijkstry użyjemy metody relaksacji. Metoda ta polega na tym, że w trakcie działania algorytmu dla każdego wierzchołka utrzymujemy wartość będącą górnym ograniczeniem wagi najkrótszej ścieżki ze do . W algorytmie utrzymywać będziemy także dla każdego wierzchołka wskaźnik 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 1 for każdy wierzchołek do 2 3 4 5 return
Ustalone przez tą procedurę wartości są dobrymi ograniczeniami górnymi na odległości.
Relaksacja krawędzi polega na sprawdzeniu, czy przechodząc krawędzią z do , nie otrzymamy krótszej ścieżki z do niż ta dotychczas znaleziona. Jeżeli tak, to aktualizowane są także wartości i . W celu relaksacji krawędzi używamy procedury RELAKSUJ.
Algorytm Relaksacja krawędzi
RELAKSUJ if then
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 1 INICJUJ 2 for to do 3 for każda krawędź do 4 RELAKSUJ 5 for każda krawędź do 6 if ' then 7 return NIL 8 return (d,\pi)
Poniższa animacja przedstawia działanie algorytmu dla grafu o pięciu wierzchołkach.
Algorytm ten działa w czasie , co łatwo pokazać gdyż:
- proces inicjacji w linii 1 zajmuje czas ,
- w każdym z przebiegów głównej pętli w linii 2 algorytmu przeglądane są wszystkie krawędzie grafu w linii 3 , co zajmuje czas ,
- końcowa pętla algorytmu w liniach 5-7 działa w czasie .
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
Dowód
Zauważmy, że teza indukcyjna zachodzi po inicjacji algorytmu, gdyż Załóżmy, że teza indukcyjna zachodzi dla kroku 'tego. Ponieważ ścieżki dla są najkrótsze jako podścieżki ścieżki , to po wykonaniu pętli wartości dla się nie zmienią. Pozostaje nam więc do pokazania to, że wartość będzie dobrze policzona. W przebiegu wykonujemy między innymi relaksację krawędzi . Ponieważ jest dobrze policzone, to po tej relaksacji wyznaczona będzie także poprawnie wartość , bo założyliśmy, że najkrótsza ścieżka do przechodzi przez .
Pozostaje nam jedynie zastanowić się co się dzieje gdy wierzchołek nie jest osiągalny z . Musi wtedy zachodzić pod koniec działania algorytmu. Gdyby tak nie było to oznaczałoby, z właściwości procedury RELAKSUJ, że istnieje ścieżka od do . Sprzeczność.
Dowód
Dowód ten można przeprowadzić w podobny sposób do dowodu Lematu 1.

Twierdzenie 3
{{dowod|||3=Załóżmy najpierw, że graf nie zawiera cykli o ujemnej wadze, które byłyby osiągalne z . Wtedy z Lematu 1 wiemy, że są poprawnie policzonymi odległościami. Jeżeli odległości zostały poprawnie policzone przez funkcję RELAKSUJ to 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.
Musimy teraz pokazać, że algorytm poprawnie wykrywa, czy w grafie istnieje cykl ujemnej długości osiągalny z . Jeżeli nie ma takiego cyklu to wtedy są poprawnie policzone przed wykonaniem testu w liniach 5-8 algorytmu Bellmana-Forda. W takim razie zachodzi:
Powyższa nierówność zachodzi ponieważ jest ścieżką w grafie, a więc jest nie krótsza niż najkrótsza ścieżka . 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 istnieje cykl o ujemnej wadze osiągalny z . Oznaczmy ten cykl jako , gdzie . Dla cyklu tego mamy:
(1)
Gdyby w tej sytuacji algorytm Bellmana-Forda nie zwrócił wartości NIL to dla każdej krawędzi musiałaby zachodzić nierówność . Sumując tą nierówność stronami po wszystkich otrzymujemy.
ponieważ to
Wiemy, że cykl jest osiągalny a zatem dla każdego mamy . Możemy więc skrócić po obydwu stronach równania i otrzymujemy:
co stoi w sprzeczności z nierównością (1). Jeżeli więc w grafie istnieje cykl o ujemnej wadze osiągalny z , to algorytm zwróci NIL.
}}