ALGORYTMY I STRUKTURY DANYCH
WYKŁAD 03 cd. Wyszukiwanie Grażyna Mirkowska
PJWSTK, 2003/2004
Plan wykładu
Drugi co do wielkości element
– Algorytm turniej
K-ty co do wielkości element ciągu
– Algorytm Hoare
Wyszukiwanie w ciągu uporządkowanym
– Algorytm bisekcji
Problem Dany jest ciąg różnych elementów e[1],...,e[n]
pewnej przestrzeni liniowo uporządkowanej < E, >.
Znaleźć drugi co do wielkości element tego ciągu.
WP = { e[i] e[j] dla i j , n>0},
WK = {1 wynik n, e[j] e[wynik] < e[max] dla j=1,2,...,n }
e[wynik] = maximum({e[1]...,e[n]} - maximum{e[1],...,e[n]})
Algorytm naiwny : 1. Znaleźć element maksymalny. 2. Usunąć go
Drugi największy element ciągu
{
i := 2; max :=1;
while i n do
if e[i] > e[max] then max := i fi;
i := i+1;
od;
pom := e[1]; e[1] := e[max]; e[max] := pom;
i:=3; wynik := 2;
while i n do
if e[i] > e[wynik] then wynik := i fi;
i := i+1;
od;
Swap(e[1], e[max]);
T(n) = 2n -3
Max: = 1;
for ( i =2; i<= n; i++){
if (e[i]>e[max]) {max:=i;}
}
wynik := 2;
for ( i =3; i<= n; i++){
if (e[i]>e[wynik]){wynik:=i;}
}
e[max] := maksimum(1,n);
e[wynik] := maksimum(2,n);
Algorytm naiwny
Metoda polega na porównywaniu sąsiednich elementów ciągu. Elementy większe
(wygrywające) przechodzą do następnej ‘rundy’.
2 3
4 5 7 8 1 6
4 5 8 6
5 8
Czy można to zrobić lepiej?
Każdy element, z wyjątkiem
maksymalnego przegrał co najwyżej raz.
Element drugi co do wielkości przegrał jedynie z elementem maksymalnym.
Wśród elementów,
które grały z największym!
Por. przykład
Analiza metody
Krok1. Zbudowanie drzewa turnieju.
Załóżmy, że n= 2k.
Krok 2. Wybranie elementu drugiego
największego. lg n -1
A ile elementów przegrało
z największym?
Tyle, ile było ‘rund’!
n -1
porównań
T(n)= n + lg n -2
Koszt algorytmu Turniej
Problem: Dany jest ciąg n-elementów pewnej przestrzeni liniowo uporządkowanej <E, >.
Znaleźć k-ty największy element tego ciągu.
2, 4, 6, 12, 78, 45, 3, 33, 17, 22 Element największy = 78
element drugi co do wielkości = 45 3-ci największy = 33
4-ty największy = 22
k-ty największy element
Krok1. Wyszukaj element e[max] największy wśród elementów e[i],...,e[n];
Krok 2. Zamień elementy na pozycjach i-tej oraz max
Krok 3. Powtórz postępowanie dla następnego i.
T(n) = (n-1) + (n-2) +... +(n-k) = k*n - k*(k+1)/2
Pierwsze rozwiązanie
Zakładam, że elementy w ciągu e nie powtarzają się.
{ x := 1;
while x k do max := x;
for i := x+1 to n do
if e[i] > e[max] then max := i fi od;
swap(e[x], e[max]);
x := x+1;
od;
wynik := e[k]
}
e[1]>...>e[x-1] >{e[x],...,e[n] }
e[max] {e[x],...,e[n]}
e[1]>...>e[x-1] >{e[x],...,e[n] }
Algorytm naiwny
Rozdziel wszystkie elementy na większe od pewnego elementu M(część starsza) i na mniejsze od M (część młodsza).
M = mediana
Umieść medianę tak by oddzielała cześć młodszą od starszej.
Wynikiem jest mediana, jeśli w części starszej jest tylko k-1 elementów.
W przeciwnym przypadku: jeśli elementów starszych jest >k-1, to szukaj k-tego elementu w części starszej. Jeśli elementów starszych jest mniej niż k-1, to szukaj elementu
k-(liczba elementów starszych+1)
Czy można to zrobić taniej?
10 5 7 9 11 4 3 2 12 8 6 1
W podanym ciągu szukamy 7tego co do wielkości elementu
mediana
Część młodsza 5 7 9 4 3 2 6 1
Część starsza 11 12
10
5 7 9 4 3 2 6 1 mediana
Część młodsza 4 3 2 1
Część starsza 7 9 6
5
Szukam 4-go największego
Wynikiem jest element 5
Przykład
function Hoare(l, p, k) { j := SPLIT(l, p);
if ( p-j = k-1) then wynik := e[j]
else
if p-j>k-1 then Hoare(j+1, p, k) else
Hoare(l,j-1, k-(p-j+1)) fi
{e[1]...,e[j-1]}< e[j]<{e[j+1],...,e[n]}
K-ty największy znajduje się wśród elementów e[j+1],... e[p]
K-ty największy znajduje się wśród elementów e[l],... e[j-1]
Zakładam, że elementy w ciągu nie powtarzają się i, że algorytm zwraca jako wynik wartość k-tego największego elementu.
Algorytm Hoare
int function SPLIT(lewy,prawy){
mediana := e[lewy];
i := lewy+1; j := prawy;
bool := true;
while (bool) do
while (j>lewy andif e[j] mediana ) do j := j-1 od;
while (i<j andif e[i] < mediana) do i := i+1 od;
if (i<j) then
swap(e[i], e[j]); i := i+1; j := j-1;
else bool := false fi od;
swap(e[lewy],e[j]);
return j;
(k, lewy< k <j) e[k] < e[j]
(k, j < k prawy) e[j] e[k]
Algorytm rozdzielania
powrót
10, 5, 9, 8, 14, 7, 12, 3, 11
i j
mediana
14 3
10, 5, 9, 8, 3, 7, 12, 14, 11
i j
mediana
10, 5, 9, 8, 3, 7, 12, 14, 11
7 10
i < j
i > j powrót
Jak to działa?
Każdy element jest co najwyżej raz porównywany z medianą.
Koszt algorytmu SPLIT
Czyli T(SPLIT, n ) = n-1 = (n)
W( n,k) = n-1 +W( n-1,k) Czyli W(n,k)= k*n – k(k+1)/2 A(n,k) = (n-1) + 1/n[ j=1...n-k A(n-j, k) + j=n-k+2... n A(j-1,k – (n-j+1)]
Szukanie w części starszej Szukanie w części młodszej
A(n,k) = O(n)
Koszt algorytmu Hoare
Wyszukiwanie w ciągu uporządkowanym
Problem Dany jest ciąg rosnący e[1],..,e[n] oraz element x pewnej przestrzeni liniowo uporządkowanej <E, >. Chcemy ustalić
przedział e[i], e[i+1], w którym ewentualnie mieści się x.
Specyfikacja
Specyfikacja
: wp= {(i<n) e[i]<e[i+1] , n>0 }wk= {wynik= i wttw x należy do itego przedziału}
Metoda sekwencyjna
1. Zbadamy przypadki skrajne: przedział 0 i przedział n-ty i ew. kończymy . 2. Następnie porównujemy x z kolejnymi elementami ciągu poczynając od e[2].
e[1] e[2] ... e[n-1] e[n]
Przedział 0 Przedział 1 Przedział n-1 Przedział n
e[3]
Metoda poszukiwań sekwencyjnych
{
if x<e[1] then i := 0 else
if x e[n] then i := n else i := 1;
while x e[i+1] { i := i+1;
} fi;
fi;
wynik := i;
}
e[i] x e[i+1], i n
e[i]x e[n], 1i n
e[i]x , xe[i+1], i+1 n xe[i] , i n
Koszt Koszt
algorytmu algorytmu
W skrajnych przypadkach mamy 1 lub 2 porównania W najgorszym razie 2 + (n-1) porównań.Czyli W(n) = O(n).
Poniważ x e[i+1],
to i+1 nie może być równe n,bo z wcześniejszych porównań wiemy, że
x<e[n]. Czyli i+1<n
Koszt średni algorytmu sekwencyjnego
Rozszerzamy dany ciąg o elementy a i b, tzn. przyjmujemy e[0] = a i e[n+1] =b, gdzie [a,b] jest przedziałem, w którym mieszczą się
elementy ciągu oraz x. Załóżmy, że prawdopodobieństwo tego, że x przyjmuje jakąś wartość z przedziału [a,b] jest zawsze takie samo.
Mamy p(x [e[i],e[i+1])) = (e[i+1] – e[i])/(b-a) Koszt oczekiwany algorytmu A(n) =
)) 1 (
]) 1 [
] [ ( ...
3 ]) 1 [ ] 2 [ (
2 ]) [ ] 1 [
( 1 ]) 0 [ ] 1 [ 1 (
) 2 ] (
[ ] 1 2 [
] [ ] 1 1 [
] 0 [ ] 1 ) [
( 1
1
n n
e n
e e
e
n e n
e e
e a b
a i b
i e i
e a
b
n e n
e a
b e n e
A n
i
] [ ) 1 (
] [ ]
1 [ 2
1 2 1
b a a b e n e i n e n
Jeśli długości przedziałów są takie same, to
Algorytm bisekcji
Metoda „dziel i zwyciężaj”
Podziel dany ciąg na dwie części. Sprawdź, w której połowie ciągu znajduje się poszukiwany element i dalej szukaj tą samą metodą w tej właśnie połowie.
{ i :=1; j := n;
while j-i >1 do m := (i+j) div 2;
if e[m] x then i := m else j := m
fi od;
wynik := i
wp : e[1] x < e[n], n>1
wk : e[wynik] x< e[wynik+1]
Niezmiennik e[i] x e[j], i j
1+i = j oraz e[i] x e[j]
Koszt algorytmu bisekcji
Czy ten algorytm zatrzymuje się ? Czy ten algorytm zatrzymuje się ?
Niech k będzie liczbą wykonanych iteracji pętli oraz odl(k) = j - i.
Przed wykonaniem pętli mamy k=0 i odl(k) = n-1.
odl(k+1) = odl(k)/2, jeśli odl(k) jest liczbą parzystą (odl(k)-1)/2 odl(k+1) (odl(k)+1)/2 ,
jeśli odl(k) jest liczbą nieparzystą
Odl(k) jest liczbą całkowitą z przedziału [1,n-1 ] i ze wzrostem k maleje!
Istnieje zatem takie k, że odl(k)=1. A więc algorytm zatrzymuje się.
Następne slajdy będą wykorzystane na
ćwiczeniach
Skoki „co 4”
Idea Porównujemy element x z co czwartym elementem danego ciągu tak długo aż znajdziemy element większy od x. Wtedy szukamy x
sekwencyjnie wśród czterech poprzedzających go elementach ciągu.
Metoda dokładniej:
1 krok: Zbadać czy x [e[1],e[n]). Jeśli nie, to ustalić wynik zgodnie ze specyfikacją, a jeśli tak to wykonać krok 2.
2 krok: Porównywać x z kolejnymi elementami o indeksach 4i, tak długo aż (a) znajdziemy element e[4i]>x lub (b) aż przekroczymy n (a) szukamy wyniku sekwencyjnie w przedziale [e[4i- 4], e[4i]),
omiń
Algorytm skoki „co 4”
{ if x < e[1] then i :=0 else if x e[n] then i := n else i := 4; bool := false;
while (i n and not bool) do if x e[i] then
i := i + 4 else
bool := true fi
od;
i := i- 4;
while x e[i+1] do i := i+1 od;
e[1] x e[n]
e[j] x dla j=1,2,...,i-4, bool e[j] x dla j=1,2,...,i, bool, in e[j] x dla j=1,2,...,i-4, bool , in+4
e[j] x dla j=1,2,...,i-4, x<e[i], bool bool oraz e[i- 4] x<e[i] lub bool oraz e[i- 4] x<e[n]
Koszt
pesymistyczny:
Skoki „co k”
{ if x < e[1] then i :=0 else if x e[n] then i := n else i := k; bool := false;
while (i n and not bool) do if x e[i] then
i := i + k else
bool := true fi
od;
i := i-k;
e[j] x<e[n] dla j=1,2,...,i-k, i n+k
Optymalne k
Dla jakich wartości k algorytm ma najmniejszy koszt pesymistyczny?
Koszt pesymistyczny wyraża się funkcją f(n) = 2 + n/k + ( k-1)
Szukamy minimum tej funkcji:
f’(k) = -n/k2 + 1 f’(k) = 0 dla k = n oraz f’’ ( n)>0
Koszt pesymistyczny będzie najmniejszy, gdy k = n