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


W wykładzie tym zajmiemy się przedstawieniem głównych motywacji stojących za algorytmami randomizowanymi. Po krótkim wstępie przestawimy podstawowe pomysły używane w algorytmach randomizowanych. Na zakończenie tego wykładu zaprezentujemy dwa algorytmy randomizowane. Jednym z nich będzie algorytm rozwiązujący problemu maksymalnego przekroju w grafie, a drugim algorytm aproksymacyjny szukający najdłuższej ścieżki w grafie.
W wykładzie tym zajmiemy się przedstawieniem głównych motywacji stojących za algorytmami randomizowanymi. Po krótkim wstępie przestawimy podstawowe pomysły używane w algorytmach randomizowanych. Na zakończenie tego wykładu zaprezentujemy dwa algorytmy randomizowane. Jednym z nich będzie algorytm rozwiązujący problem maksymalnego przekroju w grafie, a drugim algorytm aproksymacyjny szukający najdłuższej ścieżki w grafie.


== Motywacja ==
== Motywacja ==


'''Prostota:''' Jest to pierwszy i najważniejszy powód używania algorytmów randomizowanych. Istnieje wiele przykładów gdzie efektywność prostego algorytmu randomizowanego może równać się (a nawet przerosnąć) efektywność algorytmu deterministycznego.
'''Prostota:''' Jest to pierwszy i najważniejszy powód używania algorytmów randomizowanych. Istnieje wiele przykładów, w których efektywność prostego algorytmu randomizowanego może równać się (a nawet przerosnąć) efektywność algorytmu deterministycznego.


''Przykład: Algorytmy sortujące.'' Istnieje wiele deterministycznych algorytmów sortujących działających w asymptotycznym czasie <math>O(n\log n)</math>, np. algorytm Merge-Sort. W praktyce bardzo często stosowanym algorytmem jest algorytm Quick-sort, który ma tylko oczekiwany czas działania <math>O(n \log n)</math>. Jednakże, algorytm ten jest bardzo prosty do implementacji, i działa zazwyczaj szybciej niż bardziej złożone algorytmy deterministyczne.
''Przykład: Algorytmy sortujące.'' Istnieje wiele deterministycznych algorytmów sortujących działających w asymptotycznym czasie <math>O(n\log n)</math>, np. algorytm Merge-Sort. W praktyce bardzo często stosowanym algorytmem jest algorytm Quick-sort, który ma tylko oczekiwany czas działania <math>O(n \log n)</math>. Jednakże algorytm ten jest bardzo prosty do implementacji i działa zazwyczaj szybciej niż bardziej złożone algorytmy deterministyczne.


'''Szybkość:''' Dla niektórych problemów znane algorytmy randomizowane są dużo szybsze od tych deterministycznych. Może tak być ponieważ, dla algorytmów randomizowanych żądamy często tylko aby poprawna odpowiedź była zwrócona z dużym prawdopodobieństwem, bądź aby działały one w oczekiwanym czasie wielomianowym. To oznacza, że w w niektórych przypadkach algorytm randomizowany może w ogóle nie znaleźć poprawnej odpowiedzi lub może ją znaleźć, ale po bardzo długim czasie.
'''Szybkość:''' Dla niektórych problemów znane algorytmy randomizowane są dużo szybsze od tych deterministycznych. Może tak być, ponieważ dla algorytmów randomizowanych żądamy często tylko aby poprawna odpowiedź była zwrócona z dużym prawdopodobieństwem, bądź aby działały one w oczekiwanym czasie wielomianowym. To oznacza, że w niektórych przypadkach algorytm randomizowany może w ogóle nie znaleźć poprawnej odpowiedzi lub może ją znaleźć, ale po bardzo długim czasie.


''Przykład: Sprawdzanie czy wielomian wielu zmiennych jest zerowy.'' Nie ma żadnego znanego algorytmy deterministycznego działającego w czasie wielomianowym, który by określał czy dany wielomian wielu zmiennych jest zerowy. Jednakże istnieje bardzo prosty i efektywny algorytm randomizowany rozwiązujący ten problem poprzez wyliczenie wartości danego wielomianu w losowym punkcie. Należy zaznaczyć, że taki algorytm jest bardzo przydatny, gdy taki wielomian jest przedstawiony nie wprost, i możemy tylko wyznaczać jego wartość.
''Przykład: Sprawdzanie czy wielomian wielu zmiennych jest zerowy.'' Nie ma żadnego znanego algorytmu deterministycznego działającego w czasie wielomianowym, który by określał czy dany wielomian wielu zmiennych jest zerowy. Jednakże istnieje bardzo prosty i efektywny algorytm randomizowany rozwiązujący ten problem poprzez wyliczenie wartości danego wielomianu w losowym punkcie. Należy zaznaczyć, że taki algorytm jest bardzo przydatny, gdy taki wielomian jest przedstawiony nie wprost i możemy tylko wyznaczać jego wartość.


'''Algorytmy online.''' Algorytmy online są algorytmami, które podejmują decyzje na podstawie częściowej wiedzy na temat danych wejściowych, na przykład gdy przyszłe dane wejściowe nie są znane. W takiej sytuacji losowość jest użyteczna, ponieważ pozwala na uniezależnienie się od nieznanego ciągu danych, który może być dobrany w sposób pesymistyczny.
'''Algorytmy online.''' Algorytmy online są algorytmami, które podejmują decyzje na podstawie częściowej wiedzy na temat danych wejściowych, na przykład gdy przyszłe dane wejściowe nie są znane. W takiej sytuacji losowość jest użyteczna, ponieważ pozwala na uniezależnienie się od nieznanego ciągu danych, który może być dobrany w sposób pesymistyczny.
Linia 18: Linia 18:
'''Złożoność komunikacji.''' W tym przypadku randomizacja też jest przydatna.
'''Złożoność komunikacji.''' W tym przypadku randomizacja też jest przydatna.


''Przykład:'' Rozważmy grę z dwoma graczami: Ula i Piotr. Każdy z nich ma ciąg <math>n</math>-bitów i chcą je porównać, aby sprawdzić czy są one takie same. Jednakże, za każdy wysłany bit muszą zapłacić. Najlepszy algorytm deterministyczny dla tego problemu wymaga wysłania <math>n</math> bitów. Zasadniczo to jest równoważne wysłanie Uli całego ciągu do Piotra. Z drugiej poprzez użycie randomizację wymagana komunikacja może być zredukowana, jadnak przy usłabieniu wymagań, a mianowicie możemy tylko żądać, aby protokół się powiódł z wysokim prawdopodobieństwem. W tym celu Ula i Piotr muszą posiadać ten sam ciąg <math>n</math> losowych bitów, a ciągi są porównane poprzez porównanie jednobitowej wartości iloczynu skalarnego ciągu z tym ciągiem losowym.
''Przykład:'' Rozważmy grę z dwoma graczami: Ula i Piotr. Każdy z nich ma ciąg <math>n</math>-bitów i chcą je porównać, aby sprawdzić czy są one takie same. Jednakże, za każdy wysłany bit muszą zapłacić. Najlepszy algorytm deterministyczny dla tego problemu wymaga wysłania <math>n</math> bitów. Zasadniczo to jest równoważne wysłaniu przez Ulę całego ciągu do Piotra. Z drugiej poprzez użycie randomizacji wymagana komunikacja może być zredukowana, jednak przy osłabieniu wymagań, a mianowicie możemy tylko żądać, aby protokół się powiódł z wysokim prawdopodobieństwem. W tym celu Ula i Piotr muszą posiadać ten sam ciąg <math>n</math> losowych bitów, a ciągi są porównane poprzez porównanie jednobitowej wartości iloczynu skalarnego ciągu z tym ciągiem losowym.




== Ważne pomysły ==
== Ważne pomysły ==


Zanim przedyskutujemy główne pomysły stosowane w algorytmach randomizowanych, trzeba zaznaczyć, algorytmy randomizowane to nie to samo co analiza probabilistyczna. W analizie probabilistycznej, (deterministyczne) algorytmy są analizowane przy założeniu losowych danych wejściowych, na przykład, przy sortowaniu można zanalizować deterministyczny quick-sort (ze stałym punktem podziału) danych wejściowych, który jest losową permutacją n elementów. Z drugiej strony, algorytmy randomizowane stosują przy działaniu losowy rzut monetą, co jest równoznaczne z wyborem algorytmu deterministycznego z talii algorytmów. Ponadto, algorytmy randomizowane dają gwarancje także w przypadku pesymistycznych danych wejściowych.
Zanim przedyskutujemy główne pomysły stosowane w algorytmach randomizowanych, trzeba zaznaczyć, że algorytmy randomizowane to nie to samo, co analiza probabilistyczna. W analizie probabilistycznej, (deterministyczne) algorytmy są analizowane przy założeniu losowych danych wejściowych, na przykład przy sortowaniu można zanalizować deterministyczny quick-sort (ze stałym punktem podziału) danych wejściowych, który jest losową permutacją <math>n</math> elementów. Z drugiej strony, algorytmy randomizowane stosują przy działaniu losowy rzut monetą, co jest równoznaczne z wyborem algorytmu deterministycznego z talii algorytmów. Ponadto, algorytmy randomizowane dają gwarancję także w przypadku pesymistycznych danych wejściowych.


''Liniowość wartości oczekiwanej.'' Podstawowym właściwością zmiennych losowych, która znajduje bardzo częste zastosowanie w algorytmach randomizowanych jest wykorzystanie liniowości wartości oczekiwanych. Niech <math>X</math> i <math>Y</math> będą zmiennymi losowymi. Zachodzi wtedy następująca równość:
''Liniowość wartości oczekiwanej.'' Podstawową właściwością zmiennych losowych, która znajduje zastosowanie w algorytmach randomizowanych, jest wykorzystanie liniowości wartości oczekiwanych. Niech <math>X</math> i <math>Y</math> będą zmiennymi losowymi. Zachodzi wtedy następująca równość:


{{wzor2|1=
{{wzor2|1=
Linia 37: Linia 37:
{{cwiczenie|||3=
{{cwiczenie|||3=
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
Przypuśćmy, że jest <math>26</math> uczniów w klasie i nauczyciel ma ciastka, na których są litery alfabetu (każe ciastko ma inną literkę). Przypuśćmy, z nauczyciel rozdaje po jednym ciastku każdemu uczniowi. Jaka jest oczekiwana liczba uczniów, którzy dostana ciastko z literą, która jest pierwszą literą ich imienia?
Przypuśćmy, że jest <math>26</math> uczniów w klasie i nauczyciel ma ciastka, na których są litery alfabetu (każde ciastko ma inną literkę). Przypuśćmy, z nauczyciel rozdaje po jednym ciastku każdemu uczniowi. Jaka jest oczekiwana liczba uczniów, którzy dostaną ciastko z literą, która jest pierwszą literą ich imienia?
<div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible-content" style="display:none">
Aby odpowiedzieć na to pytanie, rozważmy losowe zmienne: <math>X_{1},X_{2},\ldots,X_{26}</math>, gdzie dla każdego <math>i</math>, <math>1 \le i \le 26</math> definiujemy
Aby odpowiedzieć na to pytanie, rozważmy losowe zmienne: <math>X_{1},X_{2},\ldots,X_{26}</math>, gdzie dla każdego <math>i</math>, <math>1 \le i \le 26</math> definiujemy
Linia 50: Linia 50:
}}
}}


Ponieważ ciastka były losowo rozdzielone, prawdopodobieństwo tego ze <math>n</math>'ta osoba dostanie prawidłową literkę wynosi <math>\frac{1}{26}</math>. Dlatego mamy <math>\mathcal{E}[X_i] = \frac{1}{26}</math> dla wszystkich <math>i</math>.
Ponieważ ciastka były losowo rozdzielone, prawdopodobieństwo tego, że <math>n</math>'ta osoba dostanie prawidłową literkę wynosi <math>\frac{1}{26}</math>. Dlatego mamy <math>\mathcal{E}[X_i] = \frac{1}{26}</math> dla wszystkich <math>i</math>.


Ostatecznie, oczekiwana liczba osób z właściwą literką na ich ciastku wynosi:
Ostatecznie, oczekiwana liczba osób z właściwą literką na ich ciastku wynosi:
Linia 66: Linia 66:




''Liniowość wariancji.'' Jeżeli zmienne losowe są od siebie niezależne, to ich wariancja tez jest liniowa. Dla <math>n</math> niezależnych zmiennych losowych <math>X_1,X_2,\ldots, X_n</math> mamy:
''Liniowość wariancji.'' Jeżeli zmienne losowe są od siebie niezależne, to ich wariancja też jest liniowa. Dla <math>n</math> niezależnych zmiennych losowych <math>X_1,X_2,\ldots, X_n</math> mamy:


{{wzor2|1=
{{wzor2|1=
<math>
<math>
Var(\sum_{i=1}^{n} X_i ) = \frac{\sum_{i=1}^{n} Var(X_i)}{n^2}.
Var(\sum_{i=1}^{n} X_i ) =\frac{\sum_{i=1}^{n} Var(X_i)}{n^2}.
</math>
</math>
}}
}}


Własność ta znajduje zastosowanie, w połączenie z nierównością Czebyszewa bądź Czernoffa, gdy chcemy obniżyć prawdopodobieństwo błędu bądź wielkość tego błędu. Możemy wtedy pobierać losową próbą, tzn. pobrać małą próbę z dużego zbioru danych wejściowych. Zadany problem rozwiązać na tej małej próbie, po czym uzyskanym wynikiem posłużyć się dla problemu właściwego. Problem min-cut posiada algorytmy randomizowane, które korzystają z tej techniki. Także wyznaczenie liczby wartościowań spełniających formułę boolowską może być wykonane w ten sposób (zobacz [[../Ćwiczenia 15#Zadanie 2|Zadanie 2]]).
Własność ta znajduje zastosowanie w połączeniu z nierównością Czebyszewa bądź Czernoffa, gdy chcemy obniżyć prawdopodobieństwo błędu, bądź wielkość tego błędu. Możemy wtedy pobierać losową próbę, tzn. pobrać małą próbę z dużego zbioru danych wejściowych. Zadany problem można rozwiązać na tej małej próbie, po czym uzyskanym wynikiem można posłużyć się dla problemu właściwego. Problem min-cut posiada algorytmy randomizowane, które korzystają z tej techniki. Także wyznaczenie liczby wartościowań spełniających formułę boolowską może być wykonane w ten sposób (zobacz [[../Ćwiczenia 15#Zadanie 2|Zadanie 2]]).


{{cwiczenie|||3=
{{cwiczenie|||3=
Linia 80: Linia 80:
Mamy daną populację <math>n</math> osób, których wzrost nie przekracza <math>3m</math>. Jak przybliżyć średni wzrost w populacji, tak aby wynik z prawdopodobieństwem <math>\frac{3}{4}</math> był różny od średniej o <math>10cm</math>?
Mamy daną populację <math>n</math> osób, których wzrost nie przekracza <math>3m</math>. Jak przybliżyć średni wzrost w populacji, tak aby wynik z prawdopodobieństwem <math>\frac{3}{4}</math> był różny od średniej o <math>10cm</math>?
<div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible-content" style="display:none">
Wybierzmy próbkę <math>k</math> osób, niech <math>X_i</math>, dla <math>1\le i \le k</math> oznacza zmienną losową będącą wzrostem wybranej osoby. Zauważmy, że niezależnie od rozkładu wysokości w populacji mamy:
Wybierzmy próbkę <math>k</math> osób, niech <math>X_i</math> dla <math>1\le i \le k</math> oznacza zmienną losową będącą wzrostem wybranej osoby. Zauważmy, że niezależnie od rozkładu wysokości w populacji, mamy:
{{wzor2|1=
{{wzor2|1=
<math>\sigma^2 = Var(X_i) \le 9m^2 </math>.
<math>\sigma^2 = Var(X_i) \le 9m^2</math>.
}}
}}


Linia 91: Linia 91:
}}
}}


wtedy z nierówności Chebysheva dostajemy:
wtedy z nierówności Czebyszewa dostajemy:


{{wzor2|1=
{{wzor2|1=
<math>\Pr(|\mathcal{E}[X_k] - \mu| < \frac{1}{2} \sigma) < \frac{1}{4},</math>
<math>\Pr(|\mathcal{E}[X_k] - \mu| < \frac{1}{2} \sigma) < \frac{1}{4}</math>
}}
}}


Linia 100: Linia 100:


{{wzor2|1=
{{wzor2|1=
<math>\Pr(|\mathcal{E}[X_k] - \mu| < \frac{1}{10} m) < \frac{1}{4}.</math>
<math>\Pr(|\mathcal{E}[X_k] - \mu| < \frac{1}{10} m) < \frac{1}{4}</math>
}}
}}


Linia 110: Linia 110:




''Duża liczba świadków.'' Kolejny powód, dla którego algorytmy randomizowane są efektywne w znajdowaniu rozwiązania to, to że często problem posiada dużo świadków na ‘tak’ lub na ‘nie’.
''Duża liczba świadków.'' Kolejny powód, dla którego algorytmy randomizowane są efektywne w znajdowaniu rozwiązania to taki, że często problem posiada dużo świadków na "tak" lub na "nie", na przykład przy sprawdzaniu, czy wielomian wielu zmiennych jest zerowy. Kiedy dany wielomian jest rzeczywiście zerowy, to wartość wielomianu w każdym punkcie wynosi zero. Natomiast gdy wielomian nie jest zerowy, wtedy istnieje duża liczba punktów, w których wielomian przyjmuje niezerową wartość (zobacz [[../Ćwiczenia 15#Zadanie 1|Zadanie 1]]). Dlatego znalezienie jednego świadka spośród wielu jest może być łatwo zrealizowane poprzez wybranie małej losowej próby.


Na przykład, przy sprawdzaniu, czy wielomian wielu zmiennych jest zerowy.  Kiedy dany wielomian jest rzeczywiście zerowy, to wartość wielomianu w każdym punkcie wynosi zero.  Natomiast gdy wielomian nie jest zerowy, wtedy istnieje duża liczba punktów w których wielomian przyjmuje niezerową wartość (zobacz [[../Ćwiczenia 15#Zadanie 1|Zadanie 1]]). Dlatego, znalezienie jednego świadka spośród wielu jest może być łatwo zrealizowane poprzez wybranie małej losowej próby.
''Balansowanie obciążenia.'' Randomizacja jest bardzo dobrą metodą na rozłożenie obciążenia. Dla przykładu rozważmy sytuację w której mamy <math>n</math> serwerów i <math>n</math> zadań. Jeżeli każde z <math>n</math> zadań zostanie przydzielone do jednego serwera w sposób losowy, wtedy z dużym prawdopodobieństwem żadna z maszyn nie otrzymuje więcej niż <math>\log n</math> zadań. Możemy delikatnie poprawić prostą randomizowaną strategię: dla każdego zadania wybieramy losowo dwa serwery i przydzielamy to zadanie serwerowi o mniejszym obciążeniu. Można pokazać, że ten zmodyfikowany schemat z dużym prawdopodobieństwem nie przydzieli więcej niż <math>\log\log n</math> zadań dla każdego serwera. W ten sposób, w przypadku miliona zadań, każdy serwer otrzyma co najwyżej pięć zadań. Stwierdzenie '''z dużym prawdopodobieństwem''' oznacza, że jakaś własność zajdzie z prawdopodobieństwem większym niż <math>\frac{1}{2}</math>.
 
''Balansowanie obciążenia.'' Randomizacja jest bardzo dobrą metodą na rozłożenie obciążenia. Dla przykładu rozważmy sytuacje w której mamy <math>n</math> serwerów i <math>n</math> zadań. Jeżeli, każde z <math>n</math> zadań zostanie przydzielone do jednego serwera w sposób losowy, wtedy z dużym prawdopodobieństwem, żadna z maszyn nie otrzymuje więcej niż <math>\log n</math> zadań. Możemy delikatnie poprawić ta prostą randomizowaną strategię: dla każdego zadania wybieramy losowo dwa serwery i przydzielamy to zadanie serwerowi o mniejszym obciążeniu. Można pokazać, że ten zmodyfikowany schemat z dużym prawdopodobieństwem nie przydzieli więcej niż <math>\log\log n</math> zadań dla każdego serwera. W ten sposób, w przypadku miliona zadań, każdy serwer otrzyma co najwyżej pięć zadań. Stwierdzenie '''z dużym prawdopodobieństwem''' oznacza, że jakaś własność zajdzie z prawdopodobieństwem większym niż <math>\frac{1}{2}</math>.


Kolejny przykład na balansowanie obciążenia można odnaleźć w algorytmach dla tablicach hashujących.
Kolejny przykład na balansowanie obciążenia można odnaleźć w algorytmach dla tablicach hashujących.


'''Łamanie symetrii.''' W obliczeniach równoległych i rozproszonych ważne jest, aby złamać symetrię między procesorami. Na przykład, w sieci Ethernet, kiedy dwie maszyny wysyłają pakiet danych w tym samym czasie, następuje kolizja. Nie wiadomo który procesor powinien sprobować ponownie przesłać pakiet. Jak uczynią to obydwa to znów nastąpi kolizja. Najprostszym sposób na uniknięcie tej sytuacji jest zastosowanie jest złamanie symetrii poprzez randomizację, np. każdy procesor rzuca monetą i próbuje ponownie wysłać pakiet gdy wypadła mu reszka. Kolejnym przykładem jest problem znalezienia doskonałego skojarzenia w grafie na maszynie równoległej. Ważne jest, aby wszystkie procesory obliczały to samo skojarzenie, jednak nie wiadomo które, bo jeszcze go nie znaleźliśmy. Tutaj można posłużyć się randomizacją w celu złamania symetrii pomiędzy różnymi możliwymi skojarzeniami i odizolowaniem pojedynczego skojarzenia.
'''Łamanie symetrii.''' W obliczeniach równoległych i rozproszonych ważne jest, aby złamać symetrię między procesorami. Na przykład w sieci Ethernet, kiedy dwie maszyny wysyłają pakiet danych w tym samym czasie, następuje kolizja. Nie wiadomo, który procesor powinien spróbować ponownie przesłać pakiet. Gdy uczynią to obydwa, znów nastąpi kolizja. Najprostszym sposobem na uniknięcie tej sytuacji jest złamanie symetrii poprzez randomizację, np. każdy procesor rzuca monetą i próbuje ponownie wysłać pakiet, gdy wypadła mu reszka. Kolejnym przykładem jest problem znalezienia doskonałego skojarzenia w grafie na maszynie równoległej. Ważne jest, aby wszystkie procesory obliczały to samo skojarzenie, jednak nie wiadomo które, bo jeszcze go nie znaleźliśmy. Tutaj można posłużyć się randomizacją w celu złamania symetrii pomiędzy różnymi możliwymi skojarzeniami i odizolowaniem pojedynczego skojarzenia.


'''Derandomizacja.''' Niektóre proste algorytmy randomizowane mogą być przerobione na (nie takie proste) algorytmy deterministyczne poprzez derandomizację. Często w ten sposób dostaje się najefektywniejsze algorytmy deterministyczne.
'''Derandomizacja.''' Niektóre proste algorytmy randomizowane mogą być przerobione na (nie takie proste) algorytmy deterministyczne poprzez derandomizację. Często w ten sposób dostaje się najefektywniejsze algorytmy deterministyczne.


== Problem maksymalnego przekroju ==
== Problem maksymalnego przekroju ==


W problemie '''maksymalnego przekroju''' dany jest graf <math>G = (V,E)</math>, a problem polega na podzieleniu zbioru wierzchołków na dwa podzbiory <math>(A,B)</math>. Krawędź jest '''rozcięta''', wtedy gdy jej końce leżą po dwóch różnych stronach podziału zbioru wierzchołków. Istotą problemu jest znalezienie takiego podziału, który maksymalizuje liczbę krawędzi roziętych. Problem ten jest NP-trudny. Używając randomizacji skonstruujemy prosty <math>\frac{1}{2}</math>–aproksymacyjny algorytm dla tego problemu.
W problemie '''maksymalnego przekroju''' dany jest graf <math>G = (V,E)</math>, a wynikiem jest podzieł zbioru wierzchołków na dwa podzbiory <math>(A,B)</math>. Krawędź jest '''rozcięta''' wtedy, gdy jej końce leżą po dwóch różnych stronach podziału. Istotą problemu jest znalezienie takiego podziału, który maksymalizuje liczbę krawędzi rozciętych. Problem ten jest NP-trudny. Używając randomizacji, skonstruujemy prosty <math>\frac{1}{2}</math>–aproksymacyjny algorytm dla tego problemu.


Niech <math>|V| = n</math> i <math>|E| = m</math>.
Niech <math>|V| = n</math> i <math>|E| = m</math>.
Linia 133: Linia 130:


{{wzor2|1=
{{wzor2|1=
<math>\mbox{liczba krawędzi w zbiorze } > \frac{|E|}{2} = \frac{m}{2}.</math>
<math>\mbox{liczba krawędzi w zbiorze } > \frac{|E|}{2} = \frac{m}{2}</math>
}}
}}
}}
}}


{{dowod|||3=
{{dowod|||3=
Budujemy zbiór <math>A</math> (to znaczy jedną stronę podziału) włączając każdy wierzchołek do <math>A</math> z prawdopodobieństwem <math>\frac{1}{2}</math>. Zadajmy teraz pytanie: jaka jest oczekiwana liczba rozciętych krawędzi dla podziału <math>(A, V - A)</math>. Aby odpowiedzieć na to pytanie, dla każdej krawędzi <math>(i,j) \in E</math>, zdefiniujmy
Budujemy zbiór <math>A</math> (to znaczy jedną stronę podziału) włączając każdy wierzchołek do <math>A</math> z prawdopodobieństwem <math>\frac{1}{2}</math>. Zadajmy teraz pytanie: jaka jest oczekiwana liczba rozciętych krawędzi dla podziału <math>(A, V - A)</math>? Aby odpowiedzieć na to pytanie, dla każdej krawędzi <math>(i,j) \in E</math>, zdefiniujmy


{{wzor2|1=
{{wzor2|1=
Linia 157: Linia 154:
}}
}}


Nasze pytanie sprowadza się teraz do wyznaczenia wartości <math>\mathcal{E}[X]</math>. Posłużymy się liniowością wartości oczekiwanych. Po pierwsze, zauważ  że <math>\mathcal{E}[X_{i,j}] = 1</math>. To zachodzi, ponieważ niezależnie czy <math>i</math> jest w zbiorze <math>A</math> czy nie, jest dokładnie jeden wybór dla <math>j</math>, który rozetnie krawędź <math>(i, j)</math>. Stąd, otrzymujemy:
Nasze pytanie sprowadza się teraz do wyznaczenia wartości <math>\mathcal{E}[X]</math>. Posłużymy się liniowością wartości oczekiwanych. Po pierwsze zauważmy, że <math>\mathcal{E}[X_{i,j}] = \frac{1}{2}</math>. Jest tak, ponieważ niezależnie od tego, czy <math>i</math> jest w zbiorze <math>A</math> czy nie, jest dokładnie jeden wybór dla <math>j</math>, który rozetnie krawędź <math>(i, j)</math>. Stąd otrzymujemy:


{{wzor2|1=
{{wzor2|1=
Linia 165: Linia 162:
}}
}}


Aby zakończyć dowód, zaobserwujmy, że w wyniku tego losowego eksperymentu liczba rozciętych krawędzi musi być co najmniej <math>\frac{m}{2}</math>.}}
Aby zakończyć dowód, zaobserwujmy, że musi istnieć podział dla którego liczba rozciętych krawędzi jest równa co najmniej średniej, czyli <math>\frac{m}{2}</math>.}}


Twierdzenie to zobrazowane jest na następującej animacji.
Twierdzenie to zobrazowane jest na następującej animacji.
<flash>file=Zasd_ilustr_s.swf|width=600|height=500</flash>
[[File:Zasd_ilustr_s.svg|800x250px|thumb|center]]
 


Zauważamy, ze liczba rozciętych krawędzi w najlepszym podziale nie może przekraczać <math>m</math>. Powyższy algorytm daje nam przecięcie wielkości <math>\frac{m}{2}</math>. Stąd, oczekiwana aproksymacja wyniku to <math>\frac{1}{2}</math>.
Zauważamy, że liczba rozciętych krawędzi w najlepszym podziale nie może przekraczać <math>m</math>. Powyższy algorytm daje nam przecięcie wielkości <math>\frac{m}{2}</math>. Stąd oczekiwana aproksymacja wyniku to <math>\frac{1}{2}</math>.


{{uwaga|||3=Istnieje prosty sposób na derandomizację powyższego algorytmu poprzez zastosowanie metody zachłannej. Włączamy pierwszy wierzchołek do <math>A</math> a następnie analizujemy pozostałe wierzchołki, jeden za drugim. Włączamy do <math>A</math> wierzchołek wtedy i tylko wtedy, kiedy po włączeniu wierzchołka rozciętych jest więcej krawędzi niż w przypadku uprzednio rozpatrywanych wierzchołków.}}
{{uwaga|||3=Istnieje prosty sposób na derandomizację powyższego algorytmu poprzez zastosowanie metody zachłannej. Włączamy pierwszy wierzchołek do <math>A</math>, a następnie analizujemy pozostałe wierzchołki, jeden za drugim. Włączamy do <math>A</math> wierzchołek wtedy i tylko wtedy, kiedy po włączeniu wierzchołka rozciętych jest więcej krawędzi niż w przypadku uprzednio rozpatrywanych wierzchołków.}}


== Problem długiej ścieżki ==
== Problem długiej ścieżki ==


W problemie '''znalezienia długiej ścieżki''' dany mamy graf <math>G = (V,E)</math> i liczbę całkowitą <math>k</math>, problem ten polega na znalezieniu prostej ścieżki o długości <math>k</math> w grafie <math>G</math>. Zauważmy, że dla <math>k = n 1</math>, problem ten jest równoznaczny ze znalezieniem ścieżki Hamiltona w grafie <math>G</math>. Pokażemy teraz algorytm, który działa w czasie wielomianowym dla <math>k = O(\frac{\log n}{\log \log n})</math>. [[../Ćwicznia 15#Zadanie 3|Zadanie 3]] polega na poprawieniu tego algorytmu tak aby działał w czasie wielomianowym dla <math>k = O(\log n)</math>.
W problemie '''znalezienia długiej ścieżki''' dany mamy graf <math>G = (V,E)</math> i liczbę całkowitą <math>k</math>. Problem ten polega na znalezieniu prostej ścieżki o długości <math>k</math> w grafie <math>G</math>. Zauważmy, że dla <math>k = n - 1</math> problem ten jest równoznaczny ze znalezieniem ścieżki Hamiltona w grafie <math>G</math>. Pokażemy teraz algorytm, który działa w czasie wielomianowym dla <math>k = O(\frac{\log n}{\log \log n})</math>. [[../Ćwicznia 15#Zadanie 3|Zadanie 3]] polega na poprawieniu tego algorytmu tak, aby działał w czasie wielomianowym dla <math>k = O(\log n)</math>.


Główny pomysł algorytmiczny opiera się na pomyśle, że dla skierowanego acyklicznego grafu (DAGu) istnieje algorytm działający w czasie wielomianowym znajdujący najdłuższą ścieżkę.
Główny pomysł to wykożystanie tego, że dla skierowanego acyklicznego grafu (DAG-u) istnieje algorytm działający w czasie wielomianowym znajdujący najdłuższą ścieżkę.


{{cwiczenie|||3=
{{cwiczenie|||3=
Linia 185: Linia 181:
Korzystając z programowania dynamicznego, podaj algorytm znajdujący najdłuższą ścieżkę w DAGu, który działa w czasie <math>O(|E|)</math>?  
Korzystając z programowania dynamicznego, podaj algorytm znajdujący najdłuższą ścieżkę w DAGu, który działa w czasie <math>O(|E|)</math>?  
<div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible-content" style="display:none">
TODO
Zacznijmy od znalezienia porządku topologicznego <math>t_1,\ldots, t_n</math> w zadanym DAGu <math>G = (V,E)</math>. Zauważmy, że ścieżki w <math>G</math> mogą iść tylko zgodnie z tym porządkiem. Umożliwia nam to wyznaczenie długości najdłuższej ścieżki dynamicznie, tzn., długość najdłuższej ścieżki kończącej się w danym wierzchołku jest równa długości najdłuższej ścieżki kończącej się w jednym z jego poprzedników plus <math>1</math>.
</div>
</div>
</div>
</div>
}}
}}


Przypuśćmy, że przypisujemy wierzchołkom grafu <math>G</math> wartości ze zbioru <math>\{1, 2, \ldots, n\}</math> losowo tak, że każda permutacja jest tak samo prawdopodobna. Ponadto, skierowujemy wszystkie krawędzie z gafu od mniejszych wartości do większych.  W ten sposób otrzymujemy graf DAG, który oznaczamy przez <math>\vec{G}</math>.
Przypuśćmy, że przypisujemy wierzchołkom grafu <math>G</math> wartości ze zbioru <math>\{1, 2, \ldots, n\}</math> losowo tak, że każda permutacja jest tak samo prawdopodobna. Ponadto, skierowujemy wszystkie krawędzie z grafu od mniejszych wartości do większych.  W ten sposób otrzymujemy graf DAG, który oznaczamy przez <math>\vec{G}</math>.


{{twierdzenie|2|twierdzenie_2|3= Jeśli <math>G</math> zawiera ścieżkę <math>P</math> o długości <math>k</math> to, wtedy <math>P</math> będzie skierowaną ścieżką w <math>\vec{G}</math> z prawdopodobieństwem
{{twierdzenie|2|twierdzenie_2|3= Jeśli <math>G</math> zawiera ścieżkę <math>P</math> o długości <math>k</math>, to wtedy <math>P</math> będzie skierowaną ścieżką w <math>\vec{G}</math> z prawdopodobieństwem
<math>\alpha = \frac{2}{(k+1)!}</math>
<math>\alpha = \frac{2}{(k+1)!}</math>
}}
}}


{{dowod|||3=Najpierw dla przykładu rozważmy ścieżkę nieskierowaną przechodzącą przez wierzchołki <math>a,b,c,d</math> w <math>G</math> o długości <math>3</math>. Będzie to skierowana  ścieżka w <math>\vec{G}</math> wtedy i tylko wtedy gdy, albo <math>a < b < c < d</math> albo <math>a > b > c > d</math> w naszym przypisaniu. Generalnie, ścieżka <math>P</math> długości <math>k</math> będzie skierowaną ścieżką w <math>\vec{G}</math> wtedy i tylko wtedy, kiedy <math>k + 1</math> wierzchołków na ścieżce <math>P</math> będzie miało przypisane wartości w sposób albo ściśle rosnący albo ściśle malejący. Ze względu na to, że wszystkie permutacje <math>k + 1</math> wierzchołków są jednakowo prawdopodobne i tylko dwie z nich dają ścieżkę skierowaną w <math>\vec{G}</math>, to wynika z tego, że <math>P</math> będzie ścieżką skierowaną w <math>\vec{G}</math> z prawdopodobieństwem <math>\alpha = \frac{2}{(k+1)!}</math>.
{{dowod|||3=Najpierw dla przykładu rozważmy ścieżkę nieskierowaną, przechodzącą przez wierzchołki <math>a,b,c,d</math> w <math>G</math> o długości <math>3</math>. Będzie to skierowana  ścieżka w <math>\vec{G}</math> wtedy i tylko wtedy, gdy albo <math>a < b < c < d</math>, albo <math>a > b > c > d</math> w naszym przypisaniu. Generalnie, ścieżka <math>P</math> długości <math>k</math> będzie skierowaną ścieżką w <math>\vec{G}</math> wtedy i tylko wtedy, kiedy <math>k + 1</math> wierzchołków na ścieżce <math>P</math> będzie miało przypisane wartości w sposób albo ściśle rosnący, albo ściśle malejący. Ze względu na to, że wszystkie permutacje <math>k + 1</math> wierzchołków są jednakowo prawdopodobne i tylko dwie z nich dają ścieżkę skierowaną w <math>\vec{G}</math>, to wynika z tego, że <math>P</math> będzie ścieżką skierowaną w <math>\vec{G}</math> z prawdopodobieństwem <math>\alpha = \frac{2}{(k+1)!}</math>.
}}
}}


Linia 210: Linia 206:
  3    przypisz losową permutację <math>\{1,\ldots, n\}</math> wierzchołkom <math>G</math>
  3    przypisz losową permutację <math>\{1,\ldots, n\}</math> wierzchołkom <math>G</math>
  4    używając tej permutacji skonstruuj <math>\vec{G}</math>
  4    używając tej permutacji skonstruuj <math>\vec{G}</math>
  5    '''if''' udało się znaleźć w <math>\vec{G}</math> ścieżkę długości <math>k</math> <math>then</math>
  5    '''if''' udało się znaleźć w <math>\vec{G}</math> ścieżkę długości <math>k</math> '''then'''
  6      '''return''' TRUE
  6      '''return''' TRUE
  7  '''end'''
  7  '''end'''
Linia 218: Linia 214:
Działanie tego algorytmu zobrazowane jest na następującej animacji.
Działanie tego algorytmu zobrazowane jest na następującej animacji.


<flash>file=Zasd_ilustr_t.swf|width=600|height=500</flash>
[[File:Zasd_ilustr_t.svg|800x250px|thumb|center]]
 


{{twierdzenie|4|twierdzenie_4|3=Powyższy algorytm znajduje ścieżkę o długości <math>k = O(\frac{\log n}{\log \log n}})</math>, jeżeli taka istnieje, w randomizowanym czasie wielomianowym.
{{twierdzenie|4|twierdzenie_4|3=Powyższy algorytm znajduje ścieżkę o długości <math>k = O(\frac{\log n}{\log \log n}})</math>, jeżeli taka istnieje, w randomizowanym czasie wielomianowym.
}}
}}


{{dowod|||3=Załóżmy, że istnieje ścieżka o długości <math>k</math> w grafie <math>G</math>. Chcemy obliczyć prawdopodobieństwo tego ze poniesiemy klęskę w odnalezieniu ścieżki o długości <math>k</math> w <math>\vec{G}</math> we wszystkich <math>t</math> próbach, które jest takie samo jak prawdopodobieństwo ze <math>\vec{G}</math> nie posiada skierowanej ścieżki o długości <math>k</math> we wszystkich próbach <math>t</math>.
{{dowod|||3=Załóżmy, że istnieje ścieżka o długości <math>k</math> w grafie <math>G</math>. Chcemy obliczyć prawdopodobieństwo tego, że nie znajdziemy ścieżki o długości <math>k</math> w <math>\vec{G}</math> we wszystkich <math>t</math> próbach. Prawdopodobieństwo to jest takie samo jak prawdopodobieństwo, że <math>\vec{G}</math> nie posiada skierowanej ścieżki o długości <math>k</math> we wszystkich <math>t</math> próbach.


{{wzor2|1=
{{wzor2|1=
Linia 231: Linia 226:
}}
}}


Pierwsza nierówność zachodzi, ponieważ w każdej próbie graf <math>\vec{G}</math> nie posiada skierowanej ścieżki o długości <math>k</math> z prawdopodobieństwem co najwyżej <math>(1 \alpha)</math> (zgodnie z [[#wniosek_3|Wnioskiem 3]]), przy czym każda z prób jest niezależna. Druga nierówność jest konsekwencja tego że <math>1 + x \le e^x</math>, dla wszystkich <math>x</math>. Dlatego tez, algorytm może znajdzie ścieżkę o długości <math>k</math> (jeżeli taka istnieje) z prawdopodobieństwem co najmniej <math>1 - \frac{1}{e}</math>. Czas działania algorytmu wynosi <math>t \times O(|E|) = \frac{(k+1)!}{2} \times O(|E|)</math>. Zauważ, że <math>(k+1)! \le (k+1)^k</math>, a po wstawieniu <math>k = \frac{c\log n}{\log \log n}</math> daje nam czas działania <math>O(mn^c)</math>, który jest wielomianowy dla każdej stałej <math>c</math>.
Pierwsza nierówność zachodzi, ponieważ w każdej próbie graf <math>\vec{G}</math> nie posiada skierowanej ścieżki o długości <math>k</math> z prawdopodobieństwem co najwyżej <math>(1 - \alpha)</math> (zgodnie z [[#wniosek_3|Wnioskiem 3]]), przy czym każda z prób jest niezależna. Druga nierówność jest konsekwencją tego, że <math>1 + x \le e^x</math>, dla wszystkich <math>x</math>. Dlatego też algorytm znajdzie ścieżkę o długości <math>k</math> (jeżeli taka istnieje) z prawdopodobieństwem co najmniej <math>1 - \frac{1}{e}</math>. Czas działania algorytmu wynosi <math>t \times O(|E|) = \frac{(k+1)!}{2} \times O(|E|)</math>. Zauważ, że <math>(k+1)! \le (k+1)^k</math>, czyli po wstawieniu <math>k = \frac{c\log n}{\log \log n}</math> otrzymujemy czas <math>O(mn^c)</math>, który jest wielomianowy dla każdej stałej <math>c</math>.
}}
}}

Aktualna wersja na dzień 11:29, 5 wrz 2023

Abstrakt

W wykładzie tym zajmiemy się przedstawieniem głównych motywacji stojących za algorytmami randomizowanymi. Po krótkim wstępie przestawimy podstawowe pomysły używane w algorytmach randomizowanych. Na zakończenie tego wykładu zaprezentujemy dwa algorytmy randomizowane. Jednym z nich będzie algorytm rozwiązujący problem maksymalnego przekroju w grafie, a drugim algorytm aproksymacyjny szukający najdłuższej ścieżki w grafie.

Motywacja

Prostota: Jest to pierwszy i najważniejszy powód używania algorytmów randomizowanych. Istnieje wiele przykładów, w których efektywność prostego algorytmu randomizowanego może równać się (a nawet przerosnąć) efektywność algorytmu deterministycznego.

Przykład: Algorytmy sortujące. Istnieje wiele deterministycznych algorytmów sortujących działających w asymptotycznym czasie O(nlogn), np. algorytm Merge-Sort. W praktyce bardzo często stosowanym algorytmem jest algorytm Quick-sort, który ma tylko oczekiwany czas działania O(nlogn). Jednakże algorytm ten jest bardzo prosty do implementacji i działa zazwyczaj szybciej niż bardziej złożone algorytmy deterministyczne.

Szybkość: Dla niektórych problemów znane algorytmy randomizowane są dużo szybsze od tych deterministycznych. Może tak być, ponieważ dla algorytmów randomizowanych żądamy często tylko aby poprawna odpowiedź była zwrócona z dużym prawdopodobieństwem, bądź aby działały one w oczekiwanym czasie wielomianowym. To oznacza, że w niektórych przypadkach algorytm randomizowany może w ogóle nie znaleźć poprawnej odpowiedzi lub może ją znaleźć, ale po bardzo długim czasie.

Przykład: Sprawdzanie czy wielomian wielu zmiennych jest zerowy. Nie ma żadnego znanego algorytmu deterministycznego działającego w czasie wielomianowym, który by określał czy dany wielomian wielu zmiennych jest zerowy. Jednakże istnieje bardzo prosty i efektywny algorytm randomizowany rozwiązujący ten problem poprzez wyliczenie wartości danego wielomianu w losowym punkcie. Należy zaznaczyć, że taki algorytm jest bardzo przydatny, gdy taki wielomian jest przedstawiony nie wprost i możemy tylko wyznaczać jego wartość.

Algorytmy online. Algorytmy online są algorytmami, które podejmują decyzje na podstawie częściowej wiedzy na temat danych wejściowych, na przykład gdy przyszłe dane wejściowe nie są znane. W takiej sytuacji losowość jest użyteczna, ponieważ pozwala na uniezależnienie się od nieznanego ciągu danych, który może być dobrany w sposób pesymistyczny.


Złożoność komunikacji. W tym przypadku randomizacja też jest przydatna.

Przykład: Rozważmy grę z dwoma graczami: Ula i Piotr. Każdy z nich ma ciąg n-bitów i chcą je porównać, aby sprawdzić czy są one takie same. Jednakże, za każdy wysłany bit muszą zapłacić. Najlepszy algorytm deterministyczny dla tego problemu wymaga wysłania n bitów. Zasadniczo to jest równoważne wysłaniu przez Ulę całego ciągu do Piotra. Z drugiej poprzez użycie randomizacji wymagana komunikacja może być zredukowana, jednak przy osłabieniu wymagań, a mianowicie możemy tylko żądać, aby protokół się powiódł z wysokim prawdopodobieństwem. W tym celu Ula i Piotr muszą posiadać ten sam ciąg n losowych bitów, a ciągi są porównane poprzez porównanie jednobitowej wartości iloczynu skalarnego ciągu z tym ciągiem losowym.


Ważne pomysły

Zanim przedyskutujemy główne pomysły stosowane w algorytmach randomizowanych, trzeba zaznaczyć, że algorytmy randomizowane to nie to samo, co analiza probabilistyczna. W analizie probabilistycznej, (deterministyczne) algorytmy są analizowane przy założeniu losowych danych wejściowych, na przykład przy sortowaniu można zanalizować deterministyczny quick-sort (ze stałym punktem podziału) danych wejściowych, który jest losową permutacją n elementów. Z drugiej strony, algorytmy randomizowane stosują przy działaniu losowy rzut monetą, co jest równoznaczne z wyborem algorytmu deterministycznego z talii algorytmów. Ponadto, algorytmy randomizowane dają gwarancję także w przypadku pesymistycznych danych wejściowych.

Liniowość wartości oczekiwanej. Podstawową właściwością zmiennych losowych, która znajduje zastosowanie w algorytmach randomizowanych, jest wykorzystanie liniowości wartości oczekiwanych. Niech X i Y będą zmiennymi losowymi. Zachodzi wtedy następująca równość:

[X+Y]=[X]+[Y].

Ważne tutaj jest, że ta równość nie wymaga niezależności pomiędzy X i Y. Pozwala to nam uprościć obliczenia, które w przeciwnym wypadku mogłyby być dość skomplikowane. Następujący przykład dobrze to demonstruje.

Ćwiczenie

Przypuśćmy, że jest 26 uczniów w klasie i nauczyciel ma ciastka, na których są litery alfabetu (każde ciastko ma inną literkę). Przypuśćmy, z nauczyciel rozdaje po jednym ciastku każdemu uczniowi. Jaka jest oczekiwana liczba uczniów, którzy dostaną ciastko z literą, która jest pierwszą literą ich imienia?


Liniowość wariancji. Jeżeli zmienne losowe są od siebie niezależne, to ich wariancja też jest liniowa. Dla n niezależnych zmiennych losowych X1,X2,,Xn mamy:

Var(i=1nXi)= i=1nVar(Xi)n2.

Własność ta znajduje zastosowanie w połączeniu z nierównością Czebyszewa bądź Czernoffa, gdy chcemy obniżyć prawdopodobieństwo błędu, bądź wielkość tego błędu. Możemy wtedy pobierać losową próbę, tzn. pobrać małą próbę z dużego zbioru danych wejściowych. Zadany problem można rozwiązać na tej małej próbie, po czym uzyskanym wynikiem można posłużyć się dla problemu właściwego. Problem min-cut posiada algorytmy randomizowane, które korzystają z tej techniki. Także wyznaczenie liczby wartościowań spełniających formułę boolowską może być wykonane w ten sposób (zobacz Zadanie 2).

Ćwiczenie

Mamy daną populację n osób, których wzrost nie przekracza 3m. Jak przybliżyć średni wzrost w populacji, tak aby wynik z prawdopodobieństwem 34 był różny od średniej o 10cm?


Duża liczba świadków. Kolejny powód, dla którego algorytmy randomizowane są efektywne w znajdowaniu rozwiązania to taki, że często problem posiada dużo świadków na "tak" lub na "nie", na przykład przy sprawdzaniu, czy wielomian wielu zmiennych jest zerowy. Kiedy dany wielomian jest rzeczywiście zerowy, to wartość wielomianu w każdym punkcie wynosi zero. Natomiast gdy wielomian nie jest zerowy, wtedy istnieje duża liczba punktów, w których wielomian przyjmuje niezerową wartość (zobacz Zadanie 1). Dlatego znalezienie jednego świadka spośród wielu jest może być łatwo zrealizowane poprzez wybranie małej losowej próby.

Balansowanie obciążenia. Randomizacja jest bardzo dobrą metodą na rozłożenie obciążenia. Dla przykładu rozważmy sytuację w której mamy n serwerów i n zadań. Jeżeli każde z n zadań zostanie przydzielone do jednego serwera w sposób losowy, wtedy z dużym prawdopodobieństwem żadna z maszyn nie otrzymuje więcej niż logn zadań. Możemy delikatnie poprawić tą prostą randomizowaną strategię: dla każdego zadania wybieramy losowo dwa serwery i przydzielamy to zadanie serwerowi o mniejszym obciążeniu. Można pokazać, że ten zmodyfikowany schemat z dużym prawdopodobieństwem nie przydzieli więcej niż loglogn zadań dla każdego serwera. W ten sposób, w przypadku miliona zadań, każdy serwer otrzyma co najwyżej pięć zadań. Stwierdzenie z dużym prawdopodobieństwem oznacza, że jakaś własność zajdzie z prawdopodobieństwem większym niż 12.

Kolejny przykład na balansowanie obciążenia można odnaleźć w algorytmach dla tablicach hashujących.

Łamanie symetrii. W obliczeniach równoległych i rozproszonych ważne jest, aby złamać symetrię między procesorami. Na przykład w sieci Ethernet, kiedy dwie maszyny wysyłają pakiet danych w tym samym czasie, następuje kolizja. Nie wiadomo, który procesor powinien spróbować ponownie przesłać pakiet. Gdy uczynią to obydwa, znów nastąpi kolizja. Najprostszym sposobem na uniknięcie tej sytuacji jest złamanie symetrii poprzez randomizację, np. każdy procesor rzuca monetą i próbuje ponownie wysłać pakiet, gdy wypadła mu reszka. Kolejnym przykładem jest problem znalezienia doskonałego skojarzenia w grafie na maszynie równoległej. Ważne jest, aby wszystkie procesory obliczały to samo skojarzenie, jednak nie wiadomo które, bo jeszcze go nie znaleźliśmy. Tutaj można posłużyć się randomizacją w celu złamania symetrii pomiędzy różnymi możliwymi skojarzeniami i odizolowaniem pojedynczego skojarzenia.

Derandomizacja. Niektóre proste algorytmy randomizowane mogą być przerobione na (nie takie proste) algorytmy deterministyczne poprzez derandomizację. Często w ten sposób dostaje się najefektywniejsze algorytmy deterministyczne.

Problem maksymalnego przekroju

W problemie maksymalnego przekroju dany jest graf G=(V,E), a wynikiem jest podzieł zbioru wierzchołków na dwa podzbiory (A,B). Krawędź jest rozcięta wtedy, gdy jej końce leżą po dwóch różnych stronach podziału. Istotą problemu jest znalezienie takiego podziału, który maksymalizuje liczbę krawędzi rozciętych. Problem ten jest NP-trudny. Używając randomizacji, skonstruujemy prosty 12–aproksymacyjny algorytm dla tego problemu.

Niech |V|=n i |E|=m.

Twierdzenie 1

Istnieje podział (A,VA) zbioru wierzchołków taki, że:
liczba krawędzi w zbiorze >|E|2=m2

Dowód

Budujemy zbiór A (to znaczy jedną stronę podziału) włączając każdy wierzchołek do A z prawdopodobieństwem 12. Zadajmy teraz pytanie: jaka jest oczekiwana liczba rozciętych krawędzi dla podziału (A,VA)? Aby odpowiedzieć na to pytanie, dla każdej krawędzi (i,j)E, zdefiniujmy
Xi,j={1jeżeli krawędź (i,j) jest rozcięta,0w przeciwnym przypadku.

Możemy teraz napisać, że liczba rozciętych krawędzi wynosi:

Liczba rozciętych krawędzi =X=(i,j)EXi,j.

Nasze pytanie sprowadza się teraz do wyznaczenia wartości [X]. Posłużymy się liniowością wartości oczekiwanych. Po pierwsze zauważmy, że [Xi,j]=12. Jest tak, ponieważ niezależnie od tego, czy i jest w zbiorze A czy nie, jest dokładnie jeden wybór dla j, który rozetnie krawędź (i,j). Stąd otrzymujemy:

[X]=(i,j)E[Xi,j]=(i,j)E12=m2.
Aby zakończyć dowód, zaobserwujmy, że musi istnieć podział dla którego liczba rozciętych krawędzi jest równa co najmniej średniej, czyli m2.

Twierdzenie to zobrazowane jest na następującej animacji.

Plik:Zasd ilustr s.svg

Zauważamy, że liczba rozciętych krawędzi w najlepszym podziale nie może przekraczać m. Powyższy algorytm daje nam przecięcie wielkości m2. Stąd oczekiwana aproksymacja wyniku to 12.

Uwaga
Istnieje prosty sposób na derandomizację powyższego algorytmu poprzez zastosowanie metody zachłannej. Włączamy pierwszy wierzchołek do A, a następnie analizujemy pozostałe wierzchołki, jeden za drugim. Włączamy do A wierzchołek wtedy i tylko wtedy, kiedy po włączeniu wierzchołka rozciętych jest więcej krawędzi niż w przypadku uprzednio rozpatrywanych wierzchołków.

Problem długiej ścieżki

W problemie znalezienia długiej ścieżki dany mamy graf G=(V,E) i liczbę całkowitą k. Problem ten polega na znalezieniu prostej ścieżki o długości k w grafie G. Zauważmy, że dla k=n1 problem ten jest równoznaczny ze znalezieniem ścieżki Hamiltona w grafie G. Pokażemy teraz algorytm, który działa w czasie wielomianowym dla k=O(lognloglogn). Zadanie 3 polega na poprawieniu tego algorytmu tak, aby działał w czasie wielomianowym dla k=O(logn).

Główny pomysł to wykożystanie tego, że dla skierowanego acyklicznego grafu (DAG-u) istnieje algorytm działający w czasie wielomianowym znajdujący najdłuższą ścieżkę.

Ćwiczenie

Korzystając z programowania dynamicznego, podaj algorytm znajdujący najdłuższą ścieżkę w DAGu, który działa w czasie O(|E|)?

Przypuśćmy, że przypisujemy wierzchołkom grafu G wartości ze zbioru {1,2,,n} losowo tak, że każda permutacja jest tak samo prawdopodobna. Ponadto, skierowujemy wszystkie krawędzie z grafu od mniejszych wartości do większych. W ten sposób otrzymujemy graf DAG, który oznaczamy przez G.

Twierdzenie 2

Jeśli G zawiera ścieżkę P o długości k, to wtedy P będzie skierowaną ścieżką w G z prawdopodobieństwem α=2(k+1)!

Dowód

Najpierw dla przykładu rozważmy ścieżkę nieskierowaną, przechodzącą przez wierzchołki a,b,c,d w G o długości 3. Będzie to skierowana ścieżka w G wtedy i tylko wtedy, gdy albo a<b<c<d, albo a>b>c>d w naszym przypisaniu. Generalnie, ścieżka P długości k będzie skierowaną ścieżką w G wtedy i tylko wtedy, kiedy k+1 wierzchołków na ścieżce P będzie miało przypisane wartości w sposób albo ściśle rosnący, albo ściśle malejący. Ze względu na to, że wszystkie permutacje k+1 wierzchołków są jednakowo prawdopodobne i tylko dwie z nich dają ścieżkę skierowaną w G, to wynika z tego, że P będzie ścieżką skierowaną w G z prawdopodobieństwem α=2(k+1)!.

Wniosek 3

Jeżeli graf G zawiera ścieżkę P o długości k, to wtedy G zawierać będzie skierowaną ścieżką o długości k z prawdopodobieństwem co najmniej α.

Wniosek ten możemy wykorzystać do konstrukcji następującego algorytmu.

Algorytm znajduje w grafie ścieżkę długości k


 ZNAJDŹ-ŚCIEŻKĘ(G,k)
1  for i=1 to 1α do
2  begin
3    przypisz losową permutację {1,,n} wierzchołkom G
4    używając tej permutacji skonstruuj G
5    if udało się znaleźć w G ścieżkę długości k then
6      return TRUE
7  end
8  return FALSE

Działanie tego algorytmu zobrazowane jest na następującej animacji.

Plik:Zasd ilustr t.svg

Twierdzenie 4

Powyższy algorytm znajduje ścieżkę o długości Parser nie mógł rozpoznać (błąd składni): {\displaystyle k = O(\frac{\log n}{\log \log n}})} , jeżeli taka istnieje, w randomizowanym czasie wielomianowym.

Dowód

Załóżmy, że istnieje ścieżka o długości k w grafie G. Chcemy obliczyć prawdopodobieństwo tego, że nie znajdziemy ścieżki o długości k w G we wszystkich t próbach. Prawdopodobieństwo to jest takie samo jak prawdopodobieństwo, że G nie posiada skierowanej ścieżki o długości k we wszystkich t próbach.
Pr[porażki we wszystkich t próbach](1α)teαt1e.
Pierwsza nierówność zachodzi, ponieważ w każdej próbie graf G nie posiada skierowanej ścieżki o długości k z prawdopodobieństwem co najwyżej (1α) (zgodnie z Wnioskiem 3), przy czym każda z prób jest niezależna. Druga nierówność jest konsekwencją tego, że 1+xex, dla wszystkich x. Dlatego też algorytm znajdzie ścieżkę o długości k (jeżeli taka istnieje) z prawdopodobieństwem co najmniej 11e. Czas działania algorytmu wynosi t×O(|E|)=(k+1)!2×O(|E|). Zauważ, że (k+1)!(k+1)k, czyli po wstawieniu k=clognloglogn otrzymujemy czas O(mnc), który jest wielomianowy dla każdej stałej c.