Zaawansowane algorytmy i struktury danych/Wykład 6: 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 77 wersji utworzonych przez 3 użytkowników)
Linia 1: Linia 1:
== Abstrakt ==
== 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
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 algorytmu 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>.


== 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
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>.
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 implementacji 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
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.
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ą
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:
{{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:




<center><math>
{{wzor2|1=<math>
w_{i,j} = \begin{cases}
w_{i,j} = \begin{cases}
0, & \mathrm{jeśli } i=j,
0, & \mbox{jeśli } i=j,\\
\mathrm{waga krawędzi } (i,j), & \matkrm{jeśli } i\neq j \mathrm{ i } (i,j)\in E,
\mbox{waga krawędzi } (i,j), & \mbox{jeśli } i\neq j \mbox{ i } (i,j)\in E,\\
\infty, & \matkrm{jeśli } i\neq j \mathrm{ i } (i,j)\neq E.
\infty, & \mbox{jeśli } i\neq j \mbox{ i } (i,j)\neq E.
\end{cases}
\end{cases}
</math></center>
</math>}}




W problemie najkrótszych ścieżek między wszystkimi parami
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, ł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:
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:




<center><math>
{{wzor2|1=<math>
\Pi_{v,u} = \pi_v(u).
\Pi_{v,u} = \pi_v(u).
</math></center>
</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>.


W pozostałej części tego wykładu zajmiemy się tylko wyznaczaniem
macierzy odległości <math>D</math>. Jak to zostało pokazane w
[[Ćwiczenia 5/Zadanie 3|Zadaniu 3]] do poprzedniego wykładu znając
odległości w grafie drzewo najkrótszych ścieżek można wyznaczyć w
czasie <math>O(|E|)</math>, a 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>.


Co więcej będziemy zakładać, że w grafie nie ma ujemnych cykli.
Co więcej, będziemy zakładać, że w grafie nie ma ujemnych cykli. Ujemne cykle można wykryć w czasie <math>O(|V||E|)</math> przy użyciu [[../Wykład 5#algorytm_Bellmana-Forda|Algorytmu Bellmana-Forda]]. Zobacz [[../Ćwiczenia 5#Zadanie 3| Zadanie 3]] do Wykładu 4.
Ujemne cykle można wykryć w czasie <math>O(|V||E|)</math> przy
użyciu [[wykład 5/algorytm_Bellmana-Forda|Algorytmu
Bellmana-Forda]]. Zobacz [[Ćwiczenia 5#Zadanie 4| Zadanie 4]] do
Wykładu 4.


== Najkrótsze ścieżki i mnożenie macierzy ==
== Najkrótsze ścieżki i mnożenie macierzy ==


Załóżmy, że dane mamy dwie [[#macierz_wag|macierze wag]]
=== Iloczyn odległości i jego właściwości ===
<math>C</math> oraz <math>D</math> rozmiaru <math>n\times n</math>.
 
Dla macierzy tych definiujemy operację <math>\times_{\min}</math>l
Załóżmy, że dane mamy dwie [[#macierz_wag|macierze wag]] <math>C</math> oraz <math>D</math> rozmiaru <math>n\times n</math>.
{{kotwica|liczyn_odległości|'''iloczynu odległości'''}}, której
Dla macierzy tych definiujemy operację <math>\times_{\min}</math> {{kotwica|iloczyn_odległości|'''iloczyn odległości'''}}, której wynikiem jest także macierz rozmiaru <math>n \times n</math>, zdefiniowana jako:
wynikiem jest także macierz rozmiaru <math>n \times n</math>,
zdefiniowana jako:


{{wzor|wzór_1|1|3=
{{wzor|wzór_1|1|3=
<math>
<math>
(C \times_{\min} D)_{i,j} = \min_{k = 1,\ldots,n} C_{i,k} + D_{k,j}.
(C \times_{\min} D)_{i,j} = \min_{k = 1,\ldots,n} C_{i,k} + D_{k,j}.
</center>
</math>
}}
}}




{{wniosek|wniosek_konkatenacja|1|3=
{{wniosek|1|wniosek_konkatenacja|3=
 
Jeżeli założymy, że <math>C</math> i <math>D</math> opisują minimalne wagi zbioru ścieżek odpowiednio
Jeżeli założymy, że <math>C</math> i <math>D</math> opisują
<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>.
minimalne wagi odpowiednio 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>.
 
}}
}}


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ą.
{{wzor2|1=}}


{{lemat|io_łączny|2|3=
{{lemat|2|io_łączny|3=
Dla macierzy <math>C</math>, <math>D</math> i <math>E</math>
Dla macierzy <math>C</math>, <math>D</math> i <math>E</math> rozmiaru <math>n\times n</math> zachodzi:
rozmiaru <math>n\times n</math>zachodzi:


 
{{wzor2|1=<math>
<center><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></center>
}}
}}


{{dowod||| Powyższa równość wynika wprost z [[#wzór_1|wzoru (1))]] oraz
{{dowod||| Powyższa równość wynika wprost z [[#wzór_1|wzoru (1)]] oraz przemienności operacji <math>\min</math>.
przemienności operacji <math>\min</math>.
}}
}}


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|3|3= Dla macierzy <math>C</math>, <math>D</math>
{{lemat|3|io_przemienny|3= Dla macierzy <math>C</math>, <math>D</math> i <math>E</math> rozmiaru <math>n\times n</math> zachodzi:
i <math>E</math> rozmiaru <math>n\times n</math>zachodzi:




<center><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, </math></center>
D +  C  \times_{min} E</math>}}




Linia 134: Linia 85:




<center><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></center> }} {{dowod||| Te dwie równości wynikają ponownie z
</math>}} }}
[[#wzór_1|wzoru (1)]] oraz przemienności operacji <math>\min</math>
 
względem dodawania. }}
{{dowod||| Te dwie równości wynikają ponownie z [[#wzór_1|wzoru (1)]] oraz przemienności operacji <math>\min</math> względem dodawania. }}
 
 
Zdefiniujmy macierz <math>I_{\min}</math> rozmiaru <math>n \times n</math> jako:
 


Zdefiniujmy macierz <math>I_{\min}</math> rozmiaru <math>n \times
{{wzor2|1=<math>\left(I_{\min}\right)_{i,j} =
n</math> jako: <center><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}
</math></center>
</math>}}
 


Macierz ta jest jedynką dla iloczynu odległości.
Macierz ta jest jedynką dla iloczynu odległości.


{{lemat|io_jedynka|4|3= Dla macierzy <math>C</math> rozmiaru <math>n
 
\times n</math> zachodzi:
{{lemat|4|io_jedynka|3= Dla macierzy <math>C</math> rozmiaru <math>n \times n</math> zachodzi:




<center><math> I_{\min} \times_{\min} C = C \times_{\min} I_{\min} =
{{wzor2|1=<math>I_{\min} \times_{\min} C = C \times_{\min} I_{\min} =C</math>}} }}
C. </math></center> }}


{{dowód||| Mamy
{{dowod||| Mamy


<center><math> \left(\I_{\min} \times_{\min} C\right)_{i,j} =
{{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>}}
\min_{k = 1,\ldots,n} \left(I_{\min}\right)_{i,k} + C_{k,j} =
</math></center>


ponieważ wszystkie elementy <math>\left(I_{\min}\right)_{i,k}</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:
równe są <math>\infty</math> oprócz elementu <math>i = k</math>, to
możemy je pominąć w operacji <math>\min</math> i:




<center><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}. </math></center> }}
= I_{i,i} + C_{i,j} = C_{i,j}</math>}} }}


=== Pomysł algorytmu ===


Łączność iloczynu odległości ma dla nas bardzo ważne konsekwencję i
Łą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:
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(n^{3}\log n)</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:




<center>
{{wzor2|1=
<math>
<math>
W^{m} = \begin{cases}
W^{m} = \begin{cases}
I_{\min}, &\mbox{jeżeli } m=0,\\
I_{\min}, &\mbox{jeżeli } m=0,\\
W \times_{\min} W^{m-1}, &\mbox{jeżeli } m>0.
W \times_{\min} W^{m-1}, &\mbox{jeżeli } m>0.
\end{cases}
</math>
</math>
</center>
}}


Pokażemy teraz, że macierz <math>W^m</math> opisuje odległości
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.
między wierzchołkami grafu ale tylko dla ścieżek używających mniej niż
<math>m</math> krawędzi.


{{lemat|potęgi_odległości|5|3=
{{lemat|5|potęgi_odległości|3=


Element <math>W_{i,j}^{m}</math> macierzy <math>W^{m}</math> zadaje
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.:
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ą
mniej niż <math>m</math> krawędzi, tzn.:




<center><math>
{{wzor2|1=<math>
w_{i,j}^{m} = \begin{cases}
w_{i,j}^{m} = \begin{cases}
\min\{w(p): p \mbox{ ścieżka o długości } \le m \mboc{ z } u \mbox{ do } v\}, & \mbox{jeżeli istnieje ścieżka o długości } \le m \mboc{ z } u \mbox{ do } v,\\
\min\{w(p): p \mbox{ ścieżka o długości } \le m \mbox{ z } u \mbox{ do } v\}, & \mbox{jeżeli istnieje ścieżka o długości } \le m \mbox{ z } u \mbox{ do } v,\\
\infty & \mbox{w przeciwnym przypadku.}
\infty & \mbox{w przeciwnym przypadku.}
\end{cases}
\end{cases}
</math></center>
</math>}}
}}
}}


{{dowód|||3= Tezę udowodnimy przez indukcję po <math>m</math>.
{{dowod|||3= Tezę 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>.
Dla danego wierzchołka istnieją dla niech tylko ścieżki długości
zero prowadzące do niego samego, 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
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>.
<math>W^{m+1} = W \times_{\min} W^{m}</math>. Zauważmy, że ze
definicji [[#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>.


}}
}}




Zajmiemy się teraz konstrukcją algorytmu obliczającego najkrótsze
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.
ścieżki w grafie. W tym celu będziemy potrzebowali jeszcze udowodnić
następujące dwa lematy.


{{lemat|lemat_6|6|3=
{{lemat|6|lemat_6|3=
Jeżeli w grafie <math>G=(V,E)</math>, w którym wagi krawędzi zadane
Jeżeli w grafie <math>G=(V,E)</math>, w którym wagi krawędzi zadane są macierzą <math>W</math> nie istnieje cykl o ujemnej wadze to:
są macierzą <math>W</math> nie istnieje cykl o ujemnej wadze
to:




<center><math>
{{wzor2|1=<math>
\delta(u,v) = W^{(k)}_{u,v}, \mbox{ dla } k \ge n-1.
\delta(u,v) = W^{(k)}_{u,v}, \mbox{ dla } k \ge n-1.
</math></center>
</math>}}


}}
}}


{{dowód|||3= Jeżeli w grafie nie istnieje cykl o ujemnej wadze, to
{{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>. }}
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 \le n-1</math>. }}


Zauważmy, że iloczyn odległości dwóch macierzy
=== Algorytm ===
możemy policzyć w czasie <math>O(|V|^3)</math> wykorzystując następujący
 
Zauważmy, że iloczyn odległości dwóch macierzy możemy policzyć w czasie <math>O(|V|^3)</math> wykorzystując następujący
algorytm.
algorytm.




{{algorytm|algorytm_iloczyn_odległości|Mnożenia macierzy odległości
{{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'''
   3    '''for''' <math>j = 0</math> '''to''' <math>n-1</math> '''do'''
   3    '''for''' <math>j = 0</math> '''to''' <math>n-1</math> '''do'''
   4      <math>e_{i,j} = \infty</math>
   4   '''begin'''
   5     '''for''' <math>k =0</math> '''to''' <math>n-1</math> '''do'''
  5     <math>e_{i,j} = \infty</math>
   6       e_{i,j} = \min(c_{i,k} + d_{k,j}, e_{i,j})
   6     '''for''' <math>k =0</math> '''to''' <math>n-1</math> '''do'''
   7 '''return''' D'
   7       <math>e_{i,j} = \min(c_{i,k} + d_{k,j}, e_{i,j})</math>
   8    '''end'''
  9 '''return''' E
}}
}}


Ponieważ operacja iloczynu odległości jest łączna to możemy
Ponieważ operacja iloczynu odległości jest łączna, możemy wykorzystać [[algorytm_szybkiego_potęgowania|algorytm szybkiego
wykorzystać [[algorytm_szybkiego_potęgowania|algorytm szybkiego
potęgowania]] i policzyć odległości przy pomocy następującego algorytmu.
potęgowania]] i policzyć odległości przy pomocy następującego
algorytmu.


{{algorytm|algorytm_apsp_mnozenie|Algorytm obliczania odległości
{{algorytm|Algorytm obliczania odległości między wszystkimi parami wierzchołków I|algorytm_apsp_mnozenie|3=
między wszystkimi parami wierzchołków I|3=
   ODLEGŁÓŚCI-I<math>(W)</math>
   ODLEGŁÓŚCI-I(W)
   1  <math>D = W</math>
   1  <math>D = W</math>,
   2  <math>m = 1</math>
   2  <math>k = 1</math>
   3 '''while''' <math>n-1 > m</math> '''do'''
   3 '''while''' <math>n-1 > k</math> '''do'''
   4 '''begin'''
   4  <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>
   <math>m = 2m</math>
   6    <math>m = 2m</math>
   7  '''return''' <math>D</math>
   7 '''end'''
  8 '''return''' <math>D</math>
}}
}}


Poprawności tego algorytmu wynika wprost z [[lemat_6|Lematu 6]] ponieważ
Poprawność tego algorytmu wynika wprost z [[#lemat_6|Lematu 6]] ponieważ na zakończenie algorytmu <math>D = W^{m}</math> i <math>m > n-1</math>.
na zakończenie algorytmu <math>D = W^{m}</math> i <math>m > n-1</math>.
 


== Algorytm Floyda-Warshalla ==
== Algorytm Floyda-Warshalla ==


W algorytmie Floyda-Warshalla wykorzystamy inną cechę najkrótszych
W algorytmie Floyda-Warshalla wykorzystamy inną cechę najkrótszych ścieżek niż ta użyta w algorytmie z wykorzystaniem
ścieżek niż ta użyta w algorytmie z wykorzystaniem
[[#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_wewnetrzny|'''wewnetrznym'''} ś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 =
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>.
\{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>\{v_1, \ldots,v_{k}</math>. Pokażemy następujący
rekurencyjny wzór na <math>D^{(k)}</math>.


{{lemat|lemat_7|7|3=
{{lemat|7|lemat_7|3=
Dla <math>k=0,\ldots, n</math> zachodzi:
Dla <math>k=0,\ldots, n</math> zachodzi:


 
{{wzor|wzor_2|2|3=
<center><math>
<math>
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></center>
</math>
 
}}
}}
}}


{{dowod|||3= Dla <math>k=0</math> poprawność powyższego wzoru wynika bezpośrednio z definicji <math>D^0</math>. Dla <math>k>0</math> musimy pokazać, że


{{dowód|||3= Dla <math>k=0</math> poprawność powyższego wzoru wynika
bezpośrednio z definicji <math>D^0</math>. Dla <math>k>0</math>
musimy pokazać, że


{{wzor|wzor_2|2
{{wzor2|1=<math>
<center><math>
d_{i,j}^{(k)} = \min( d_{i,j}^{(k-1)}, d_{i,k}^{(k-1)} + d_{k,j}^{(k-1)}).
d_{i,j}^{(k)} = \min( d_{i,j}^{(k-1)}, d_{i,k}^{(k-1)} + d_{k,j}^{(k-1)}).
</math></center>
</math>}}
}}
 


Niech <math>p</math> będzie najkrótszą ścieżką z <math>i</math> do <math>j</math>,
Niech <math>p</math> będzie najkrótszą ścieżką z <math>i</math> do <math>j</math>, której wierzchołki wewnętrzne należą do zbioru <math>\{v_1,\ldots,v_k\}</math>. Mamy dwa przypadki:
której wierzchołki wewnętrzne należą do zbioru <math>\{v_1,\ldots,v_k\}</math>. Mamy dwa przypadki:
* Wierzchołek <math>v_k</math> nie leży na ścieżce <math>p</math>. Wtedy zachodzi <math>d_{i,j}^{(k)} = p(w) = d_{i,j}^{(k-1)}</math>. Ponieważ <math>p</math> jest najkrótszą ścieżką, to także <math>p(w) \le d_{i,k}^{(k-1)} + d_{k,j}^{(k-1)}</math> i powyższy wzór zachodzi.
* Wierzchołek <math>v_k</math> nie leży na ścieżce <math>p</math>. Wtedy zachodzi
* Jeżeli wierzchołek <math>v_k</math> należy do ścieżki <math>p</math>, to występuje on dokładnie raz i możemy podzielić <math>p</math> na dwie ścieżki <math>p_1</math> z <math>i</math> do <math>k</math> oraz <math>p_2</math> z <math>k</math> do <math>j</math>. Ścieżki <math>p_1</math> i <math>p_2</math> nie zawierają wierzchołka <math>v_k</math> 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 <math>w(p_1) = d_{i,k}^{(k-1)}</math> oraz <math>w(p_2) = d_{k,j}^{(k-1)}</math>. Otrzymujemy więc <math>d_{i,j}^{(k)} = w(p) = w(p_1) + w(p_2) = d_{i,k}^{(k-1)} + d_{k,j}^{(k-1)}</math>. Ponieważ <math>p</math> jest najkrótszą ścieżką, <math>p(w) \le d_{i,j}^{(k-1)}</math> i wzór zachodzi także w tym przypadku.
<math>d_{i,j}^{(k)} = p(w) = d_{i,j}^{(k-1)}</math>. Ponieważ <math>p</math> jest najkrótszą
ścieżką to także <math>p(w) \le d_{i,k}^{(k-1)} + d_{k,j}^{(k-1)}</math> i powyższy wzór zachodzi.
* Jeżeli wierzchołek <math>v_k</math> należy do ścieżki <math>p</math>, to występuje
on dokładnie raz i możemy podzielić <math>p</math> na dwie ścieżki
<math>p_1</math> z <math>i</math> do <math>k</math> oraz
<math>p_2</math> z <math>k</math> do <math>j</math>. Ścieżki
<math>p_1</math> i <math>p_2</math> nie zawierają wierzchołka
<math>v_k</math> 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 <math>w(p_1) = d_{i,k}^{(k-1)}</math> oraz
<math>w(p_2) = d_{k,j}^{(k-1)}</math>. Otrzymujemy więc
<math>d_{i,j}^{(k)} = w(p) = w(p_1) + w(p_2) = d_{i,k}^{(k-1)} +
d_{k,j}^{(k-1)}</math>. Ponieważ <math>p</math> jest najkrótszą ścieżką
to <math>p(w) \le d_{i,j}^{(k-1)}</math> i wzór zachodzi także w tym przypadku.
<!-- TODO: może  jakiś rysunek -->
<!-- TODO: może  jakiś rysunek -->
}}
}}


Wykorzystując [[#wzor_2| wzór (2)]] możemy skonstruować następujący
Wykorzystując [[#wzor_2| wzór (2)]] możemy skonstruować następujący algorytm, obliczający w czasie <math>O(|V|^3)</math> odległości między wszystkimi parami wierzchołków.
algorytm obliczający w czasie <math>O(|V|^3)</math> odległości
między wszystkimi parami wierzchołków.


{{algorytm|algorytm_Floyda-Warshalla|Algorytm Floyda-Warshalla|3=
{{algorytm|Algorytm Floyda-Warshalla|algorytm_Floyda-Warshalla|3=
między wszystkimi parami wierzchołków I
   ODLEGŁÓŚCI-II<math>(W)</math>
   ODLEGŁÓŚCI-II(W)
   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 357: Linia 250:
   6  '''return''' <math>D^{(n)}</math>
   6  '''return''' <math>D^{(n)}</math>
}}
}}
== 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 [[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)</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>}}
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>.
{{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=
<math>w_h(p) = w(p) + h(v_0) - h(v_k)</math>
}}
}}
{{dowod|||3=Mamy:
{{wzor2|1=
<math>w_h(p) = \sum_{i=0}^{k-1} w_h(v_i, v_{i+1}) =</math>
}}
{{wzor2|1=
<math>= \sum_{i=0}^{k-1} \left(w(v_i, v_{i+1}) + h(v_i) - h(v_{i+1}) \right) =</math>
}}
{{wzor2|1=
<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=
<math>= w(p) + h(v_0) - h(v_k)</math>
}}
}}
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_9|3=Dla funkcji <math>w</math>, <math>w_h</math>, oraz dowolnej pary wierzchołków zachodzi:
{{wzor2|1=
<math>
\delta_h(u,v) = \delta(u,v) + h(u) - h(v).
</math>
}}
}}
{{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>.
}}
Funkcję <math>h</math> nazwiemy {{kotwica|potencjał|'''potencjałem'''}} jeżeli zachodzi:
{{wzor|wzor_4|4|3=
<math>
w_h(u,v) \ge 0, \mbox{ dla } (u,v) \in E.
</math>
}}
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=
  OBLICZ-POTENCJAŁ<math>(G=(V,E), w)</math>
  1  <math>s</math> nowy wierzchołek
  2  <math>V' = V \cup \{s\}</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 & \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>
  6  '''if''' <math>(d,\pi) = NIL</math> '''then'''
  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)''
}}
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>. }}
{{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 uruchamiany dla <math>G'</math> i wierzchołka <math>s</math> zwraca <math>NIL</math> wtedy i tylko wtedy, gdy <math>G</math> zawiera cykl o ujemnej wadze.
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ą:
<center><math>
d(v) \le d(u) + w(u,v).
</math></center>
Innymi słowy:
<center><math>
w_d(u,v) = w(u,v) + d(u) - d(v) \ge 0</math>,</center>
co kończy dowód lematu.
}}
Powyższy lemat jest ostatnim składnikiem potrzebnym nam do konstrukcji algorytmu Johnsona.
{{algorytm|Algorytm Johnsona|algorytm_johnsona|3=
  JOHNSON<math>(G=(E,V),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>
  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>
  5  '''for''' każdy wierzchołek <math>u \in V</math> '''do'''
  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'''
  8      <math>d(u,v) = d_h(v) + h(v) - h(u)</math>
  9  '''return''' <math>D</math>
}}
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>.

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 G=(V,E). Przedstawimy trzy algorytmy rozwiązujące ten problem:

  • algorytm wykorzystujący mnożenie macierzy, działający w czasie O(|V|3log|V|),
  • algorytm Floyda-Warshalla, działający w czasie O(|V|3),
  • algorytm Johnsona, działający w czasie O(|V|2log|V|+|V||E|).

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 |V| 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 O(|V|log|V|+|E|), co daje nam algorytm rozwiązujący problem policzenia odległości między wszystkimi parami wierzchołków, działający w czasie O(|V|2log|V|+|V||E|).

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 O(|V|2|E|). 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 W rozmiaru n×n reprezentującą wagi krawędzi n-wierzchołkowego grafu G=(V,E). Dla macierzy tej zachodzi:


wi,j={0,jeśli i=j,waga krawędzi (i,j),jeśli ij i (i,j)E,,jeśli ij i (i,j)E.


W problemie najkrótszych ścieżek między wszystkimi parami wierzchołków chcemy wyznaczyć macierz D rozmiaru n×n, taką że di,j jest równe odległości δ(i,j) z wierzchołka i do wierzchołka j. Chcemy także wyznaczyć dla każdego wierzchołka v drzewo najkrótszych ścieżek Tv ukorzenione w v. Podobnie jak w poprzednim rozdziale drzewo Tv możemy kodować dla każdego wierzchołka przy pomocy funkcji poprzedników πv. Ponieważ tutaj interesuje nas wiele drzew, łatwiej będzie nam używać macierzy poprzedników Π. Macierz tą definiujemy używając funkcji πv w następujący sposób:


Πv,u=πv(u).


W pozostałej części tego wykładu zajmiemy się tylko wyznaczaniem macierzy odległości D. 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 O(|E|). Tak więc |V| drzew możemy wyliczyć w czasie O(|E||V|). 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 D.


Co więcej, będziemy zakładać, że w grafie nie ma ujemnych cykli. Ujemne cykle można wykryć w czasie O(|V||E|) 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 C oraz D rozmiaru n×n. Dla macierzy tych definiujemy operację ×min iloczyn odległości, której wynikiem jest także macierz rozmiaru n×n, zdefiniowana jako:

(C×minD)i,j=mink=1,,nCi,k+Dk,j.      (1)


Wniosek 1

Jeżeli założymy, że C i D opisują minimalne wagi zbioru ścieżek odpowiednio ΠC i ΠD w pewnym grafie G, to iloczyn odległości wyznacza minimalne wagi zbioru ścieżek powstałego z konkatenacji ścieżek ze zbioru ΠC ze ścieżkami ze zbioru ΠD.

Wniosek ten jest przedstawiony na poniższej animicji, gdzie zostały zaznaczone dwa zbiory ścieżek, pierwszy, zaznaczony na zielono, reprezentowany przez macierz A oraz graf GA, oraz drugi, zaznaczony na niebiesko, reprezentowany przez macierz B oraz graf GB. W wyniku wykonania mnożenia otrzymujemy macierz C oraz graf GC

Plik:Zasd ilustr f.svg

Pokażemy teraz, że produkt odległości jest operacją łączną.

Lemat 2

Dla macierzy C, D i E rozmiaru n×n zachodzi:
(C×minD)×minE=C×min(D×minE)

Dowód

Powyższa równość wynika wprost z wzoru (1) oraz przemienności operacji min.

Co więcej, produkt odległości jest przemienny względem dodawania.

Lemat 3

Dla macierzy C, D i E rozmiaru n×n zachodzi:


C×min(D+E)=C×minD+C×minE


oraz


(D+E)×minC=D×minC+E×minC

Dowód

Te dwie równości wynikają ponownie z wzoru (1) oraz przemienności operacji min względem dodawania.


Zdefiniujmy macierz Imin rozmiaru n×n jako:


(Imin)i,j={0,jeśli i=j,,jeśli ij.


Macierz ta jest jedynką dla iloczynu odległości.


Lemat 4

Dla macierzy C rozmiaru n×n zachodzi:


Imin×minC=C×minImin=C

Dowód

Mamy
(Imin×minC)i,j=mink=1,,n(Imin)i,k+Ck,j=

ponieważ wszystkie elementy (Imin)i,k równe są oprócz elementu i=k, możemy je pominąć w operacji min i:


=mink=i(Imin)i,k+Ck,j=Ii,i+Ci,j=Ci,j

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 O(|V|3log|V|). Niech W będzie macierzą wag grafu G=(V,E). Rozważmy macierz Wm zdefiniowaną jako:


Wm={Imin,jeżeli m=0,W×minWm1,jeżeli m>0.

Pokażemy teraz, że macierz Wm opisuje odległości między wierzchołkami grafu, ale tylko dla ścieżek używających nie więcej niż m krawędzi.

Lemat 5

Element Wi,jm macierzy Wm zadaje najmniejszą wagę ścieżki, wychodzącej z wierzchołka i do wierzchołka j spośród ścieżek, które zawierają nie więcej niż m krawędzi, tzn.:


wi,jm={min{w(p):p ścieżka o długości m z u do v},jeżeli istnieje ścieżka o długości m z u do v,w przeciwnym przypadku.

Dowód

Tezę tę udowodnimy przez indukcję po m. Ścieżki używające 0 krawędzi istnieją tylko z każdego wierzchołka do niego samego i mają długość 0, a więc dla m=0 teza wynika wprost z definicji macierzy W0. Załóżmy teraz, że teza zachodzi dla m>0. Mamy wtedy Wm+1=W×minWm. Zauważmy, że definicja macierzy wag W opisuje ścieżki używające 1 krawędź. Korzystając teraz z Wniosku 1 otrzymujemy tezę dla m+1.


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

Jeżeli w grafie G=(V,E), w którym wagi krawędzi zadane są macierzą W nie istnieje cykl o ujemnej wadze to:


δ(u,v)=Wu,v(k), dla kn1.

Dowód

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 n1. Z Lematu 5 wynika więc, że odległości tych ścieżek są dobrze wyznaczone w Wk dla kn1.

Algorytm

Zauważmy, że iloczyn odległości dwóch macierzy możemy policzyć w czasie O(|V|3) wykorzystując następujący algorytm.


Algorytm Mnożenia macierzy odległości


 MNOŻENIE-ODLEGŁOŚCI(C,D)
 1  E macierz rozmiaru n×n
 2  for i=0 to n1 do
 3    for j=0 to n1 do
 4    begin
 5      ei,j=
 6      for k=0 to n1 do
 7        ei,j=min(ci,k+dk,j,ei,j)
 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(W)
 1  D=W
 2  m=1
 3  while n1>m do
 4  begin
 5    D=MNOŻENIE-ODLEGŁOŚCI(D,D)
 6    m=2m
 7  end
 8  return D

Poprawność tego algorytmu wynika wprost z Lematu 6 ponieważ na zakończenie algorytmu D=Wm i m>n1.

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 p=(v0,,vl) jest każdy wierzchołek na ścieżce p różny od jej początku v0 i końca vl.

Niech zbiorem wierzchołków grafu G będzie V={1,,n}. Niech di,j(k) dla k=0,,n oznacza najmniejszą wagę ścieżki z i do j spośród ścieżek, których wierzchołki wewnętrzne należą do zbioru {1,,k}. Pokażemy następujący rekurencyjny wzór na D(k).

Lemat 7

Dla k=0,,n zachodzi:

di,j(k)={wi,j,jeżeli k=0,min(di,j(k1),di,k(k1)+dk,j(k1)),jeżeli k1.      (2)

Dowód

Dla k=0 poprawność powyższego wzoru wynika bezpośrednio z definicji D0. Dla k>0 musimy pokazać, że


di,j(k)=min(di,j(k1),di,k(k1)+dk,j(k1)).


Niech p będzie najkrótszą ścieżką z i do j, której wierzchołki wewnętrzne należą do zbioru {v1,,vk}. Mamy dwa przypadki:

  • Wierzchołek vk nie leży na ścieżce p. Wtedy zachodzi di,j(k)=p(w)=di,j(k1). Ponieważ p jest najkrótszą ścieżką, to także p(w)di,k(k1)+dk,j(k1) i powyższy wzór zachodzi.
  • Jeżeli wierzchołek vk należy do ścieżki p, to występuje on dokładnie raz i możemy podzielić p na dwie ścieżki p1 z i do k oraz p2 z k do j. Ścieżki p1 i p2 nie zawierają wierzchołka vk 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 w(p1)=di,k(k1) oraz w(p2)=dk,j(k1). Otrzymujemy więc di,j(k)=w(p)=w(p1)+w(p2)=di,k(k1)+dk,j(k1). Ponieważ p jest najkrótszą ścieżką, p(w)di,j(k1) 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 O(|V|3) odległości między wszystkimi parami wierzchołków.

Algorytm Algorytm Floyda-Warshalla


 ODLEGŁÓŚCI-II(W)
 1  D(0)=W,
 2  for k=1 to n do
 3    for i=1 to n do
 4      for j=1 to n do
 5        di,j(k)=min(di,j(k1),di,k(k1)+dk,j(k1))
 6  return D(n)


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 O(|V|2log|V|+|V||E|). 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 G=(V,E) wraz z funkcją wagową w:E. Niech funkcja h:V będzie dowolną funkcją ze zbioru wierzchołków w liczby rzeczywiste. Dla funkcji h oraz w definiujmy nową funkcję wagową oznaczoną wh w następujący sposób:

wh(u,v)=w(u,v)+h(u)h(v)      (3)

Oznaczmy, przez δ(u,v) odległości w grafie G dla funkcji wagowej w oraz przez δh(u,v) odległości w grafie G dla funkcji wagowej wh.

Lemat 8

Dla funkcji w, wh oraz dowolnej ścieżki p=(v0,,vk) zachodzi:
wh(p)=w(p)+h(v0)h(vk)

Dowód

Mamy:


wh(p)=i=0k1wh(vi,vi+1)=
=i=0k1(w(vi,vi+1)+h(vi)h(vi+1))=
=i=0k1w(vi,vi+1)+i=0k1h(vi)i=0k1h(vi+1)=
=w(p)+h(v0)h(vk)


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

Dla funkcji w, wh, oraz dowolnej pary wierzchołków zachodzi:
δh(u,v)=δ(u,v)+h(u)h(v).


Wniosek 10

Ścieżka p z wierzchołka u do wierzchołka v jest najkrótszą ścieżką w G=(V,E) dla funkcji wagową w wtedy i tylko wtedy, gdy jest najkrótszą ścieżką w G dla funkcji wagowej wh.

Dowód

Wniosek ten wynika wprost z Lematu 8 i Wniosku 9, który pokazuje, że odległości transformują się tak samo jak wagi ścieżek, a więc równość w(p)=δ(u,v) jest równoważna równości wh(p)=δh(u,v).


Funkcję h nazwiemy potencjałem jeżeli zachodzi:

wh(u,v)0, dla (u,v)E.      (4)


Pokażemy teraz, jak wyznaczyć funkcję potencjału h dla grafu G. Rozważmy następujący algorytm:

Algorytm Algorytm obliczania potencjału


 OBLICZ-POTENCJAŁ(G=(V,E),w)
 1  s nowy wierzchołek
 2  V=V{s}
 3  E=E{(s,v):vV}
 4  w(u,v)={w(u,v),jeżeli (u,v)E0jeżeli u=s0w pozostałych przypadkach
 5  (d,π)=BELLMAN-FORD(G,w,s)
 6  if (d,π)=NIL then
 7    return NIL (graf zawiera cykl ujemnej długości)
 8  return d (d(v) obliczone w algorytmie Bellmana-Forda jest potencjałem)

Działanie tego algorytmu przedstawione jest na poniższej animacji.

Plik:Zasd ilustr c.svg

Lemat 11

Jeżeli graf G=(V,E) z wagami krawędzi zadanymi funkcją w zawiera cykl o ujemnej wadze, to algorytm OBLICZ-POTENCJAŁ zwraca wartość NIL. Natomiast w przeciwnym wypadku algorytm zwraca potencjał d(v) dla grafu G z funkcją wagową w.

Dowód

Zauważmy, że dodanie wierzchołka s do grafu G nie wprowadza nowych cykli, a jedynie powoduje, że wszystkie wierzchołki są osiągalne z s. Dlatego algorytm Bellmana-Forda uruchamiany dla G i wierzchołka s zwraca NIL wtedy i tylko wtedy, gdy G zawiera cykl o ujemnej wadze.

Zakładamy teraz, że graf G nie zawiera cyklu o ujemnej wadze, wtedy wartości d(v) jako odległości w grafie spełniają:


d(v)d(u)+w(u,v).


Innymi słowy:


wd(u,v)=w(u,v)+d(u)d(v)0,


co kończy dowód lematu.


Powyższy lemat jest ostatnim składnikiem potrzebnym nam do konstrukcji algorytmu Johnsona.

Algorytm Algorytm Johnsona


 JOHNSON(G=(E,V),w)
 1  d=OBLICZ-POTENCJAŁ(G,w)
 1  if d=NIL then return NIL
 3  for każda krawędź (u,v)E do
 4     wd(u,v)=w(u,v)+d(u)d(v)
 5  for każdy wierzchołek uV do
 6    (dh,π) = DIJKSTRA(G,wh,u)
 7    for każdy wierzchołek vV do
 8      d(u,v)=dh(v)+h(v)h(u)
 9  return D

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 O(|V|) wywołań algorytmu Dijkstry, co daje całkowity czas O(|V||E|+|V|2log|V|).