Algorytmy i Struktury Danych, 2. ćwiczenia 2015-10-09 Spis treści 1 Szybkie potęgowanie 1 2 Liczby Fibonacciego 2 3 Dowód, że n 1 porównań jest potrzebne do znajdowania minimum 2 4 Optymalny algorytm do znajdowania min i max jednocześnie 2 5 Optymalne znajdowanie drugiego co wielkości elementu 3 6 Sortowanie metodą Shella 3 7 Optymalne sortowanie 5 ciu elementów 4 1 Szybkie potęgowanie Function Pow1(a, n) w = 1 foreach i = 1..n do w = w a return w; Function Pow2(a, n) if n == 0 then return 1 else if n == 1 then return a else w = P ow2(a, n/2 ) w = w w if n mod 2 = 1 then w = w a return w; 1
2 Liczby Fibonacciego Ciekawe własności: F 0 = 0, F 1 = 1, F n = F n 1 + F n 2 (for n 2) ( 1 1 1 0 ) n = ( ) Fn+1 F n F n F n 1 3 Dowód, że n 1 porównań jest potrzebne do znajdowania minimum Weźmy algorytm, A, powiedzmy, za każdym razem, gdy porównuje on dwa elementy, to łączymy je krawędzią. Jeśli A użył mniej niż n 1 porównań, to istnieją dwa elementy, które nie są ze sobą porównywalne. 4 Optymalny algorytm do znajdowania min i max jednocześnie Algorytm dziel i rządź. (3 n/2 2 porównań) Q = for i = 1 to n/2 do Q.push(pair(min(A[2i 1], A[2i]), max(a[2i 1], A[2i]))) while Q > 1 do (a 1, b 1 ) = Q.P OP, (a 2, b 2 ) = Q.P OP, Q.P USH(min(a 1, a 2 ), max(b 1, b 2 )) return Q.P OP Jest to również optymalna liczba porównań. (Knuth, Tom 3, ćwiczenie 16, strona 231). Niech (a, b, c, d) oznacza stan obliczeń algorytmu, a liczba elementów, które nie były jeszcze porównywane, b liczba elementów, które były porównywane i nie przegrały żadnego porównania, c liczba elementów, które były porównywane i przegrały wszystkie porównania, d liczba elementów, które wygrały co najmniej jedno porównanie, i przegrały co najmniej jedno porównanie. Dowolny algorytm zaczyna obliczenia w stanie (n, 0, 0, 0) i powinien kończyć w (0, 1, 1, n 2) (jeśli kończy w innym to łatwo podać kontprzykład). Konstrukcja przeciwnika dla algorytmu. Dla zapytania (x, y) postaci: 2
(a 1, a 2 ) odpowiada a 1 < a 2, zmiana ( 2, +1, +1, 0) (b 1, b 2 ) odpowiada b 1 < b 2, zmiana (0, 1, 0, +1) (c 1, c 2 ) odpowiada c 1 < c 2, zmiana (0, 0, 1, +1) (a 1, b 1 ) odpowiada a 1 < b 1, zmiana ( 1, 0, +1, 0) (a 1, c 1 ) odpowiada a 1 > c 1, zmiana ( 1, +1, 0, 0) (a 1, d 1 ) odpowiada a 1 > d 1, zmiana ( 1, +1, 0, 0) (d 1, d 2 ) odpowiada d 1 < d 2, zmiana (0, 0, 0, 0) (b 1, c 1 ) odpowiada b 1 > c 1, zmiana (0, 0, 0, 0) (b 1, d 1 ) odpowiada b 1 > d 1, zmiana (0, 0, 0, 0) (c 1, d 1 ) odpowiada c 1 < d 1, zmiana (0, 0, 0, 0) Dowolny algorytm musi zadać co najmniej n/2 zapytań typu (a, ), aby zmniejszyć licznik a o n, a co za tym idzie liczniki b i c zostaną sumarycznie zwiększone o n. Żeby zmniejszyć liczniki b i c o n 2 należy wykonać co najmniej n 2 zapytań typu (b c, ). 5 Optymalne znajdowanie drugiego co wielkości elementu budujemy drzewo turniejowe (porównujemy sąsiednie elementy, dalej przechodzi wygrany) ten krok zabiera n 1 porównań, niech S zbiór elementów które przegrały z liderem, S = log n wybierz lidera wśród elementów S ten krok zabiera S 1 = log n 1 porównań. razem n + log n 2 Dowód, że algorytm jest optymalny. Knuth, tom III, 5.3.3. strona 221. 6 Sortowanie metodą Shella Lemat 1. Niech m, n, r będą nieujemnymi liczba całkowitymi i niech (x 1,..., x m+r ) oraz (y 1,..., y n+r ) będą dowolnymi ciągami liczbowymi takimi, że y i x m+i dla 1 i r. Jeśli elementy x oraz y posortujemy niezależnie tak, że x 1... x m+r oraz y 1... y n+r to nadal będziemy mieli y i x m+i dla 1 i r. Dowód. Po posortowaniu element x m+i jest większy bądź równy od co najmniej m + i elementów z x, wśród nich jest co najmniej i elementów które przed sortowaniem były na pozycjach m,..., m+r, każdy z tych elementów ma wśród y element od którego jest większy, stąd x m+i jest większy bądź równy od i najmniejszych elementów y. (pełny dowód jest w Knuth, tom III, strona 94) 3
Lemat 2. Jeśli tablica jest h posortowana i k posortujemy, to nadal będzie h posortowana. Dowód. Niech a i i a i+h elementy które po sortowaniu nie są h posortowane. Niech Y ciąg zawierający a i, a i+k, a i+2k,.... Niech X ciąg zawierający a i+h, a i+h+k, a i+h+2k,.... Po k posortowaniu ciągi Y i X są uporządkowane, z poprzedniego lematu mamy jednak, że a i a i+h sprzeczność. Lemat 3. Liczba porównań wymagana przy h posortowaniu tablicy rozmiaru n wynosi O(n 2 /h). Dowód. Mamy h ciągów, każdy o długości n/h stąd całkowity czas wynosi h n 2 /h 2 = n 2 /h. Lemat 4. Liczba porównań wymagana przy h i posortowaniu tablicy rozmiaru n, która jest h i+1 i h i+2 posortowana wynosi O(nh i+1 h i+2 /h i ) (przy założeniu, że h i+1 i h i+2 są względnie pierwsze) Dowód. Trzeba pokazać, że jeśli ciąg jest h i+1 i h i+2 posortowany, to jeśli k h i+1 h i+2, to a i a i+k. Lemat 5. Dla ciągu h = {2 i 1 : i N} algorytm ShellSort ma złożoność O(n n). Dowód. (Knuth, tom III, strona 95) Niech B i koszt i tej fazy, t = log n. Dla pierwszy t/2 przebiegów h n, ponieważ koszt jednej fazy jest ograniczona przez O(n 2 /h) stąd sumaryczny koszt jest rzędu O(n 1.5 ). Dla pozostałych przebiegów możemy skorzystać z poprzedniego lematu, koszt pojedynczej fazy jest równy B i = O(nh i+2 h i+1 /h i ), więc sumaryczny koszt tych faz jest również rzędu O(n 1.5 ). Lemat 6. Dla ciągu h = {2 i 3 j : i, j N} algorytm ShellSort ma złożoność O(n log 2 n). Dowód. Wszystkich faz algorytmu jest O((log n) 2 ). Trzeba pokazać, że każda z nich zajmuje O(n) czasu. Obserwacja jeśli ciąg jest 2 i 3 uporządkowany, to jego 1 posortowanie wymaga O(n) czasu. Analogicznie jeśli ciąg jest 2i i 3i posortowany to jego i posortowanie wymaga O(n). 7 Optymalne sortowanie 5 ciu elementów Niech A = (a, b, c, d, e). compare(a, b), (niech a < b) compare(c, d), (niech c < d) compare(a, c), (niech a < c) c a d b 4
teraz wkładamy, e pomiędzy a, c, d, if (e > c) then compare(e, a) else compare(e, d) możemy otrzymać jeden z następujących posetów: każdy z nich można posortować używając 2 porównań. 5