Algorytm selekcji Hoare`a

Transkrypt

Algorytm selekcji Hoare`a
Algorytm selekcji Hoare’a
Łukasz Miemus
1 lutego 2006
Rozdział 1
O algorytmie
1.1
Problem
Mamy tablicę A[N] różnych elementów i zmienną int K, takie że 1 ¬ K
¬ N. Oczekiwane rozwiązanie to określenie K-tego najmniejszego elementu
tablicy A[] w taki sposób, aby element ten znalazł się na pozycji A[K] w
tablicy oraz wszystkie elementy o indeksach mniejszych od K były mniejsze
wartościami od A[K], natomiast elementy od indeksach większych od K były
od A[K] większe wartościami:
A[1], A[2], ..., A[K-1] ¬ A[K] ¬ A[K+1], ..., A[N]
1.2
1.2.1
Wybrane rozwiązania
Algorytm naiwny
Algorytm ten opiera się na początkowym posortowaniu tablicy A[], wtedy
znalezienie K-tego najmniejszego elementu przeprowadzamy w czasie O(1),
a więc złożoność algorytmu wynosi:
• Sortowanie elementów tablicy (merge-sort lub heapsort) ⇒ O(n log n)
• Zwrócenie K-tego elementu ⇒ O(1)
O(n log n), co wydaje się być wynikiem zadowalającym — natomiast my
pokażemy, że problem ten da się rozwiązać w średnim czasie O(n), stosując
algorytm selekcji Hoare’a.
1.2.2
Algorytm Hoare’a
Definicja: K-ty najmniejszy z N elementów to unikalny element, który jest
większy od K-1 innych elementów, ale mniejszy od N-K elmentów.
1
Wniosek: Element nie może być K-tym najmniejszym jeśli jest większy od
K (i więcej) elementów lub mniejszy od N-K+1 (i więcej) elementów.
Zaczynamy od hipotezy: A[K] jest K-tym najmniejszym elementem. Algorytm dzieli tablicę A na dwie części poprzez skanowanie jej od lewej strony
(dla indeksów I = 1,2,...) w poszukiwaniu elementu A[I] ­ A[K], skanowanie
od prawej strony (dla indeksów J = N,N-1,...) w poszukiwaniu elementu A[J]
¬ A[K], oraz zamianę znalezionych elementów; procedura jest powtarzana
dopóki wskaźniki I oraz J się nie spotkają.
Daje nam to trzy przypadki:
1. (J < K < I)
K-ty najmniejszy element jest na wyjściowej pozycji i procedura kończy działanie.
2. (J < I ¬ K)
Elementy A[1]...A[J] są mniejsze od N-K+1 innych elementów, zatem żaden z nich nie może być K-tym najmniejszym, przeglądniemy
więc podtablicę A[I]...A[N] w poszukiwaniu (K-I+1)-tego najmniejszego elementu.
2
3. (K ¬ J < I)
Elementy A[I]...A[N] są większe od K innych elementów, zatem żaden
z nich nie może być K-tym najmniejszym, przeglądniemy więc podtablicę A[1]...A[J] w poszukiwaniu K-tego najmniejszego elementu.
1.2.3
Analiza złożoności algorytmu Hoare’a
• Złożoność pesymistyczna
Dla podanej wersji algorytmu, najgorszy z możliwych przypadków to
trafienie, jako elementu podziału, największego lub najmniejszego wartością elementu z tablicy A. Powodem jest strategia wyboru — w naszym przypadku elementem tym jest po prostu element o K-tym indeksie przeszukiwanej tablicy. Istnieje zatem określony zestaw danych,
dla których algorytm zawsze będzie działał w czasie pesymistycznym:
O(n2 )
Przykład dla K = 1:
A[1] = N, A[2] = 2, A[3] = 1, A[4] = 3, ..., A[N] = N-1
Aby uniknąć takiej sytuacji, można skorzystać z innej wersji algorytmu
— wybierać za każdym razem element podziału losowo. Należy jednak
mieć na uwadze fakt, że nie zmieni to rzędu złożoności!
• Złożoność oczekiwana
Oczekiwany wynik wyboru elementu podziału można rozbić na dwa
przypadki. W pierwszym przeszukujemy podtablicę o wielkości co najwyżej 3N
4 , w drugim (pesymistycznie) podtablicę o wielkości N. Prawdopodobieństwo wystąpienia dla każdego z tych wyników wynosi 50%,
zatem mamy:
Tn ¬
1
1
(Tn ) + (T 3N ) + N.
2
2 4
3
Tn ¬ T 3N + 2N.
4
Tn = O(n)
algorytm działa w czasie liniowym.
4
Rozdział 2
Przykład
Ilustracje przedstawiają wszystkie wywołania procedury dla danych zapisanych w tablicy A[10], poszukiwanym elementem jest 6-ty najmniejszy.
1. Procedurę wywołujemy na całej tablicy wejściowej.
2. Elementem, względem którego będzie wykonywany podział, jest A[6]
= 4. Ponieważ elementy A[1]...A[J] są mniejsze od N-K+1 (= 5) innych
elementów, żaden z nich nie może być poszukiwanym przez nas 6-tym
najmniejszym — zatem wywołamy procedurę ponownie na podtablicy
A[I]...A[N], szukając (K-I+1 = 2)-tego najmniejszego elementu.
3. Elementem podziału jest tym razem A[2] = 8. Ponieważ każdy z elementów A[I]...A[N] jest większy od co najmniej K (= 2) innych elementów, żaden z nich nie może być poszukiwanym 2-gim najmniejszym —
wywołujemy procedurę na podtablicy A[1]...A[J], szukając (K = 2)tego najmniejszego elementu.
5
4. Dzielimy względem A[2] = 5. Widać, że na powstałej podtablicy żaden
podział nie ma już sensu, 6-ty najmniejszy element został odnaleziony
— jest nim A[6] = 6, program kończy działanie.
6
Rozdział 3
Implementacja
#include <s t d i o . h>
#define N 10
int main ( void ) {
int A[N+1] = { 0 , 1 0 , 8 , 3 , 1 , 9 , 4 , 6 , 5 , 2 , 7 } ;
int K = 6 ;
int L = 1 , R = N;
int I , J , X;
while (L < R) {
X = A[K ] ;
I = L , J = R;
while ( 1 ) {
while (A[ I ] < X) I ++;
while (X < A[ J ] ) J−−;
i f ( I <= J ) {
int W = A[ I ] ;
A[ I ] = A[ J ] ;
A[ J ] = W;
I ++; J−−;
} e l s e break ;
}
i f ( J < K) L = I ;
i f (K < I ) R = J ;
}
p r i n t f ( ”%d n a j m n i e j s z y elem en t to : %d\n” , K, A[K ] ) ;
return 0 ;
}
7