Zaawansowane algorytmy i struktury danych/Wykład 10: Różnice pomiędzy wersjami
m Zastępowanie tekstu – „.</math>” na „</math>” |
|||
(Nie pokazano 21 wersji utworzonych przez 3 użytkowników) | |||
Linia 1: | Linia 1: | ||
== Abstrakt == | == Abstrakt == | ||
W wykładzie tym przedstawimy trzy algorytmy znajdowania przepływu w grafie. | W wykładzie tym przedstawimy trzy algorytmy znajdowania przepływu w grafie. Pierwszym będzie algorytm Edmondsa-Karpa działający w czasie <math>O(nm^2)</math>. Następnym będzie algorytm Dinica działający w czasie <math>O(n^2 m)</math>, oraz trzecim tak zwany algorytm trzech Hindusów, działający w czasie <math>O(n^3)</math>. Nazwiska tych tytułowych Hindusów to Malhotra, Kumar i Maheshwari. Dwa ostatnie algorytmy oparte będą na konstrukcji przepływów blokujących, które są analogiczną konstrukcją do konstrukcji maksymalnego zbioru rozłącznych ścieżek, której użyliśmy w [[../Wykład 7#algorytm_hopcrofta-karpa|algorytmie Hopcrofta-Karpa]]. | ||
Pierwszym będzie algorytm Edmondsa-Karpa działający w czasie <math>O(nm^2)</math>. Następnym będzie algorytm Dinica działający w czasie <math>O(n^2 m)</math>, oraz trzecim tak zwany algorytm trzech Hindusów działający w czasie <math>O(n^3)</math>. Nazwiska tych tytułowych Hindusów to Malhotra, Kumar i Maheshwari. | |||
== Algorytm Edmonds’a-Karp’a == | == Algorytm Edmonds’a-Karp’a == | ||
Algorytm Edmonds'a-Karp'a to algorytm Forda-Fulkersona w, którym zamiast dowolnej ścieżki powiększającej wybieramy zawsze najkrótszą ścieżkę powiększającą. Zakładamy tutaj, że wszystkie krawędzie mają jednostkowe długości. Taka modyfikacja pozwala poprawić ograniczenie w czasie działania tego algorytmu. Udowodnimy, że algorytm Edmonds’a–Karp’a działa w czasie <math>O(nm^2)</math>. W naszej analizie będziemy korzystać z zapisu <math>d_f (u, v)</math> dla odległości z <math>u</math> do <math>v</math> w <math>G_f</math>, przy założenie, że każda krawędź ma jednostkową wagę. | |||
{{lemat|1|lemat_1|3= | {{lemat|1|lemat_1|3= | ||
Jeśli algorytm | Jeśli algorytm Edmondsa–Karpa działa w sieci przepływowej <math>G = (V, E)</math> ze źródłem <math>s</math> i ujściem <math>t</math>, to wtedy dla wszystkich wierzchołków <math>v \in V - \{s, t\}</math>, odległość <math>d_f (s, v)</math> w sieci rezydualnej <math>G_f</math> nie maleje. | ||
}} | }} | ||
{{Dowod|||3=Przypuśćmy, że dla pewnego wierzchołka <math>v \in V - \{s, t\}</math> | {{Dowod|||3=Przypuśćmy, że dla pewnego wierzchołka <math>v \in V - \{s, t\}</math> istnieje powiększający przepływ, który powoduje zmniejszenie odległości najkrótszej ścieżki z <math>s</math> do <math>v</math>, a następnie otrzymamy wynik sprzeczny z tym założeniem. Niech <math>f</math> będzie przepływem zaraz przed pierwszym powiększeniem, które skraca długość najkrótszej ścieżki i niech <math>f'</math> będzie przepływem następującym zaraz potem. Niech <math>v</math> będzie wierzchołkiem o minimalnym <math>d_{f'}(s, v)</math>, którego dystans został zmniejszony poprzez to powiększenie tak, że <math>d_{f'}(s, v) < d_f(s, v)</math>. Niech <math>p = s \to u \to v</math> będzie najkrótszą ścieżką z <math>s</math> do <math>v</math> w <math>G_{f'}</math>, tak że <math>(u, v) \in E_{f'}</math> oraz: | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>d_{f'}(s,u) = d_{f'}(s,v) -1 | <math>d_{f'}(s,u) = d_{f'}(s,v) -1</math> | ||
}} | }} | ||
Ze względu na sposób wybrania <math>v</math>, wiemy | Ze względu na sposób wybrania <math>v</math>, wiemy że odległość z wierzchołka <math>u</math> się nie zmniejszyła, to znaczy: | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>d_{f'}(s,u) \ge d_{f}(s,u) | <math>d_{f'}(s,u) \ge d_{f}(s,u)</math> | ||
}} | }} | ||
Twierdzimy, że <math>(u, v) \notin E_f</math>. Dlaczego? Gdybyśmy mieli <math>(u, v) \in E_f</math>, wówczas | Twierdzimy, że <math>(u, v) \notin E_f</math>. Dlaczego? Gdybyśmy mieli <math>(u, v) \in E_f</math>, wówczas z nierówności trójkąta dla <math>s,v</math> i <math>u</math> oraz powyższych nierówności wynikałoby: | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>d_f(s, v) \le d_f(s, u) + 1 \le d_{f'} (s, u) + 1 \le d_{f'} (s, v) | <math>d_f(s, v) \le d_f(s, u) + 1 \le d_{f'} (s, u) + 1 \le d_{f'} (s, v)</math> | ||
}} | }} | ||
Co jest | Co jest sprzeczne z założeniem, że <math>d_{f'}(s, v) < d_f(s, v)</math>. Jak możemy zatem otrzymać <math>(u, v) \notin E_f</math> i <math>(u, v) \in E_{f'}</math>? Powiększeniu przepływu z <math>f</math> do <math>f'</math> powinno także powiększyć przepływ z <math>v</math> do <math>u</math>. Algorytm Edmondsa–Karpa zawsze powiększa przepływ wzdłuż najkrótszych ścieżek i dlatego też najkrótsza ścieżka z <math>s</math> do <math>u</math> w <math>G_f</math> posiada <math>(v, u)</math> jako ostatnią krawędź. Dlatego mamy: | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>d_f(s, v) = d_f(s, u) | <math>d_f(s, v) = d_f (s, u) - 1 = d_{f'} (s, u) - 2 = d_{f'} (s, v) - 2</math> | ||
}} | }} | ||
co jest sprzeczne z | co jest sprzeczne z założeniem, że <math>d_{f'} (s, v) < d_f(s, v)</math>. Wnioskujemy zatem, że założenie, iż taki wierzchołek <math>v</math> istnieje, jest nieprawdziwe. | ||
}} | }} | ||
Następujące twierdzenie ogranicza liczbę iteracji algorytmu Edmondsa–Karpa. | |||
{{twierdzenie|2|twierdzenie_2|3= | {{twierdzenie|2|twierdzenie_2|3= | ||
Jeśli algorytm | Jeśli algorytm Edmondsa–Karpa działa w sieci przepływowej <math>G = (V, E)</math> ze źródłem <math>s</math> i ujściem <math>t</math>, wówczas całkowita liczba przepływów powiększających znalezionych w algorytmie wynosi <math>O(V E)</math>. | ||
}} | }} | ||
{{dowod|||3= | {{dowod|||3= | ||
Mówimy, że krawędź <math>(u, v)</math> w sieci rezydualnej <math>G_f</math> jest '''krytyczna''' na ścieżce powiększającej <math>p</math> jeśli przepustowość rezydualna <math>p</math> jest przepustowością rezydualną <math>(u, v)</math>, to znaczy jeśli <math>c_f(p) = c_f(u, v)</math>. Po tym jak otrzymamy powiększający przepływ wzdłuż ścieżki powiększającej, każda krawędź krytyczna na ścieżce znika z sieci rezydualnej. Ponadto | Mówimy, że krawędź <math>(u, v)</math> w sieci rezydualnej <math>G_f</math> jest '''krytyczna''' na ścieżce powiększającej <math>p</math>, jeśli przepustowość rezydualna <math>p</math> jest przepustowością rezydualną <math>(u, v)</math>, to znaczy jeśli <math>c_f(p) = c_f(u, v)</math>. Po tym, jak otrzymamy powiększający przepływ wzdłuż ścieżki powiększającej, każda krawędź krytyczna na ścieżce znika z sieci rezydualnej. Ponadto co najmniej jedna krawędź na dowolnej ścieżce musi być krytyczna. Pokażemy, że każda z <math>|E|</math>krawędzi może stać się krytyczna co najwyżej <math>|V|/2 - 1</math> razy. | ||
Niech <math>u</math> i <math>v</math> będą wierzchołkami w <math>V</math> połączonymi krawędzią <math>E</math>. Ponieważ ścieżki powiększające są | Niech <math>u</math> i <math>v</math> będą wierzchołkami w <math>V</math> połączonymi krawędzią <math>E</math>. Ponieważ ścieżki powiększające są najkrótszymi ścieżkami, to dla kawędzi krytycznej <math>(u,v)</math>, otrzymujemy | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>d_f(s, v) = d_f (s, u) + 1 | <math>d_f(s, v) = d_f (s, u) + 1</math> | ||
}} | }} | ||
Gdy tylko przepływ jest zwiększony, krawędź <math>(u, v)</math> znika z sieci rezydualnej. Nie może ona się znów pojawić na żadnej innej ścieżce powiększającej dopóki przepływ z <math>u</math> do <math>v</math> nie będzie zmniejszony, a nastąpi to tylko wtedy, kiedy <math>(v, u)</math> pojawi się na ścieżce powiększającej. Jeśli <math>f'</math> jest przepływem w <math>G</math> i to zdarzenie ma miejsce, wówczas mamy: | |||
{{wzor2|1= | {{wzor2|1= | ||
<math>d_{f'}(s, u) = d_{f'}(s, v) + 1 | <math>d_{f'}(s, u) = d_{f'}(s, v) + 1</math> | ||
}} | }} | ||
Ponieważ <math>d_f(s, v) | Ponieważ <math>d_f(s, v) \le d_{f'}(s, v)</math>, co wynika z [[#lemat_1|lematu 1]], otrzymujemy | ||
{{wzor2|1= | {{wzor2|1= | ||
<math>d_{f'}(s, u) = d_{f'}(s, v) + 1 | <math>d_{f'}(s, u) = d_{f'}(s, v) + 1 \ge d_f(s, v) + 1 = d_f(s, u) + 2</math> | ||
}} | }} | ||
Czyli | Czyli od czasu, kiedy <math>(u, v)</math> stało sie krytyczne, do czasu kiedy ponownie stanie się krytyczne, dystans ze źródła do <math>u</math> zwiększa się o co najmniej <math>2</math>. Dystans do <math>u</math> wynosi początkowo co najmniej <math>0</math>. Wierzchołki pośrednie na najkrótszej ścieżce z <math>s</math> do <math>u</math> nie mogą zawierać <math>s</math>, <math>u</math> ani <math>t</math> (ponieważ to, że <math>(u, v)</math> jest krytyczna oznacza, że <math>u \neq t</math>). Dlatego też odległość do <math>u</math> może wynosić co najwyżej <math>|V| - 2</math>. Stąd <math>(u, v)</math> może stać sie krytyczne co najwyżej <math>(|V|-2)/2 = |V|/2-1</math> razy. Ponieważ istnieje <math>O(|E|)</math> par wierzchołków pomiędzy którymi może istnieć krawędź w grafie rezydualnym, to całkowita liczba krawędzi krytycznych podczas działania algorytmu Edmondsa–Karpa wynosi <math>O(|V| |E|)</math>, bo każda ścieżka powiększająca ma co najmniej jedną krawędź krytyczną. | ||
}} | }} | ||
Ponieważ każdą | Ponieważ każdą iterację algorytmu FORD-FULKERSON można zaimplementować w czasie <math>O(|E|)</math>, to całkowity czas działania algorytmu Edmondsa-Karpa wynosi <math>O(|V| |E|^2)</math>. W następnych częściach wykładu pokażemy, jak wykorzystując przepływy blokujące poprawić ten wynik do czasu <math>O(|V|^3)</math>. | ||
== Przepływ blokujący == | == Przepływ blokujący == | ||
'''Przepływem blokującym''' w sieci rezydualnej <math>G_f</math> nazywamy taki przepływ <math>b</math> w <math>G_f</math>, że: | '''Przepływem blokującym''' w sieci rezydualnej <math>G_f</math> nazywamy taki przepływ <math>b</math> w <math>G_f</math>, że:{{kotwica|warunki_blokujace}} | ||
# każda ścieżka z <math>s</math> do <math>t</math> w <math>b</math> jest najkrótszą ścieżką w <math> | # każda ścieżka z <math>s</math> do <math>t</math> w <math>b</math> jest najkrótszą ścieżką w <math>G_f</math>, | ||
# oraz każda najkrótsza ścieżka w <math>G_f</math> zawiera krawędź nasyconą w <math>G_{f+b}</math>. | # oraz każda najkrótsza ścieżka w <math>G_f</math> zawiera krawędź nasyconą w <math>G_{f+b}</math>. | ||
Zauważ, że jest to definicja która odpowiada pojęciu maksymalnego zbioru najkrótszych ścieżek powiększających użytemu w [[../Wykład 7#max_sieżki|Wykładzie 7]]. Załóżmy | Zauważ, że jest to definicja, która odpowiada pojęciu maksymalnego zbioru najkrótszych ścieżek powiększających użytemu w [[../Wykład 7#max_sieżki|Wykładzie 7]]. Załóżmy na chwilę, że mamy algorytm znajdujący przepływ blokujący. Pokażemy jak go wykorzystać do znalezienia przepływu maksymalnego, jest to algorytm Dinica. Algorytmy na znajdowanie przepływu blokującego pokażemy w dalszej części tego wykładu. | ||
{{algorytm|[Dinica] znajduje przepływ maksymalny w grafie <math>G</math>|algorytm_Dinica| | {{algorytm|[Dinica] znajduje przepływ maksymalny w grafie <math>G</math>|algorytm_Dinica| | ||
3= | 3= | ||
DINIC(G, s, t) | DINIC<math>(G, s, t)</math> | ||
1 <math>f = 0</math> | 1 <math>f = 0</math> | ||
2 '''while''' istnieje ścieżka od <math>s</math> do <math>t</math> w <math>G_f</math> '''do''' | 2 '''while''' istnieje ścieżka od <math>s</math> do <math>t</math> w <math>G_f</math> '''do''' | ||
Linia 91: | Linia 90: | ||
}} | }} | ||
Poprawność algorytmu Dinica wynika bezpośrednio z [[../Wykład 9#twierdznie_5|twierdzenia o maksymalnym przepływie i minimalnym przekroju]], gdyż po zakończeniu algorytmu nie ma już w sieci <math>G</math> ścieżek powiększających. Zastanówmy się teraz ile razy może zostać wykonana pętla '''while''', czyli innymi słowy ile razy będzie konieczne | Poprawność algorytmu Dinica wynika bezpośrednio z [[../Wykład 9#twierdznie_5|twierdzenia o maksymalnym przepływie i minimalnym przekroju]], gdyż po zakończeniu algorytmu nie ma już w sieci <math>G</math> ścieżek powiększających. Zastanówmy się teraz, ile razy może zostać wykonana pętla '''while''', czyli innymi słowy ile razy będzie konieczne znalezienie przepływu blokującego. | ||
{{lemat|3|lemat 3|3= | {{lemat|3|lemat 3|3= | ||
Linia 98: | Linia 97: | ||
{{dowod|||3= | {{dowod|||3= | ||
Załóżmy, że długość najkrótszej ścieżki <math>p</math> w <math>G_{f+b}</math> jest nie większa niż długość najkrótszej ścieżki w <math>G_f</math>. Wtedy ścieżka <math>p</math> ma z przepływem blokującym <math>b</math> wspólną krawędź nasyconą. Niech <math>u-v</math> będzie ostatnią taką krawędzią na <math>p</math>. Oznacza to, że krawędź <math>v-u</math> musiała należeć do przepływu <math>b</math>. Inaczej w <math>G_{f+b}</math> <math>u-v</math> nadal byłaby nasycona. Ponieważ <math>b</math> może zostać rozłożone na sumę pewnych najkrótszych ścieżek w <math>G_f</math>, to z [[#lemat_1|lematu 1]], wiemy, że odległość z <math>s</math> do <math>u</math> nie zmalała, tzn. <math>d_{f}(s,u) \le d_{f+b}(s,u)</math>. Jednak ponieważ <math>p</math> jest najkrótszą ścieżką z <math>s</math> do <math>t</math>, to | Załóżmy, że długość najkrótszej ścieżki <math>p</math> w <math>G_{f+b}</math> jest nie większa niż długość najkrótszej ścieżki w <math>G_f</math>. Wtedy ścieżka <math>p</math> ma z przepływem blokującym <math>b</math> wspólną krawędź nasyconą. Niech <math>u-v</math> będzie ostatnią taką krawędzią na <math>p</math>. Oznacza to, że krawędź <math>v-u</math> musiała należeć do przepływu <math>b</math>. Inaczej w <math>G_{f+b}</math> krawędź <math>u-v</math> nadal byłaby nasycona. Ponieważ <math>b</math> może zostać rozłożone na sumę pewnych najkrótszych ścieżek w <math>G_f</math>, to z [[#lemat_1|lematu 1]], wiemy, że odległość z <math>s</math> do <math>u</math> nie zmalała, tzn. <math>d_{f}(s,u) \le d_{f+b}(s,u)</math>. Jednak ponieważ <math>p</math> jest najkrótszą ścieżką z <math>s</math> do <math>t</math>, oznacza to, że odległość do <math>v</math> wzrosła o co najmniej <math>2</math>, <math>d_{f}(s,v) +2 \le d_{f+b}(s,v)</math>. Kawałek ścieżki <math>p</math> od <math>v</math> do <math>t</math> też jest najkrótszą ścieżką, więc <math>d_{f}(s,t) +2 \le d_{f+b}(s,t)</math>. Długość najkrótszej ścieżki w grafie rezydualnym musiała więc wzrosnąć. | ||
}} | }} | ||
{{wniosek|4|wniosek_4|3= | {{wniosek|4|wniosek_4|3= | ||
Ponieważ maksymalna długość najkrótszej ścieżki może wynosić co najwyżej <math>n-1</math>, | Ponieważ maksymalna długość najkrótszej ścieżki może wynosić co najwyżej <math>n-1</math>, maksymalna liczba faz w algorytmie Dinica wynosi <math>n</math>. | ||
}} | }} | ||
== Znajdowanie przepływu blokującego - Algorytm Dinica == | == Znajdowanie przepływu blokującego - Algorytm Dinica == | ||
Zanim przejdziemy do algorytmów znajdujących przepływ | Zanim przejdziemy do opisu algorytmów znajdujących przepływ blokujący, wprowadźmy pojęcie sieci warstwowej. '''Sieć warstwową''' <math>\overline{G}_f</math> dla sieci rezydualnej <math>G_f = (V,E_f)</math> definiujemy jako graf skierowany <math>\overline{G}_f = (V,\overline{E}_f)</math> o następującym zbiorze krawędzi: | ||
{{wzor2|1= | {{wzor2|1= | ||
<math> | <math> | ||
\overline{E} | \overline{E}_f = \{(u,v): (u,v) \in E_f \mbox{ i } d_f(s,u) + 1 = d_f(s,v)\}. | ||
</math> | </math> | ||
}} | }} | ||
Zauważmy, że wszystkie ścieżki w <math>\overline{G}_f</math> z <math>s</math> do <math>t</math> są najkrótszymi ścieżkami. Jeżeli chcemy wyszukać przepływ blokujący, to zauważmy, że robiąc to w sieci warstwowej będziemy mieli spełniony automatycznie warunek 1 definicji przepływu blokującego. W grafie <math>\overline{G}_f</math> wszystkie ścieżki są najkrótsze, jednak nie wszystkie ścieżki muszą prowadzić do <math>t</math>. Jeżeli usuniemy | Zauważmy, że wszystkie ścieżki w <math>\overline{G}_f</math> z <math>s</math> do <math>t</math> są najkrótszymi ścieżkami. Jeżeli chcemy wyszukać przepływ blokujący, to zauważmy, że robiąc to w sieci warstwowej, będziemy mieli spełniony automatycznie [[#warunki_blokujace|warunek 1]] definicji przepływu blokującego. W grafie <math>\overline{G}_f</math> wszystkie ścieżki są najkrótsze, jednak nie wszystkie ścieżki muszą prowadzić do <math>t</math>. Jeżeli usuniemy zawczasu z grafu <math>\overline{G}_f</math> krawędzie, które prowadzą donikąd, to ścieżki z <math>s</math> do <math>t</math> będziemy mogli wyszukiwać w czasie <math>O(n)</math>. | ||
{{algorytm|[Dinica] znajduje przepływ blokujący w grafie <math>G_f</math>|algorytm_Dinica| | {{algorytm|[Dinica] znajduje przepływ blokujący w grafie <math>G_f</math>|algorytm_Dinica| | ||
3= | 3= | ||
DINIC-PRZEPŁYW-BLOKUJĄCY(G_f, s, t) | DINIC-PRZEPŁYW-BLOKUJĄCY<math>(G_f, s, t)</math> | ||
1 <math>b = 0</math> | 1 <math>b = 0</math> | ||
2 skonstruuj graf <math>\overline{G}_f</math> | 2 skonstruuj graf <math>\overline{G}_f</math> | ||
3 | 3 '''while''' <math>\overline{E}_f \neq \emptyset</math> '''do''' | ||
4 '''begin''' | |||
5 znajdź ścieżkę <math>p</math> z <math>s</math> do <math>t</math> w <math>\overline{G}_f</math> | |||
6 '''for''' każda krawędź <math>(u,v) \in p</math> '''do''' | |||
7 '''begin''' | |||
8 <math>b(u,v) = b(u,v) + c_f(p)</math> | |||
9 <math>b(v,u) = -b(u,v)</math> | |||
10 usuń rekurencyjnie <math>u</math> i inne wierzchołki jeżeli nie wychodzi z nich żadna krawędź residualna | |||
11 '''end''' | |||
12 '''return''' <math>b</math> | |||
}} | }} | ||
Działanie tego algorytmu zobrazowane jest na następującej animacji. | Działanie tego algorytmu zobrazowane jest na następującej animacji. | ||
[[File:Zasd_ilustr_p.svg|800x300px|thumb|center]] | |||
Zauważmy, że po zakończeniu działania algorytmu, w grafie <math>\overline{G}_f</math> nie pozostanie żadna ścieżka z <math>s</math> do <math>t</math>. Skonstruowany przepływ będzie więc przepływem blokującym. | |||
Główna pętla programu w liniach 5-17 wykonana zostanie co najwyżej <math>m</math> razy, bo w każdym jej przebiegu nasycana jest co najmniej jedna krawędź. Pętlę tę można zaimplementować tak, aby działała w czasie <math>O(n)</math>, dlatego całkowity czas działania tej procedury wynosi <math>O(nm)</math>. Korzystając z [[#wnisek_4|Wniosku 4]] widzimy, że czas działania algorytmu Dinica wynosi <math>O(mn^2)</math>. | |||
Główna pętla programu w liniach 5-17 wykonana zostanie co najwyżej <math>m</math> razy, bo w każdym jej przebiegu | |||
== Znajdowanie przepływu blokującego - Algorytm trzech Hindusów == | == Znajdowanie przepływu blokującego - Algorytm trzech Hindusów == | ||
W algorytmie tym użyjemy pojęcia '''przepustowości wierzchołka''' w sieci <math> | W algorytmie tym użyjemy pojęcia '''przepustowości wierzchołka''' w sieci <math>G</math>, którą definiujemy jako: | ||
{{wzor2|1= | {{wzor2|1= | ||
Linia 155: | Linia 147: | ||
}} | }} | ||
W algorytmie trzech Hindusów, który nazywany jest też algorytmem MKM | W algorytmie trzech Hindusów, który nazywany jest też algorytmem MKM (od nazwisk autorów), będziemy w każdym wykonaniu głównej pętli algorytmu nasycać jeden wierzchołek, przesyłając z niego przepływ do przodu i w pewnym sensie do tyłu. W czasie wykonywania pętli funkcja <math>f</math> przestanie spełniać warunek zachowania przepływu, jednak pod koniec ten warunek zostanie przywrócony. Użyjemy tutaj dwóch pomocniczych procedur: | ||
* procedury PRZEŚLIJ(v) - jeżeli do wierzchołka <math>v</math> wpływa większy przepływ niż wypływa, to procedura ta przesyła ten nadmiar do przodu w grafie <math>\overline{G}_f</math> nasycając po kolei krawędzie wychodzące z <math>v</math>, | * procedury {{kotwica|przeslij|PRZEŚLIJ<math>(v)</math>}} - jeżeli do wierzchołka <math>v</math> wpływa większy przepływ niż wypływa, to procedura ta przesyła ten nadmiar do przodu w grafie <math>\overline{G}_f</math>, nasycając po kolei krawędzie wychodzące z <math>v</math>, | ||
* procedury COFNIJ(v) - jeżeli z wierzchołka <math>v</math> wypływa więcej niż do niego wpływa, to procedura ta kompensuje ten niedomiar przesyłając przepływ z wierzchołków z których istnieją w <math>\overline{G}_f</math> krawędzie do <math>v</math> nasycając po kolei krawędzie wchodzące do <math>v</math>. | * procedury {{kotwica|cofnij|COFNIJ<math>(v)</math>}} - jeżeli z wierzchołka <math>v</math> wypływa więcej niż do niego wpływa, to procedura ta kompensuje ten niedomiar, przesyłając przepływ z wierzchołków, z których istnieją w <math>\overline{G}_f</math> krawędzie do <math>v</math>, nasycając po kolei krawędzie wchodzące do <math>v</math>. | ||
{{algorytm|[Malhotra, Kumar i Maheshwari] znajduje przepływ blokujący w grafie <math>G_f</math>|algorytm_Dinica| | {{algorytm|[Malhotra, Kumar i Maheshwari] znajduje przepływ blokujący w grafie <math>G_f</math>|algorytm_Dinica| | ||
3= | 3= | ||
MKM-PRZEPŁYW-BLOKUJĄCY(G_f, s, t) | MKM-PRZEPŁYW-BLOKUJĄCY<math>(G_f, s, t)</math> | ||
1 <math>b = 0</math> | 1 <math>b = 0</math> | ||
2 skonstruuj graf <math>\overline{G}_f</math> | 2 skonstruuj graf <math>\overline{G}_f</math> | ||
3 '''while''' <math>\overline{E}_f \neq \emptyset </math> '''do''' | 3 '''while''' <math>\overline{E}_f \neq \emptyset</math> '''do''' | ||
4 '''begin''' | 4 '''begin''' | ||
5 znajdź wierzchołek o najmniejszym <math>c(v)</math> | 5 znajdź wierzchołek o najmniejszym <math>c(v)</math> | ||
6 prześlij <math>c(v)</math> jednostek przepływu krawędziami wychodzącymi z <math>v</math> | 6 prześlij <math>c(v)</math> jednostek przepływu krawędziami wychodzącymi z <math>v</math> | ||
7 prześlij <math>c(v)</math> jednostek przepływu krawędziami wchodzącymi do <math>v</math> | 7 prześlij <math>c(v)</math> jednostek przepływu krawędziami wchodzącymi do <math>v</math> | ||
8 '''for''' | 8 '''for''' <math>i=d(s,v)+1</math> '''to''' <math>n-1</math> '''do''' | ||
9 | 9 '''foreach''' <math>w \in \{w \in V: d(s,w) = i\}</math> '''do''' | ||
10 | 10 [[#przeslij|PRZEŚLIJ]]<math>(w)</math> | ||
11 | 11 '''for''' <math>i=d(s,v)-1</math> '''downto''' <math>1</math> '''do''' | ||
12 | 12 '''foreach''' <math>w \in \{w \in V: d(s,w) = i\}</math> '''do''' | ||
13 usuń <math>v</math> z grafu poprawiając przepustowości wierzchołków sąsiednich | 13 [[#cofnij|COFNIJ]]<math>(w)</math> | ||
14 usuń <math>v</math> z grafu poprawiając przepustowości wierzchołków sąsiednich | |||
15 '''end''' | |||
16 '''return''' <math>b</math> | |||
}} | }} | ||
Działanie tego algorytmu zobrazowane jest na następującej animacji. | Działanie tego algorytmu zobrazowane jest na następującej animacji. | ||
[[File:Zasd_ilustr_q.svg|800x300px|thumb|center]] | |||
Zauważmy, że ponieważ wybraliśmy wierzchołek o najmniejszej przepustowości, to zawsze w procedurach PRZEŚLIJ i COFNIJ uda nam | Zauważmy, że ponieważ wybraliśmy wierzchołek o najmniejszej przepustowości, to zawsze w procedurach [[#przeslij|PRZEŚLIJ]] i [[#cofnij|COFNIJ]] uda nam się przesłać nadmiar bądź zrekompensować niedomiar w wierzchołku. | ||
Zauważmy, że główna pętla procedury może wykonać się co najwyżej <math>n-2</math> razy, ponieważ za każdym razem nasycany jest co najmniej jeden wierzchołek grafu. Policzmy teraz ile razy łącznie będą nasycane krawędzie w trakcie wykonywania procedur PRZEŚLIJ i COFNIJ. Co najwyżej <math>m</math> razy będziemy przesyłać przepływ nasycając krawędzie. Natomiast liczba przesłań nie nasycających krawędzi nie przekroczy <math>O(n^2)</math>, gdyż dla każdego wierzchołka w wykonaniu procedury PRZEŚLIJ i COFNIJ wykonujemy co najwyżej jedno | Zauważmy, że główna pętla procedury może wykonać się co najwyżej <math>n-2</math> razy, ponieważ za każdym razem nasycany jest co najmniej jeden wierzchołek grafu. Policzmy teraz, ile razy łącznie będą nasycane krawędzie w trakcie wykonywania procedur [[#przeslij|PRZEŚLIJ]] i [[#cofnij|COFNIJ]]. Co najwyżej <math>m</math> razy będziemy przesyłać przepływ nasycając krawędzie. Natomiast liczba przesłań nie nasycających krawędzi nie przekroczy <math>O(n^2)</math>, gdyż dla każdego wierzchołka w wykonaniu procedury [[#przeslij|PRZEŚLIJ]] i [[#cofnij|COFNIJ]] wykonujemy co najwyżej jedno przesłanie nie nasycające, a operacji tych łącznie wykonywanych jest <math>O(n^2)</math>. Czas potrzebny na znalezienie przepływu blokującego wynosi więc <math>O(n^2)</math>. Łącząc ten algorytm z [[#algorytm_Dinica|algorytmem Dinica]], otrzymujemy algorytm znajdujący maksymalny przepływ w grafie w czasie <math>O(n^3)</math>. |
Aktualna wersja na dzień 11:28, 5 wrz 2023
Abstrakt
W wykładzie tym przedstawimy trzy algorytmy znajdowania przepływu w grafie. Pierwszym będzie algorytm Edmondsa-Karpa działający w czasie . Następnym będzie algorytm Dinica działający w czasie , oraz trzecim tak zwany algorytm trzech Hindusów, działający w czasie . Nazwiska tych tytułowych Hindusów to Malhotra, Kumar i Maheshwari. Dwa ostatnie algorytmy oparte będą na konstrukcji przepływów blokujących, które są analogiczną konstrukcją do konstrukcji maksymalnego zbioru rozłącznych ścieżek, której użyliśmy w algorytmie Hopcrofta-Karpa.
Algorytm Edmonds’a-Karp’a
Algorytm Edmonds'a-Karp'a to algorytm Forda-Fulkersona w, którym zamiast dowolnej ścieżki powiększającej wybieramy zawsze najkrótszą ścieżkę powiększającą. Zakładamy tutaj, że wszystkie krawędzie mają jednostkowe długości. Taka modyfikacja pozwala poprawić ograniczenie w czasie działania tego algorytmu. Udowodnimy, że algorytm Edmonds’a–Karp’a działa w czasie . W naszej analizie będziemy korzystać z zapisu dla odległości z do w , przy założenie, że każda krawędź ma jednostkową wagę.
Lemat 1
Dowód
Ze względu na sposób wybrania , wiemy że odległość z wierzchołka się nie zmniejszyła, to znaczy:
Twierdzimy, że . Dlaczego? Gdybyśmy mieli , wówczas z nierówności trójkąta dla i oraz powyższych nierówności wynikałoby:
Co jest sprzeczne z założeniem, że . Jak możemy zatem otrzymać i ? Powiększeniu przepływu z do powinno także powiększyć przepływ z do . Algorytm Edmondsa–Karpa zawsze powiększa przepływ wzdłuż najkrótszych ścieżek i dlatego też najkrótsza ścieżka z do w posiada jako ostatnią krawędź. Dlatego mamy:

Następujące twierdzenie ogranicza liczbę iteracji algorytmu Edmondsa–Karpa.
Twierdzenie 2
Dowód
Niech i będą wierzchołkami w połączonymi krawędzią . Ponieważ ścieżki powiększające są najkrótszymi ścieżkami, to dla kawędzi krytycznej , otrzymujemy
Gdy tylko przepływ jest zwiększony, krawędź znika z sieci rezydualnej. Nie może ona się znów pojawić na żadnej innej ścieżce powiększającej dopóki przepływ z do nie będzie zmniejszony, a nastąpi to tylko wtedy, kiedy pojawi się na ścieżce powiększającej. Jeśli jest przepływem w i to zdarzenie ma miejsce, wówczas mamy:
Ponieważ , co wynika z lematu 1, otrzymujemy

Ponieważ każdą iterację algorytmu FORD-FULKERSON można zaimplementować w czasie , to całkowity czas działania algorytmu Edmondsa-Karpa wynosi . W następnych częściach wykładu pokażemy, jak wykorzystując przepływy blokujące poprawić ten wynik do czasu .
Przepływ blokujący
Przepływem blokującym w sieci rezydualnej nazywamy taki przepływ w , że:{{{2}}}
- każda ścieżka z do w jest najkrótszą ścieżką w ,
- oraz każda najkrótsza ścieżka w zawiera krawędź nasyconą w .
Zauważ, że jest to definicja, która odpowiada pojęciu maksymalnego zbioru najkrótszych ścieżek powiększających użytemu w Wykładzie 7. Załóżmy na chwilę, że mamy algorytm znajdujący przepływ blokujący. Pokażemy jak go wykorzystać do znalezienia przepływu maksymalnego, jest to algorytm Dinica. Algorytmy na znajdowanie przepływu blokującego pokażemy w dalszej części tego wykładu.
Algorytm [Dinica] znajduje przepływ maksymalny w grafie
DINIC 1 2 while istnieje ścieżka od do w do 3 begin 4 znajdź przepływ blokujący w 5 6 end 7 return
Poprawność algorytmu Dinica wynika bezpośrednio z twierdzenia o maksymalnym przepływie i minimalnym przekroju, gdyż po zakończeniu algorytmu nie ma już w sieci ścieżek powiększających. Zastanówmy się teraz, ile razy może zostać wykonana pętla while, czyli innymi słowy ile razy będzie konieczne znalezienie przepływu blokującego.
Lemat 3
Dowód

Wniosek 4
Znajdowanie przepływu blokującego - Algorytm Dinica
Zanim przejdziemy do opisu algorytmów znajdujących przepływ blokujący, wprowadźmy pojęcie sieci warstwowej. Sieć warstwową dla sieci rezydualnej definiujemy jako graf skierowany o następującym zbiorze krawędzi:
Zauważmy, że wszystkie ścieżki w z do są najkrótszymi ścieżkami. Jeżeli chcemy wyszukać przepływ blokujący, to zauważmy, że robiąc to w sieci warstwowej, będziemy mieli spełniony automatycznie warunek 1 definicji przepływu blokującego. W grafie wszystkie ścieżki są najkrótsze, jednak nie wszystkie ścieżki muszą prowadzić do . Jeżeli usuniemy zawczasu z grafu krawędzie, które prowadzą donikąd, to ścieżki z do będziemy mogli wyszukiwać w czasie .
Algorytm [Dinica] znajduje przepływ blokujący w grafie
DINIC-PRZEPŁYW-BLOKUJĄCY 1 2 skonstruuj graf 3 while do 4 begin 5 znajdź ścieżkę z do w 6 for każda krawędź do 7 begin 8 9 10 usuń rekurencyjnie i inne wierzchołki jeżeli nie wychodzi z nich żadna krawędź residualna 11 end 12 return
Działanie tego algorytmu zobrazowane jest na następującej animacji.
Zauważmy, że po zakończeniu działania algorytmu, w grafie nie pozostanie żadna ścieżka z do . Skonstruowany przepływ będzie więc przepływem blokującym.
Główna pętla programu w liniach 5-17 wykonana zostanie co najwyżej razy, bo w każdym jej przebiegu nasycana jest co najmniej jedna krawędź. Pętlę tę można zaimplementować tak, aby działała w czasie , dlatego całkowity czas działania tej procedury wynosi . Korzystając z Wniosku 4 widzimy, że czas działania algorytmu Dinica wynosi .
Znajdowanie przepływu blokującego - Algorytm trzech Hindusów
W algorytmie tym użyjemy pojęcia przepustowości wierzchołka w sieci , którą definiujemy jako:
W algorytmie trzech Hindusów, który nazywany jest też algorytmem MKM (od nazwisk autorów), będziemy w każdym wykonaniu głównej pętli algorytmu nasycać jeden wierzchołek, przesyłając z niego przepływ do przodu i w pewnym sensie do tyłu. W czasie wykonywania pętli funkcja przestanie spełniać warunek zachowania przepływu, jednak pod koniec ten warunek zostanie przywrócony. Użyjemy tutaj dwóch pomocniczych procedur:
- procedury PRZEŚLIJ - jeżeli do wierzchołka wpływa większy przepływ niż wypływa, to procedura ta przesyła ten nadmiar do przodu w grafie , nasycając po kolei krawędzie wychodzące z ,
- procedury COFNIJ - jeżeli z wierzchołka wypływa więcej niż do niego wpływa, to procedura ta kompensuje ten niedomiar, przesyłając przepływ z wierzchołków, z których istnieją w krawędzie do , nasycając po kolei krawędzie wchodzące do .
Algorytm [Malhotra, Kumar i Maheshwari] znajduje przepływ blokujący w grafie
MKM-PRZEPŁYW-BLOKUJĄCY 1 2 skonstruuj graf 3 while do 4 begin 5 znajdź wierzchołek o najmniejszym 6 prześlij jednostek przepływu krawędziami wychodzącymi z 7 prześlij jednostek przepływu krawędziami wchodzącymi do 8 for to do 9 foreach do 10 PRZEŚLIJ 11 for downto do 12 foreach do 13 COFNIJ 14 usuń z grafu poprawiając przepustowości wierzchołków sąsiednich 15 end 16 return
Działanie tego algorytmu zobrazowane jest na następującej animacji.
Zauważmy, że ponieważ wybraliśmy wierzchołek o najmniejszej przepustowości, to zawsze w procedurach PRZEŚLIJ i COFNIJ uda nam się przesłać nadmiar bądź zrekompensować niedomiar w wierzchołku.
Zauważmy, że główna pętla procedury może wykonać się co najwyżej razy, ponieważ za każdym razem nasycany jest co najmniej jeden wierzchołek grafu. Policzmy teraz, ile razy łącznie będą nasycane krawędzie w trakcie wykonywania procedur PRZEŚLIJ i COFNIJ. Co najwyżej razy będziemy przesyłać przepływ nasycając krawędzie. Natomiast liczba przesłań nie nasycających krawędzi nie przekroczy , gdyż dla każdego wierzchołka w wykonaniu procedury PRZEŚLIJ i COFNIJ wykonujemy co najwyżej jedno przesłanie nie nasycające, a operacji tych łącznie wykonywanych jest . Czas potrzebny na znalezienie przepływu blokującego wynosi więc . Łącząc ten algorytm z algorytmem Dinica, otrzymujemy algorytm znajdujący maksymalny przepływ w grafie w czasie .