Algorytmy i struktury danych/Selekcja: Różnice pomiędzy wersjami
m |
|||
Linia 9: | Linia 9: | ||
Dla niektórych wartości <math>k</math> (np. 1 lub n) problem jest bardzo prosty i wymaga | Dla niektórych wartości <math>k</math> (np. 1 lub n) problem jest bardzo prosty i wymaga | ||
jedynie wyznaczenia minimalnej lub maksymalnej wartości w ''A'', co możemy zrobić w czasie <math>O(n)</math>. | jedynie wyznaczenia minimalnej lub maksymalnej wartości w ''A'', co możemy zrobić w czasie <math>O(n)</math>. | ||
− | Znacznie ciekawszym problemem jest np. znajdowanie mediany czyli | + | Znacznie ciekawszym problemem jest np. znajdowanie mediany, czyli |
<math>\lfloor \frac{n}{2} \rfloor</math>-tego co do wielkości elementu. | <math>\lfloor \frac{n}{2} \rfloor</math>-tego co do wielkości elementu. | ||
Tu niestety problem jest bardziej skomplikowany. | Tu niestety problem jest bardziej skomplikowany. | ||
Linia 42: | Linia 42: | ||
Ponieważ naszym zadaniem jest jedynie wyznaczenie ''k''-tego co do | Ponieważ naszym zadaniem jest jedynie wyznaczenie ''k''-tego co do | ||
wielkości elementu tablicy, możemy zamiast 2 wywołań rekurencyjnych | wielkości elementu tablicy, możemy zamiast 2 wywołań rekurencyjnych | ||
− | (jak to było w przypadku algorytmu QuickSort) | + | (jak to było w przypadku algorytmu QuickSort) wykonać tylko |
jedno wywołanie rekurencyjne. | jedno wywołanie rekurencyjne. | ||
Linia 74: | Linia 74: | ||
Niestety, w pesymistycznym przypadku ten algorytm może zachowywać się | Niestety, w pesymistycznym przypadku ten algorytm może zachowywać się | ||
bardzo źle. | bardzo źle. | ||
− | Dla uporządkowanego ciągu i ''k''=''n'' | + | Dla uporządkowanego ciągu i ''k''=''n'' czas działania algorytmu wynosi <math>O(n^2)</math>. |
Jednak tak jak w przypadku algorytmu QuickSort, w średni koszt działania algorytmu | Jednak tak jak w przypadku algorytmu QuickSort, w średni koszt działania algorytmu | ||
jest znacznie lepszy i wynosi ''O(n)''. | jest znacznie lepszy i wynosi ''O(n)''. | ||
Linia 118: | Linia 118: | ||
'''else''' '''begin''' | '''else''' '''begin''' | ||
podziel elementy z tablicy ''A'' na podciągi 5-elementowe: <math>P_1,\ldots,P_{n/5}</math> | podziel elementy z tablicy ''A'' na podciągi 5-elementowe: <math>P_1,\ldots,P_{n/5}</math> | ||
− | (jeśli n nie jest wielokrotnością 5, | + | (jeśli n nie jest wielokrotnością 5, uzupełnij ostatni podciąg wartościami <math>+\infty</math>) |
Niech <math>M=\{ P_i [3] : 1 \le i \le n/5 \}</math> (zbiór median ciągów <math>P_i</math>); | Niech <math>M=\{ P_i [3] : 1 \le i \le n/5 \}</math> (zbiór median ciągów <math>P_i</math>); | ||
m:=AlgorytmMagicznychPiatek(M, <math>\lceil |M|/2 \rceil</math>); | m:=AlgorytmMagicznychPiatek(M, <math>\lceil |M|/2 \rceil</math>); |
Aktualna wersja na dzień 22:19, 3 gru 2006
Selekcja
Rozważmy następujący problem:
Dany jest zbiór
składający się z liczb oraz liczba . Należy wyznaczyć -ty co do wielkości element zbioru tzn. takie , że .Dla niektórych wartości
(np. 1 lub n) problem jest bardzo prosty i wymaga jedynie wyznaczenia minimalnej lub maksymalnej wartości w A, co możemy zrobić w czasie . Znacznie ciekawszym problemem jest np. znajdowanie mediany, czyli -tego co do wielkości elementu. Tu niestety problem jest bardziej skomplikowany.Możemy zauważyć, że szybkie (np. o złożoności
) znajdowanie mediany pozwoliłoby tak poprawić algorytm sortowania QuickSort, żeby nawet w pesymistycznym przypadku działał w czasie .Najprostszym rozwiązaniem naszego problemu może być uporządkowanie zbioru, np. algorytmem MergeSort, i wskazanie k-tego elementu. Takie postępowanie wymaga czasu
.Jednak można łatwo zauważyć, że nasz problem jest znacznie prostszy od problemu sortowania. Czy można rozwiązać go szybciej? Okazuje się, że jest to możliwe. W następnej części wykładu przedstawimy dwa algorytmy, które rozwiązują nasz problem znacznie sprytniej.
Algorytm Hoare'a
Algorytm jest oparty na tym samym pomyśle, co algorytm sortowania QuickSort.
Dla danej tablicy A[1..n] oraz liczby k, algorytm wybiera element dzielący m (np. pierwszy element z tablicy), a następnie używa go do podzielenia tablicy na dwie części. Do pierwszej części tablicy - A[1..r] - zostają przeniesione elementy o wartościach mniejszych lub równych m. Do drugiej części (A[r+1..n]) - elementy o wartościach większych lub równych m.
Ponieważ naszym zadaniem jest jedynie wyznaczenie k-tego co do wielkości elementu tablicy, możemy zamiast 2 wywołań rekurencyjnych (jak to było w przypadku algorytmu QuickSort) wykonać tylko jedno wywołanie rekurencyjne.
Jeśli
, to poszukiwana wartość znajduje się w pierwszej części tablicy, wpp. możemy zawęzić poszukiwania do drugiej części tablicy, jednak zamiast wyszukiwać k-tej wartości musimy poszukiwać (k-r)-tej wartości.Algorytm Hoare'a
1 function AlgHoara(A[1..n],k); 2 begin 3 if n=1 and k=1 then return A[1]; 4 // Partition 5 m:=A[1]; l:=1; r:=n; 6 while(l<r) do begin 7 while (A[l]<m) do l++; 8 while (m<A[r]) do r--; 9 if (l<=r) then begin 10 tmp:=A[l]; A[l]:=A[r]; A[r]:=tmp; 11 l++; r--; 12 end; 13 end; 14 if (k<=r) then 15 return AlgHoara(A[1..r],k) 16 else 17 return AlgHoara(A[r+1..n],k-r) 18 end;
Analiza algorytmu
Niestety, w pesymistycznym przypadku ten algorytm może zachowywać się bardzo źle. Dla uporządkowanego ciągu i k=n czas działania algorytmu wynosi
. Jednak tak jak w przypadku algorytmu QuickSort, w średni koszt działania algorytmu jest znacznie lepszy i wynosi O(n).Algorytm magicznych piątek
Algorytm Hoare'a w pesymistycznym przypadku może wymagać bardzo długiego czasu działania. Możemy tak zmodyfikować poprzedni algorytm, aby zapewnić liniowy czas działania nawet w najgorszym przypadku.
Kluczem do nowego algorytmu jest lepszy wybór elementu dzielącego (zmienna m z 5-linii algorytmu Hoare'a). Element ten jest obliczany w następujący sposób:
- dzielimy ciąg

- każdy z podciągów sortujemy, otrzymując ,,
- wybieramy z każdego podciągu 3-ci co do wielkości element otrzymując krótszy ciąg

- jako m wybieramy medianę ciągu (którą to obliczamy rekrurencyjnie).
Dzięki takiemu znacznie bardziej skomplikowanemu wyborowi możemy zagwarantować bardziej równomierny podział ciągu
na podciągi elementów mniejszych ( ), oraz większych ( ) od m.Algorytm
function AlgorytmMagicznychPiatek(A[1..n], k); begin if n<=10 then posortuj tablicę A return A[k] else begin podziel elementy z tablicy A na podciągi 5-elementowe:(jeśli n nie jest wielokrotnością 5, uzupełnij ostatni podciąg wartościami ) Niech (zbiór median ciągów ); m:=AlgorytmMagicznychPiatek(M, ); ; ; ; if then return AlgorytmMagicznychPiatek( , k) else if return m else return AlgorytmMagicznychPiatek( , ); end end
Analiza złożoności czasowej
Na pierwszy rzut oka algorytm wygląda bardzo podobnie do algorytmu Hoare'a. Nowy algorytm jest jednak znacznie bardziej efektywny: nawet w pesymistycznym przypadku algorytm kończy działanie po
krokach.Złożoność algorytmu możemy opisać następującym równaniem rekurencyjnym.
- rozmiar zbioru |M| możemy ograniczyć przez
- ze względu na dodatkowy czas poświęcony na obliczanie mediany m, możemy podać lepsze ograniczenia na rozmiary zbiorów :
