Zaawansowane algorytmy i struktury danych/Wykład 6: Różnice pomiędzy wersjami
Nie podano opisu zmian |
|||
Linia 3: | Linia 3: | ||
W wykładzie tym zajmiemy się problemem obliczanie odległości w grafie między wszystkimi parami wierzchołków w grafie ważonym | W wykładzie tym zajmiemy się problemem obliczanie odległości w grafie między wszystkimi parami wierzchołków w grafie ważonym | ||
skierowanym <math>G=(V,E)</math>. Przedstawimy trzy algorytmy rozwiązujące ten problem: | skierowanym <math>G=(V,E)</math>. Przedstawimy trzy algorytmy rozwiązujące ten problem: | ||
* algorytm wykorzystujący mnożenie macierzy działający w czasie <math>O(|V|^3\log |V|</math>, | * algorytm wykorzystujący mnożenie macierzy działający w czasie <math>O(|V|^3\log |V|)</math>, | ||
* algorytm Floyda-Warshalla działający w czasie <math>O(|V|^3)</math>, | * algorytm Floyda-Warshalla działający w czasie <math>O(|V|^3)</math>, | ||
* algorytm Johnsona działający w czasie <math>O(|V|^2 \log |V| + |V||E|)</math>. | * algorytm Johnsona działający w czasie <math>O(|V|^2 \log |V| + |V||E|)</math>. | ||
Linia 9: | Linia 9: | ||
== Problem najkrótszych ścieżek między wszystkimi parami wierzchołków == | == 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 <math>|V|</math> 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 <math>O(|V| \log |V| + |E|)</math>, co daje nam algorytm rozwiązujący problem policzenia odległości między wszystkimi parami wierzchołków działający w czasie <math>O(|V|^2 \log |V| + |V||E|)</math>. | Problem najkrótszych ścieżek między wszystkimi parami wierzchołków można rozwiązać, wykonując <math>|V|</math> 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ć [[Algorytmy i struktury danych/ASD Moduł 11#algorytm_dijkstry|algorytmu Dijkstry]]. Najszybsza implementacja algorytmu Dijskstry wykorzystująca kopce Fibonacciego działa w czasie <math>O(|V| \log |V| + |E|)</math>, co daje nam algorytm rozwiązujący problem policzenia odległości między wszystkimi parami wierzchołków działający w czasie <math>O(|V|^2 \log |V| + |V||E|)</math>. | ||
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ć [[../Wykład 5#algorytm_Bellmana-Forda|algorytm Bellmana-Forda]]. Otrzymamy wtedy algorytm działający w czasie <math>O(|V|^2|E|</math>. W rozdziale tym zaprezentujemy bardziej efektywne rozwiązania dla tego problemu. | 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ć [[../Wykład 5#algorytm_Bellmana-Forda|algorytm Bellmana-Forda]]. Otrzymamy wtedy algorytm działający w czasie <math>O(|V|^2|E|)</math>. W rozdziale tym zaprezentujemy bardziej efektywne rozwiązania dla tego problemu. | ||
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: | ||
Linia 25: | Linia 25: | ||
W problemie najkrótszych ścieżek między wszystkimi parami wierzchołków chcemy wyznaczyć macierz <math>D</math> rozmiaru <math>n \times n</math> taką, że <math>d_{i,j}</math> jest równe odległości <math>\delta(i,j)</math> z wierzchołka <math>i</math> do wierzchołka <math>j</math>. Chcemy także wyznaczyć dla każdego wierzchołka <math>v</math> [[../Wykład 5#drzewo_najkrótszych_ścieżek|drzewo najkrótszych ścieżek]] <math>T_v</math> ukorzenione w <math>v</math>. Podobnie jak w poprzednim rozdziale drzewo <math>T_v</math> możemy kodować dla każdego wierzchołka przy pomocy funkcji poprzedników <math>\pi_{v}</math>. Ponieważ tutaj interesuje nas wiele drzew to łatwiej będzie nam używać {{kotwica|macierz_poprzedników|'''macierzy poprzedników''}} <math>\Pi</math>. Macierz tą definiujemy używając funkcji <math>\pi_v</math> w następujący sposób: | W problemie najkrótszych ścieżek między wszystkimi parami wierzchołków chcemy wyznaczyć macierz <math>D</math> rozmiaru <math>n \times n</math> taką, że <math>d_{i,j}</math> jest równe odległości <math>\delta(i,j)</math> z wierzchołka <math>i</math> do wierzchołka <math>j</math>. Chcemy także wyznaczyć dla każdego wierzchołka <math>v</math> [[../Wykład 5#drzewo_najkrótszych_ścieżek|drzewo najkrótszych ścieżek]] <math>T_v</math> ukorzenione w <math>v</math>. Podobnie jak w poprzednim rozdziale drzewo <math>T_v</math> możemy kodować dla każdego wierzchołka przy pomocy funkcji poprzedników <math>\pi_{v}</math>. Ponieważ tutaj interesuje nas wiele drzew to łatwiej będzie nam używać {{kotwica|macierz_poprzedników|'''macierzy poprzedników'''}} <math>\Pi</math>. Macierz tą definiujemy używając funkcji <math>\pi_v</math> w następujący sposób: | ||
Linia 33: | Linia 33: | ||
W pozostałej części tego wykładu zajmiemy się tylko wyznaczaniem macierzy odległości <math>D</math>. | W pozostałej części tego wykładu zajmiemy się tylko wyznaczaniem macierzy odległości <math>D</math>. [[../Ćwiczenia 6#Zadanie 3|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 <math>O(|E|)</math>. Tak więc <math>|V|</math> drzew możemy wyliczyć w czasie <math>O(|E||V|)</math>. 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 <math>D</math>. | ||
Linia 53: | Linia 53: | ||
{{wniosek|1|wniosek_konkatenacja|3= | {{wniosek|1|wniosek_konkatenacja|3= | ||
Jeżeli założymy, że <math>C</math> i <math>D</math> opisują minimalne wagi | Jeżeli założymy, że <math>C</math> i <math>D</math> opisują minimalne wagi zbioru ścieżek odpowiednio | ||
<math>\Pi_{C}</math> i <math>\Pi_{D}</math> w pewnym grafie <math>G</math>, to iloczyn odległości wyznacza minimalne wagi zbioru ścieżek powstałego z konkatenacji ścieżek ze zbioru <math>\Pi_{C}</math> ze ścieżkami zbioru <math>\Pi_{D}</math>. | <math>\Pi_{C}</math> i <math>\Pi_{D}</math> w pewnym grafie <math>G</math>, to iloczyn odległości wyznacza minimalne wagi zbioru ścieżek powstałego z konkatenacji ścieżek ze zbioru <math>\Pi_{C}</math> ze ścieżkami ze zbioru <math>\Pi_{D}</math>. | ||
}} | }} | ||
Wniosek ten jest przedstawiony na poniższej animicji, gdzie | 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> | ||
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> | |||
<flash>file=Zasd_Ilustr_f.swf|width=600|height=500</flash> | <flash>file=Zasd_Ilustr_f.swf|width=600|height=500</flash> | ||
Pokażemy teraz, że produkt odległości jest operacją łączną. | Pokażemy teraz, że produkt odległości jest operacją łączną. | ||
{{wzor2|1=}} | |||
{{lemat|2|io_łączny|3= | {{lemat|2|io_łączny|3= | ||
Dla macierzy <math>C</math>, <math>D</math> i <math>E</math> rozmiaru <math>n\times n</math>zachodzi: | Dla macierzy <math>C</math>, <math>D</math> i <math>E</math> rozmiaru <math>n\times n</math> zachodzi: | ||
{{wzor2|1=<math> | {{wzor2|1=<math> | ||
Linia 78: | Linia 77: | ||
Co więcej produkt odległości jest przemienny względem dodawania. | Co więcej produkt odległości jest przemienny względem dodawania. | ||
{{lemat|io_przemienny | {{lemat|3|io_przemienny|3= Dla macierzy <math>C</math>, <math>D</math> i <math>E</math> rozmiaru <math>n\times n</math> zachodzi: | ||
Linia 101: | Linia 100: | ||
\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} | ||
</math> | </math>}} | ||
Linia 124: | Linia 123: | ||
=== Pomysł algorytmu === | === 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 <math>O( | Łą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 <math>O(|V|^{3}\log |V|)</math>. Niech <math>W</math> będzie [[#macierz_wag|macierzą wag]] grafu <math>G = (V,E)</math>. Rozważmy macierz <math>W^m</math> zdefiniowaną jako: | ||
Linia 136: | Linia 135: | ||
}} | }} | ||
Pokażemy teraz, że macierz <math>W^m</math> opisuje odległości między wierzchołkami grafu ale tylko dla ścieżek używających | Pokażemy teraz, że macierz <math>W^m</math> opisuje odległości między wierzchołkami grafu ale tylko dla ścieżek używających nie więcej niż <math>m</math> krawędzi. | ||
{{lemat|5|potęgi_odległości|3= | {{lemat|5|potęgi_odległości|3= | ||
Element <math>W_{i,j}^{m}</math> macierzy <math>W^{m}</math> zadaje najmniejszą wagę ścieżki wychodzącej z wierzchołka <math>i</math> do wierzchołka <math>j</math> spośród ścieżek, które zawierają | Element <math>W_{i,j}^{m}</math> macierzy <math>W^{m}</math> zadaje najmniejszą wagę ścieżki wychodzącej z wierzchołka <math>i</math> do wierzchołka <math>j</math> spośród ścieżek, które zawierają nie więcej niż <math>m</math> krawędzi, tzn.: | ||
Linia 151: | Linia 150: | ||
}} | }} | ||
{{dowod|||3= Tezę tą udowodnimy przez indukcję po <math>m</math>. | {{dowod|||3= Tezę tą udowodnimy przez indukcję po <math>m</math>. Ścieżki używające <math>0</math> krawędzi istnieją tylko z każdego wierzchołka do niego samego i mają długość <math>0</math>, a więc dla <math>m=0</math> teza wynika wprost z definicji macierzy <math>W^0</math>. | ||
Załóżmy teraz, że teza zachodzi dla <math>m >0</math>. Mamy wtedy <math>W^{m+1} = W \times_{\min} W^{m}</math>. Zauważmy, że definicja [[#macierz_wag|macierzy wag]] <math>W</math> opisuje ścieżki używające <math>\le 1</math> krawędź. Korzystając teraz z [[#wniosek_konkatenacja|Wniosku 1]] otrzymujemy tezę dla <math>m+1</math>. | Załóżmy teraz, że teza zachodzi dla <math>m >0</math>. Mamy wtedy <math>W^{m+1} = W \times_{\min} W^{m}</math>. Zauważmy, że definicja [[#macierz_wag|macierzy wag]] <math>W</math> opisuje ścieżki używające <math>\le 1</math> krawędź. Korzystając teraz z [[#wniosek_konkatenacja|Wniosku 1]] otrzymujemy tezę dla <math>m+1</math>. | ||
Linia 170: | Linia 169: | ||
}} | }} | ||
{{dowod|||3= Jeżeli w grafie nie istnieje cykl o ujemnej wadze, to wszystkie najkrótsze ścieżki są ścieżkami prostymi, a więc mają długość co najwyżej <math>n-1</math>. Z [[#potęgi_odległości|Lematu 5]] wynika więc że odległości tych ścieżek są dobrze wyznaczone w <math>W^{k}</math> dla <math>k \ | {{dowod|||3= Jeżeli w grafie nie istnieje cykl o ujemnej wadze, to wszystkie najkrótsze ścieżki są ścieżkami prostymi, a więc mają długość co najwyżej <math>n-1</math>. Z [[#potęgi_odległości|Lematu 5]] wynika więc że odległości tych ścieżek są dobrze wyznaczone w <math>W^{k}</math> dla <math>k \ge n-1</math>. }} | ||
=== Algorytm === | === Algorytm === | ||
Linia 179: | Linia 178: | ||
{{algorytm|Mnożenia macierzy odległości|algorytm_iloczyn_odległości|3= | {{algorytm|Mnożenia macierzy odległości|algorytm_iloczyn_odległości|3= | ||
MNOŻENIE-ODLEGŁOŚCI(C,D) | MNOŻENIE-ODLEGŁOŚCI<math>(C,D)</math> | ||
1 <math>E</math> macierz rozmiaru <math>n \times n</math> | 1 <math>E</math> macierz rozmiaru <math>n \times n</math> | ||
2 '''for''' <math>i = 0</math> '''to''' <math>n-1</math> '''do''' | 2 '''for''' <math>i = 0</math> '''to''' <math>n-1</math> '''do''' | ||
Linia 186: | Linia 185: | ||
5 <math>e_{i,j} = \infty</math> | 5 <math>e_{i,j} = \infty</math> | ||
6 '''for''' <math>k =0</math> '''to''' <math>n-1</math> '''do''' | 6 '''for''' <math>k =0</math> '''to''' <math>n-1</math> '''do''' | ||
7 e_{i,j} = \min(c_{i,k} + d_{k,j}, e_{i,j}) | 7 <math>e_{i,j} = \min(c_{i,k} + d_{k,j}, e_{i,j})</math> | ||
8 '''end''' | 8 '''end''' | ||
9 '''return''' E | 9 '''return''' E | ||
Linia 195: | Linia 194: | ||
{{algorytm|Algorytm obliczania odległości między wszystkimi parami wierzchołków I|algorytm_apsp_mnozenie|3= | {{algorytm|Algorytm obliczania odległości między wszystkimi parami wierzchołków I|algorytm_apsp_mnozenie|3= | ||
ODLEGŁÓŚCI-I(W) | ODLEGŁÓŚCI-I<math>(W)</math> | ||
1 <math>D = W</math> | 1 <math>D = W</math> | ||
2 <math> | 2 <math>m = 1</math> | ||
3 '''while''' <math>n-1 > | 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> | ||
Linia 213: | Linia 212: | ||
[[#iloczyn_odległości|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 {{kotwica|wierzchołek_wewnętrzny|'''wewnętrznym'''}} ścieżki <math>p = (v_0, \ldots, v_l)</math> jest każdy wierzchołek na ścieżce <math>p</math> różny od jej początku <math>v_0</math> i końca <math>v_l</math>. | [[#iloczyn_odległości|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 {{kotwica|wierzchołek_wewnętrzny|'''wewnętrznym'''}} ścieżki <math>p = (v_0, \ldots, v_l)</math> jest każdy wierzchołek na ścieżce <math>p</math> różny od jej początku <math>v_0</math> i końca <math>v_l</math>. | ||
Niech zbiorem wierzchołków grafu <math>G</math> będzie <math>V = \{1,\ldots,n\}</math>. Niech <math>d_{i,j}^{(k)}</math> dla <math>k = 0,\ldots, n</math> oznacza najmniejszą wagę ścieżki z <math>i</math> do <math>j</math>, spośród ścieżek których wierzchołki wewnętrzne należą do zbioru <math>\{ | Niech zbiorem wierzchołków grafu <math>G</math> będzie <math>V = \{1,\ldots,n\}</math>. Niech <math>d_{i,j}^{(k)}</math> dla <math>k = 0,\ldots, n</math> oznacza najmniejszą wagę ścieżki z <math>i</math> do <math>j</math>, spośród ścieżek których wierzchołki wewnętrzne należą do zbioru <math>\{1, \ldots,k\}</math>. Pokażemy następujący rekurencyjny wzór na <math>D^{(k)}</math>. | ||
{{lemat|7|lemat_7|3= | {{lemat|7|lemat_7|3= | ||
Linia 222: | Linia 221: | ||
d_{i,j}^{(k)} = \begin{cases} | d_{i,j}^{(k)} = \begin{cases} | ||
w_{i,j}, & \mbox{jeżeli } k=0,\\ | w_{i,j}, & \mbox{jeżeli } k=0,\\ | ||
\min( d_{i,j}^{(k-1)}, d_{i,k}^{(k-1)} + d_{k,j}^{(k-1)}, & \mbox{jeżeli } k \ge 1. | \min( d_{i,j}^{(k-1)}, d_{i,k}^{(k-1)} + d_{k,j}^{(k-1)}), & \mbox{jeżeli } k \ge 1. | ||
\end{cases} | \end{cases} | ||
</math> | </math> | ||
Linia 245: | Linia 244: | ||
{{algorytm|Algorytm Floyda-Warshalla|algorytm_Floyda-Warshalla|3= | {{algorytm|Algorytm Floyda-Warshalla|algorytm_Floyda-Warshalla|3= | ||
ODLEGŁÓŚCI-II(W) | ODLEGŁÓŚCI-II<math>(W)</math> | ||
1 <math>D^{(0)} = W</math>, | 1 <math>D^{(0)} = W</math>, | ||
2 '''for''' <math>k = 1</math> '''to''' <math>n</math> '''do''' | 2 '''for''' <math>k = 1</math> '''to''' <math>n</math> '''do''' | ||
Linia 253: | Linia 252: | ||
6 '''return''' <math>D^{(n)}</math> | 6 '''return''' <math>D^{(n)}</math> | ||
}} | }} | ||
== Algorytm Johnsona == | == 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ą dodanie można obliczyć korzystając z [[algorytm_dijkstry|algorytmu Dijkstry]] w czasie <math>O(|V|^2\log |V| + |V||E|)</math>. Pokażemy tutaj jak zmienić wagi w grafie tak, aby stały się one dodatnie, przy zachowaniu najkrótszych ścieżek. | 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ą dodanie można obliczyć korzystając z [[Algorytmy i struktury danych/ASD Moduł 11#algorytm_dijkstry|algorytmu Dijkstry]] w czasie <math>O(|V|^2\log |V| + |V||E|)</math>. 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 <math>G(V,E) | 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).</math>}} | {{wzor|wzor_3|3|3=<math>w_h(u,v) = w(u,v) + h(u) - h(v).</math>}} | ||
Linia 264: | Linia 264: | ||
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 gafie <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 gafie <math>G</math> dla funkcji wagowej <math>w_h</math>. | ||
{{lemat|lemat_8 | {{lemat|8|lemat_8|3=Dla funkcji <math>w</math>, <math>w_h</math>, oraz dowolnej ścieżki <math>p= (v_0,\ldots,v_k)</math> zachodzi: | ||
{{wzor2|1= | {{wzor2|1= | ||
Linia 286: | Linia 286: | ||
<math>= \sum_{i=0}^{k-1} w(v_i, v_{i+1}) + \sum_{i=0}^{k-1}h(v_i) - \sum_{i=0}^{k-1}h(v_{i+1}) =</math> | <math>= \sum_{i=0}^{k-1} w(v_i, v_{i+1}) + \sum_{i=0}^{k-1}h(v_i) - \sum_{i=0}^{k-1}h(v_{i+1}) =</math> | ||
}} | }} | ||
{{wzor2|1= | {{wzor2|1= | ||
Linia 298: | Linia 297: | ||
{{wniosek|wniosek_9 | {{wniosek|9|wniosek_9|3=Dla funkcji <math>w</math>, <math>w_h</math>, oraz dowolnej pary wierzchołków zachodzi: | ||
{{wzor2|1= | {{wzor2|1= | ||
Linia 309: | Linia 308: | ||
{{wniosek|lemat_10 | {{wniosek|10|lemat_10|3= Ścieżka <math>p</math> z wierzchołka <math>u</math> do wierzchołka <math>v</math> jest najkrótszą ścieżką w <math>G= (V,E)</math> dla funkcji wagową <math>w</math> wtedy i tylko wtedy gdy jest najkrótszą ścieżką w <math>G</math> dla funkcji wagowej <math>w_h</math>. }} | ||
{{dowod|||3= Wniosek ten wynika wprost z [[#lemat_8|Lematu 8]] i [[#wniosek_9|Wniosku 9]], który pokazuje, że odległości transformują się tak samo jak wagi ścieżek, a więc równość <math>w(p) = \delta(u,v)</math> jest równoważna równości <math>w_h(p) = \delta_h(u,v)</math>. | {{dowod|||3= Wniosek ten wynika wprost z [[#lemat_8|Lematu 8]] i [[#wniosek_9|Wniosku 9]], który pokazuje, że odległości transformują się tak samo jak wagi ścieżek, a więc równość <math>w(p) = \delta(u,v)</math> jest równoważna równości <math>w_h(p) = \delta_h(u,v)</math>. | ||
Linia 315: | Linia 314: | ||
Funkcję <math>h</math> nazwiemy | Funkcję <math>h</math> nazwiemy {{kotwica|potencjał|'''potencjałem'''}} jeżeli zachodzi: | ||
{{kotwica|potencjał|'''potencjałem'''}} jeżeli | |||
zachodzi: | |||
{{wzor|wzor_4|4|3= | {{wzor|wzor_4|4|3= | ||
<math> | <math> | ||
w_h(u,v) \ge 0, \mbox{ dla } | w_h(u,v) \ge 0, \mbox{ dla } (u,v) \in E. | ||
</math> | </math> | ||
}} | }} | ||
Pokażemy teraz jak wyznaczyć funkcję potencjału <math>h</math> dla grafu <math>G</math>. Rozważmy następujący algorytm: | Pokażemy teraz jak wyznaczyć funkcję potencjału <math>h</math> dla grafu <math>G</math>. Rozważmy następujący algorytm: | ||
{{algorytm|Algorytm obliczania potencjału|algorytm_potenciału|3= | {{algorytm|Algorytm obliczania potencjału|algorytm_potenciału|3= | ||
OBLICZ-POTENCJAŁ(G=(V,E), w) | OBLICZ-POTENCJAŁ<math>(G=(V,E), w)</math> | ||
1 <math>s</math> nowy wierzchołek | 1 <math>s</math> nowy wierzchołek | ||
2 <math>V' = V \cup \{s\}</math> | 2 <math>V' = V \cup \{s\}</math> | ||
3 <math>E' = E \cup \{(s,v): v \in V\}</math> | 3 <math>E' = E \cup \{(s,v): v \in V\}</math> | ||
4 <math>w'(u,v) = \begin{cases}w(u,v), & \mbox{jeżeli} (u,v)\in E\\ 0 | 4 <math>w'(u,v) = \begin{cases}w(u,v), & \mbox{jeżeli } (u,v)\in E\\ 0 & \mbox{jeżeli } u=s\\0& \mbox{w pozostałych przypadkach}\end{cases}</math> | ||
5 <math>(d,\pi)=</math>[[../Wykład 5#algorytm_Bellmana-Forda|BELLMAN-FORD]]<math>(G',w,s)</math> | 5 <math>(d,\pi)=</math>[[../Wykład 5#algorytm_Bellmana-Forda|BELLMAN-FORD]]<math>(G',w,s)</math> | ||
6 '''if''' <math>(d,\pi) = NIL</math> '''then''' | 6 '''if''' <math>(d,\pi) = NIL</math> '''then''' | ||
7 '''return''' NIL ''(graf zawiera cykl ujemnej długości)'' | 7 '''return''' <math>NIL</math> ''(graf zawiera cykl ujemnej długości)'' | ||
8 '''return''' <math>d</math> ''(<math>d(v)</math> obliczone w algorytmie Bellmana-Forda jest potencjałem)'' | |||
}} | }} | ||
Linia 343: | Linia 341: | ||
<flash>file=Zasd_Ilustr_c.swf |width=600|height=300</flash> | <flash>file=Zasd_Ilustr_c.swf |width=600|height=300</flash> | ||
{{lemat|lemat_11 | {{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>. }} | ||
{{dowod|||3= Zauważmy, że dodanie wierzchołka <math>s</math> do grafu <math>G</math>nie wprowadza nowych cykli | {{dowod|||3= Zauważmy, że dodanie wierzchołka <math>s</math> do grafu <math>G</math> nie wprowadza nowych cykli, a jedynie powoduje, że wszystkie wierzchołki są osiągalne z <math>s</math>. Dlatego algorytm Bellmana-Forda uruchomiany dla <math>G'</math> i wierzchołka <math>s</math> zwraca <math>NIL</math> wtedy i tylko wtedy gdy <math>G</math> zawiera cyklu o ujemnej wadze. | ||
Zakładamy teraz, że graf <math>G</math> nie zawiera | Zakładamy teraz, że graf <math>G</math> nie zawiera cyklu o ujemnej wadze, wtedy wartości <math>d(v)</math> jako odległości w grafie spełniają: | ||
Linia 370: | Linia 368: | ||
{{algorytm|Algorytm Johnsona|algorytm_johnsona|3= | {{algorytm|Algorytm Johnsona|algorytm_johnsona|3= | ||
JOHNSON(G=(E,V),w) | 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''' NIL | 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''' | ||
4 <math>w_d(u,v) = w(u,v) + d(u) - d(v)</math> | 4 <math>w_d(u,v) = w(u,v) + d(u) - d(v)</math> | ||
5 '''for''' każdy wierzchołek <math>u \in V</math> '''do''' | 5 '''for''' każdy wierzchołek <math>u \in V</math> '''do''' | ||
6 (d_h,\pi) = [[DIJKSTRA]]<math>(G,w_h,u)</math> | 6 <math>(d_h,\pi)</math> = [[DIJKSTRA]]<math>(G,w_h,u)</math> | ||
7 '''for''' każdy wierzchołek <math>v \in V</math> '''do''' | 7 '''for''' każdy wierzchołek <math>v \in V</math> '''do''' | ||
8 <math>d(u,v) = d_h(v) + h(v) - h(u)</math> | 8 <math>d(u,v) = d_h(v) + h(v) - h(u)</math> | ||
Linia 382: | Linia 380: | ||
}} | }} | ||
Poprawność tego algorytmu wynika wprost z [[#lemat_11|Lematu 11]]. Czas działania algorytmu jest | Poprawność tego algorytmu wynika wprost z [[#lemat_11|Lematu 11]]. Czas działania algorytmu jest równy sumie czasu działania algorytmu Bellmana-Forda i czasu potrzebnego na <math>O(|V|)</math> wywołań algorytmu Dijkstry, co daje całkowity czas <math>O(|V||E| + |V|^2 \log |V|)</math>. |
Wersja z 11:10, 19 wrz 2006
Abstrakt
W wykładzie tym zajmiemy się problemem obliczanie 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 to ł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 <flash>file=Zasd_Ilustr_f.swf|width=600|height=500</flash>
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 potrzebowali 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 to 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ą to 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ą dodanie 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 gafie 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. <flash>file=Zasd_Ilustr_c.swf |width=600|height=300</flash>
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 .