Zaawansowane algorytmy i struktury danych/Wykład 6: Różnice pomiędzy wersjami
m Zastępowanie tekstu – „<math> ” na „<math>” |
|||
(Nie pokazano 9 wersji utworzonych przez 2 użytkowników) | |||
Linia 13: | Linia 13: | ||
W rozdziale tym będziemy zakładać, że algorytmy na wejściu otrzymują {{kotwica|macierz_wag|''macierz wag''}} <math>W</math> rozmiaru <math>n\times n</math> reprezentującą wagi krawędzi <math>n</math>-wierzchołkowego grafu <math>G = (V,E)</math>. Dla macierzy tej zachodzi: | W rozdziale tym będziemy zakładać, że algorytmy na wejściu otrzymują {{kotwica|macierz_wag|''macierz wag''}} <math>W</math> rozmiaru <math>n\times n</math> reprezentującą wagi krawędzi <math>n</math>-wierzchołkowego grafu <math>G = (V,E)</math>. Dla macierzy tej zachodzi: | ||
{{wzor2|1=<math> | {{wzor2|1=<math> | ||
Linia 57: | Linia 57: | ||
Wniosek ten jest przedstawiony na poniższej animicji, gdzie zostały zaznaczone dwa zbiory ścieżek, pierwszy, zaznaczony na zielono, reprezentowany przez macierz <math>A</math> oraz graf <math>G_A</math>, oraz drugi, zaznaczony na niebiesko, reprezentowany przez macierz <math>B</math> oraz graf <math>G_B</math>. W wyniku wykonania mnożenia otrzymujemy macierz <math>C</math> oraz graf <math>G_C</math> | Wniosek ten jest przedstawiony na poniższej animicji, gdzie zostały zaznaczone dwa zbiory ścieżek, pierwszy, zaznaczony na zielono, reprezentowany przez macierz <math>A</math> oraz graf <math>G_A</math>, oraz drugi, zaznaczony na niebiesko, reprezentowany przez macierz <math>B</math> oraz graf <math>G_B</math>. W wyniku wykonania mnożenia otrzymujemy macierz <math>C</math> oraz graf <math>G_C</math> | ||
[[File:Zasd_ilustr_f.svg|600x500px|thumb|center]] | |||
Pokażemy teraz, że produkt odległości jest operacją łączną. | Pokażemy teraz, że produkt odległości jest operacją łączną. | ||
Linia 67: | Linia 66: | ||
{{wzor2|1=<math> | {{wzor2|1=<math> | ||
\left(C \times_{\min} D \right) \times_{min} E = C \times_{\min} \left( D \times_{min} E \right) | \left(C \times_{\min} D \right) \times_{min} E = C \times_{\min} \left( D \times_{min} E \right) | ||
</math>}} | </math>}} | ||
}} | }} | ||
Linia 79: | Linia 78: | ||
{{wzor2|1=<math> C \times_{\min} \left(D + E \right)= C \times_{\min} | {{wzor2|1=<math>C \times_{\min} \left(D + E \right)= C \times_{\min} | ||
D + C \times_{min} E | D + C \times_{min} E</math>}} | ||
Linia 87: | Linia 86: | ||
{{wzor2|1=<math> | {{wzor2|1=<math> | ||
\left(D + E \right) \times_{\min} C = D \times_{\min} C + E \times_{min} C | \left(D + E \right) \times_{\min} C = D \times_{\min} C + E \times_{min} C | ||
</math>}} }} | </math>}} }} | ||
Linia 96: | Linia 95: | ||
{{wzor2|1=<math> \left(I_{\min}\right)_{i,j} = | {{wzor2|1=<math>\left(I_{\min}\right)_{i,j} = | ||
\begin{cases} 0, & \mbox{jeśli } i=j,\\ \infty, & \mbox{jeśli } i \neq j. | \begin{cases} 0, & \mbox{jeśli } i=j,\\ \infty, & \mbox{jeśli } i \neq j. | ||
\end{cases} | \end{cases} | ||
Linia 108: | Linia 107: | ||
{{wzor2|1=<math> I_{\min} \times_{\min} C = C \times_{\min} I_{\min} =C | {{wzor2|1=<math>I_{\min} \times_{\min} C = C \times_{\min} I_{\min} =C</math>}} }} | ||
{{dowod||| Mamy | {{dowod||| Mamy | ||
{{wzor2|1=<math> \left(I_{\min} \times_{\min} C\right)_{i,j} = \min_{k = 1,\ldots,n} \left(I_{\min}\right)_{i,k} + C_{k,j} = </math>}} | {{wzor2|1=<math>\left(I_{\min} \times_{\min} C\right)_{i,j} = \min_{k = 1,\ldots,n} \left(I_{\min}\right)_{i,k} + C_{k,j} =</math>}} | ||
ponieważ wszystkie elementy <math>\left(I_{\min}\right)_{i,k}</math> równe są <math>\infty</math> oprócz elementu <math>i = k</math>, możemy je pominąć w operacji <math>\min</math> i: | ponieważ wszystkie elementy <math>\left(I_{\min}\right)_{i,k}</math> równe są <math>\infty</math> oprócz elementu <math>i = k</math>, możemy je pominąć w operacji <math>\min</math> i: | ||
{{wzor2|1=<math> = \min_{k = i} \left(I_{\min}\right)_{i,k} + C_{k,j} | {{wzor2|1=<math>= \min_{k = i} \left(I_{\min}\right)_{i,k} + C_{k,j} | ||
= I_{i,i} + C_{i,j} = C_{i,j} | = I_{i,i} + C_{i,j} = C_{i,j}</math>}} }} | ||
=== Pomysł algorytmu === | === Pomysł algorytmu === | ||
Linia 198: | Linia 197: | ||
3 '''while''' <math>n-1 > m</math> '''do''' | 3 '''while''' <math>n-1 > m</math> '''do''' | ||
4 '''begin''' | 4 '''begin''' | ||
5 <math>D = </math>MNOŻENIE-ODLEGŁOŚCI<math>(D,D)</math> | 5 <math>D =</math>MNOŻENIE-ODLEGŁOŚCI<math>(D,D)</math> | ||
6 <math>m = 2m</math> | 6 <math>m = 2m</math> | ||
7 '''end''' | 7 '''end''' | ||
Linia 259: | Linia 258: | ||
Niech będzie dany graf skierowany <math>G=(V,E)</math> wraz z funkcją wagową <math>w:E \to \mathcal{R}</math>. Niech funkcja <math>h:V \to \mathcal{R}</math> będzie dowolną funkcją ze zbioru wierzchołków w liczby rzeczywiste. Dla funkcji <math>h</math> oraz <math>w</math> definiujmy nową funkcję wagową oznaczoną <math>w_h</math> w następujący sposób: | Niech będzie dany graf skierowany <math>G=(V,E)</math> wraz z funkcją wagową <math>w:E \to \mathcal{R}</math>. Niech funkcja <math>h:V \to \mathcal{R}</math> będzie dowolną funkcją ze zbioru wierzchołków w liczby rzeczywiste. Dla funkcji <math>h</math> oraz <math>w</math> definiujmy nową funkcję wagową oznaczoną <math>w_h</math> w następujący sposób: | ||
{{wzor|wzor_3|3|3=<math>w_h(u,v) = w(u,v) + h(u) - h(v) | {{wzor|wzor_3|3|3=<math>w_h(u,v) = w(u,v) + h(u) - h(v)</math>}} | ||
Oznaczmy, przez <math>\delta(u,v)</math> odległości w grafie <math>G</math> dla funkcji wagowej <math>w</math> oraz przez <math>\delta_h(u,v)</math> odległości w grafie <math>G</math> dla funkcji wagowej <math>w_h</math>. | Oznaczmy, przez <math>\delta(u,v)</math> odległości w grafie <math>G</math> dla funkcji wagowej <math>w</math> oraz przez <math>\delta_h(u,v)</math> odległości w grafie <math>G</math> dla funkcji wagowej <math>w_h</math>. | ||
Linia 266: | Linia 265: | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>w_h(p) = w(p) + h(v_0) - h(v_k) | <math>w_h(p) = w(p) + h(v_0) - h(v_k)</math> | ||
}} | }} | ||
Linia 287: | Linia 286: | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>= w(p) + h(v_0) - h(v_k) | <math>= w(p) + h(v_0) - h(v_k)</math> | ||
}} | }} | ||
}} | }} | ||
Linia 338: | Linia 337: | ||
Działanie tego algorytmu przedstawione jest na poniższej animacji. | Działanie tego algorytmu przedstawione jest na poniższej animacji. | ||
[[File:Zasd_ilustr_c.svg|600x300px|thumb|center]] | |||
{{lemat|11|lemat_11|3= Jeżeli graf <math>G=(V,E)</math> z wagami krawędzi zadanymi funkcją <math>w</math> zawiera cykl o ujemnej wadze, to [[#algorytm_potenciału|algorytm OBLICZ-POTENCJAŁ]] zwraca wartość <math>NIL</math>. Natomiast w przeciwnym wypadku algorytm zwraca potencjał <math>d(v)</math> dla grafu <math>G</math> z funkcją wagową <math>w</math>. }} | {{lemat|11|lemat_11|3= Jeżeli graf <math>G=(V,E)</math> z wagami krawędzi zadanymi funkcją <math>w</math> zawiera cykl o ujemnej wadze, to [[#algorytm_potenciału|algorytm OBLICZ-POTENCJAŁ]] zwraca wartość <math>NIL</math>. Natomiast w przeciwnym wypadku algorytm zwraca potencjał <math>d(v)</math> dla grafu <math>G</math> z funkcją wagową <math>w</math>. }} | ||
Linia 356: | Linia 354: | ||
<center><math> | <center><math> | ||
w_d(u,v) = w(u,v) + d(u) - d(v) \ge 0 | w_d(u,v) = w(u,v) + d(u) - d(v) \ge 0</math>,</center> | ||
</math></center> | |||
Linia 368: | Linia 365: | ||
{{algorytm|Algorytm Johnsona|algorytm_johnsona|3= | {{algorytm|Algorytm Johnsona|algorytm_johnsona|3= | ||
JOHNSON<math>(G=(E,V),w)</math> | JOHNSON<math>(G=(E,V),w)</math> | ||
1 <math>d = </math>[[#algorytm_potenciału|OBLICZ-POTENCJAŁ]]<math>(G,w)</math> | 1 <math>d =</math>[[#algorytm_potenciału|OBLICZ-POTENCJAŁ]]<math>(G,w)</math> | ||
1 '''if''' <math>d = NIL</math> '''then''' '''return''' <math>NIL</math> | 1 '''if''' <math>d = NIL</math> '''then''' '''return''' <math>NIL</math> | ||
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''' |
Aktualna wersja na dzień 22:16, 11 wrz 2023
Abstrakt
W wykładzie tym zajmiemy się problemem obliczania odległości w grafie między wszystkimi parami wierzchołków w grafie ważonym skierowanym . Przedstawimy trzy algorytmy rozwiązujące ten problem:
- algorytm wykorzystujący mnożenie macierzy, działający w czasie ,
- algorytm Floyda-Warshalla, działający w czasie ,
- algorytm Johnsona, działający w czasie .
Problem najkrótszych ścieżek między wszystkimi parami wierzchołków
Problem najkrótszych ścieżek między wszystkimi parami wierzchołków można rozwiązać, wykonując razy algorytm dla problemu najkrótszych ścieżek z jednego wierzchołka. Jeżeli w grafie wagi krawędzi są nieujemne to możemy użyć algorytmu Dijkstry. Najszybsza implementacja algorytmu Dijskstry wykorzystująca kopce Fibonacciego działa w czasie , co daje nam algorytm rozwiązujący problem policzenia odległości między wszystkimi parami wierzchołków, działający w czasie .
Jednakże tego rozwiązania nie możemy użyć, jeżeli w grafie wagi krawędzi mogą być ujemne. W takim przypadku możemy użyć algorytm Bellmana-Forda. Otrzymamy wtedy algorytm działający w czasie . W rozdziale tym zaprezentujemy bardziej efektywne rozwiązania dla tego problemu.
W rozdziale tym będziemy zakładać, że algorytmy na wejściu otrzymują macierz wag rozmiaru reprezentującą wagi krawędzi -wierzchołkowego grafu . Dla macierzy tej zachodzi:
W problemie najkrótszych ścieżek między wszystkimi parami wierzchołków chcemy wyznaczyć macierz rozmiaru , taką że jest równe odległości z wierzchołka do wierzchołka . Chcemy także wyznaczyć dla każdego wierzchołka drzewo najkrótszych ścieżek ukorzenione w . Podobnie jak w poprzednim rozdziale drzewo możemy kodować dla każdego wierzchołka przy pomocy funkcji poprzedników . Ponieważ tutaj interesuje nas wiele drzew, łatwiej będzie nam używać macierzy poprzedników . Macierz tą definiujemy używając funkcji w następujący sposób:
W pozostałej części tego wykładu zajmiemy się tylko wyznaczaniem macierzy odległości . Zadaniu 3 do tego wykładu polega na pokazaniu, jak znając odległości w grafie policzyć drzewo najkrótszych ścieżek w czasie . Tak więc drzew możemy wyliczyć w czasie . Czas ten jest mniejszy niż czas działania wszystkich prezentowanych w tym wykładzie algorytmów, więc bez straty ogólności, a zyskując na prostocie prezentacji, możemy ograniczyć się tylko do wyznaczenia macierzy odległości .
Co więcej, będziemy zakładać, że w grafie nie ma ujemnych cykli. Ujemne cykle można wykryć w czasie przy użyciu Algorytmu Bellmana-Forda. Zobacz Zadanie 3 do Wykładu 4.
Najkrótsze ścieżki i mnożenie macierzy
Iloczyn odległości i jego właściwości
Załóżmy, że dane mamy dwie macierze wag oraz rozmiaru . Dla macierzy tych definiujemy operację iloczyn odległości, której wynikiem jest także macierz rozmiaru , zdefiniowana jako:
(1)
Wniosek 1
Wniosek ten jest przedstawiony na poniższej animicji, gdzie zostały zaznaczone dwa zbiory ścieżek, pierwszy, zaznaczony na zielono, reprezentowany przez macierz oraz graf , oraz drugi, zaznaczony na niebiesko, reprezentowany przez macierz oraz graf . W wyniku wykonania mnożenia otrzymujemy macierz oraz graf
Pokażemy teraz, że produkt odległości jest operacją łączną.
Lemat 2
Dowód

Co więcej, produkt odległości jest przemienny względem dodawania.
Lemat 3
oraz
Dowód

Zdefiniujmy macierz rozmiaru jako:
Macierz ta jest jedynką dla iloczynu odległości.
Lemat 4
Dowód
Pomysł algorytmu
Łączność iloczynu odległości ma dla nas bardzo ważne konsekwencje i pozwoli nam na konstrukcję algorytmu obliczania odległości w grafie między wszystkimi parami wierzchołków, działającego w czasie . Niech będzie macierzą wag grafu . Rozważmy macierz zdefiniowaną jako:
Pokażemy teraz, że macierz opisuje odległości między wierzchołkami grafu, ale tylko dla ścieżek używających nie więcej niż krawędzi.
Lemat 5
Dowód

Zajmiemy się teraz konstrukcją algorytmu obliczającego najkrótsze ścieżki w grafie. W tym celu będziemy musieli jeszcze udowodnić następujące dwa lematy.
Lemat 6
Dowód

Algorytm
Zauważmy, że iloczyn odległości dwóch macierzy możemy policzyć w czasie wykorzystując następujący algorytm.
Algorytm Mnożenia macierzy odległości
MNOŻENIE-ODLEGŁOŚCI 1 macierz rozmiaru 2 for to do 3 for to do 4 begin 5 6 for to do 7 8 end 9 return E
Ponieważ operacja iloczynu odległości jest łączna, możemy wykorzystać algorytm szybkiego potęgowania i policzyć odległości przy pomocy następującego algorytmu.
Algorytm Algorytm obliczania odległości między wszystkimi parami wierzchołków I
ODLEGŁÓŚCI-I 1 2 3 while do 4 begin 5 MNOŻENIE-ODLEGŁOŚCI 6 7 end 8 return
Poprawność tego algorytmu wynika wprost z Lematu 6 ponieważ na zakończenie algorytmu i .
Algorytm Floyda-Warshalla
W algorytmie Floyda-Warshalla wykorzystamy inną cechę najkrótszych ścieżek niż ta użyta w algorytmie z wykorzystaniem iloczynu odległości. W poprzednim algorytmie konstruowaliśmy coraz dłuższe ścieżki, natomiast tutaj będziemy konstruować ścieżki przechodzące przez coraz większy zbiór wierzchołków. Wierzchołkiem wewnętrznym ścieżki jest każdy wierzchołek na ścieżce różny od jej początku i końca .
Niech zbiorem wierzchołków grafu będzie . Niech dla oznacza najmniejszą wagę ścieżki z do spośród ścieżek, których wierzchołki wewnętrzne należą do zbioru . Pokażemy następujący rekurencyjny wzór na .
Lemat 7
(2)
Dowód
Niech będzie najkrótszą ścieżką z do , której wierzchołki wewnętrzne należą do zbioru . Mamy dwa przypadki:
- Wierzchołek nie leży na ścieżce . Wtedy zachodzi . Ponieważ jest najkrótszą ścieżką, to także i powyższy wzór zachodzi.
- Jeżeli wierzchołek należy do ścieżki , to występuje on dokładnie raz i możemy podzielić na dwie ścieżki z do oraz z do . Ścieżki i nie zawierają wierzchołka jako wierzchołka wewnętrznego. Ponieważ są to podścieżki najkrótszej ścieżki, więc same też są najkrótsze. Zachodzi więc dla nich oraz . Otrzymujemy więc . Ponieważ jest najkrótszą ścieżką, i wzór zachodzi także w tym przypadku.
Wykorzystując wzór (2) możemy skonstruować następujący algorytm, obliczający w czasie odległości między wszystkimi parami wierzchołków.
Algorytm Algorytm Floyda-Warshalla
ODLEGŁÓŚCI-II 1 , 2 for to do 3 for to do 4 for to do 5 6 return
Algorytm Johnsona
W algorytmie Johnsona wykorzystamy spostrzeżenie uczynione przez nas na początku wykładu, że odległości w grafie, w którym wszystkie wagi krawędzi są dodatnie, można obliczyć korzystając z algorytmu Dijkstry w czasie . Pokażemy tutaj, jak zmienić wagi w grafie tak, aby stały się one dodatnie, przy zachowaniu najkrótszych ścieżek.
Niech będzie dany graf skierowany wraz z funkcją wagową . Niech funkcja będzie dowolną funkcją ze zbioru wierzchołków w liczby rzeczywiste. Dla funkcji oraz definiujmy nową funkcję wagową oznaczoną w następujący sposób:
(3)
Oznaczmy, przez odległości w grafie dla funkcji wagowej oraz przez odległości w grafie dla funkcji wagowej .
Lemat 8
Dowód
Widzimy więc, że zmiana długości ścieżki zależy tylko od jej końców. Otrzymujemy stąd wprost dwa wnioski.
Wniosek 9
Wniosek 10
Dowód

Funkcję nazwiemy potencjałem jeżeli zachodzi:
(4)
Pokażemy teraz, jak wyznaczyć funkcję potencjału dla grafu . Rozważmy następujący algorytm:
Algorytm Algorytm obliczania potencjału
OBLICZ-POTENCJAŁ 1 nowy wierzchołek 2 3 4 5 BELLMAN-FORD 6 if then 7 return (graf zawiera cykl ujemnej długości) 8 return ( obliczone w algorytmie Bellmana-Forda jest potencjałem)
Działanie tego algorytmu przedstawione jest na poniższej animacji.
Lemat 11
Dowód
Zakładamy teraz, że graf nie zawiera cyklu o ujemnej wadze, wtedy wartości jako odległości w grafie spełniają:
Innymi słowy:

Powyższy lemat jest ostatnim składnikiem potrzebnym nam do konstrukcji algorytmu Johnsona.
Algorytm Algorytm Johnsona
JOHNSON 1 OBLICZ-POTENCJAŁ 1 if then return 3 for każda krawędź do 4 5 for każdy wierzchołek do 6 = DIJKSTRA 7 for każdy wierzchołek do 8 9 return
Poprawność tego algorytmu wynika wprost z Lematu 11. Czas działania algorytmu jest równy sumie czasu działania algorytmu Bellmana-Forda i czasu potrzebnego na wywołań algorytmu Dijkstry, co daje całkowity czas .