Zaawansowane algorytmy i struktury danych/Wykład 5: Różnice pomiędzy wersjami
Linia 126: | Linia 126: | ||
}} | }} | ||
{{dowod|||3= | {{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. | ||
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ż | 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>. | ||
<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>. | |||
Pozostaje nam jedynie zastanowić się co się dzieje gdy wierzchołek | 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ło by, z właściwości procedury [[#algorytm_relaksacja_krawędzi|RELAKSUJ]], że istnieje ścieżka od <math>s</math> do <math>v</math>. Sprzeczność. | ||
<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ło by, z właściwości procedury | |||
że istnieje ścieżka od <math>s</math> do <math>v</math>. Sprzeczność. | |||
}} | }} | ||
{{dowod||| | {{dowod||| | ||
Dowód ten można przeprowadzić w podobny sposób do dowodu lematu | Dowód ten można przeprowadzić w podobny sposób do dowodu lematu [[#poprawnosc_1|Lematu 1]]. | ||
[[#poprawnosc_1|Lematu 1]]. | |||
}} | }} | ||
{{twierdzenie|3|bf_poprawnosc|3= | {{twierdzenie|3|bf_poprawnosc|3= | ||
Niech <math>G = (V,E)</math> będzie | 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 nie zawiera cyklu o ujemnej wadze osiągalnego ze źródła <math>s</math> to algorytm zwraca wartość TRUE, <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>. Jeżeli natomiast <math>G</math> zawiera cykl o ujemnej wadze osiągalny ze źródła to algorytm zwraca wartość FALSE. | ||
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 nie zawiera | |||
cyklu o ujemnej wadze osiągalnego ze źródła <math>s</math> to | |||
algorytm zwraca wartość TRUE, <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>. Jeżeli | |||
natomiast <math>G</math> zawiera cykl o ujemnej wadze osiągalny ze | |||
źródła to algorytm zwraca wartość FALSE. | |||
}} | }} | ||
{{dowod|||3=Załóżmy najpierw, że graf nie zawiera cykli o ujemnej | {{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 [[#poprawność_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]] relaks, która wyliczając odległość wyznaczą jednocześnie przez jaki wierzchołek prowadzi ta najkrótsza ścieżka. | ||
wadze, które byłyby osiągalne z <math>s</math>. Wtedy z | |||
[[#poprawność_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ę | |||
grafie. Wynika to z właściwości [[algorytm_relaksacja_krawędzi| | |||
funkcji | |||
jednocześnie przez jaki wierzchołek prowadzi ta najkrótsza ścieżka. | |||
Musimy teraz pokazać, że algorytm poprawnie wykrywa, czy w grafie | 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: | ||
<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: | |||
Linia 206: | Linia 153: | ||
Powyższa nierówność zachodzi ponieważ <math>s \ | Powyższa nierówność zachodzi ponieważ <math>s \rightsquidarrow u \rightsquidarrow v</math> jest ścieżką w grafie, a więc jest nie krótsza niż najkrótsza ścieżka <math>s \rightdquidarrow 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 TRUE. | ||
\ | |||
krótsza niż najkrótsza ścieżka <math>s \rightdquidarrow 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 TRUE. | |||
Załóżmy teraz, że w grafie <math>G</math> istnieje cykl o ujemnej | 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: | ||
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 | |||
{{wzor|wzor_cykl|(1)| | {{wzor|wzor_cykl|(1)| | ||
Linia 223: | Linia 163: | ||
}} | }} | ||
Gdyby w tej sytuacji algorytm Bellmana-Forda zwrócił wartość TRUE to | Gdyby w tej sytuacji algorytm Bellmana-Forda zwrócił wartość TRUE 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. | ||
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 247: | Linia 183: | ||
Wiemy, że cykl <math>c</math> jest osiągalny a zatem dla każdego | 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: | ||
<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: | |||
Linia 258: | Linia 191: | ||
co stoi w sprzeczności z [[#wzor_cykl|nierównością (1)]]. Jeżeli więc w grafie | 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 FALSE. | ||
istnieje cykl o ujemnej wadze osiągalny z <math>s</math>, to algorytm zwróci FALSE. | |||
}} | }} | ||
== Mnożenie macierzy i najkrótsze ścieżki == | == Mnożenie macierzy i najkrótsze ścieżki == |
Wersja z 17:35, 20 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 .
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 istniej 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 for każdy wierzchołek do
Ustalone przez tą procedure 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 algorytm 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 FALSE 8 return TRUE
Poniższa animacja przedstawia działanie algorytmu dla grafu o pięciu wierzchołkach.
Algorytm ten działa w czasie , co jest łatwo pokazać gdyż:
- proces inicjacji linia 1 zajmuje czas ,
- w każdym z przebiegów głównej pętli w lini 2 algorytmu przeglądane są wszystkie krawędzie grafu w linie 3 , co zajmuje czas ,
- końcowa pętla algorytmu linie 5-7 działa w czasie .
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ło by, 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 Lematu 1.

Twierdzenie 3
Dowód
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ż Parser nie mógł rozpoznać (nieznana funkcja „\rightsquidarrow”): {\displaystyle s \rightsquidarrow u \rightsquidarrow v}
jest ścieżką w grafie, a więc jest nie krótsza niż najkrótsza ścieżka Parser nie mógł rozpoznać (nieznana funkcja „\rightdquidarrow”): {\displaystyle s \rightdquidarrow v}
. Widzimy więc, że w tym przypadku żaden z testów w linijce 6 algorytmu nie będzie spełniony i algorytm zwróci TRUE.
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 zwrócił wartość TRUE 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:
