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 mi-
nimum 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, bn/2c) w = w ∗ w
if n mod 2 = 1 then w = w ∗ a return w;
2 Liczby Fibonacciego
F0= 0, F1= 1, Fn= Fn−1+ Fn−2(for n ≥ 2) Ciekawe własności:
1 1 1 0
n
=
Fn+1 Fn
Fn Fn−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ź. (3dn/2e − 2 porównań)
• Q = ∅
• for i = 1 to dn/2e do Q.push(pair(min(A[2i − 1], A[2i]), max(A[2i − 1], A[2i])))
• while |Q| > 1 do – (a1, b1) = Q.P OP , – (a2, b2) = Q.P OP ,
– Q.P U SH(min(a1, a2), max(b1, b2))
• 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 po- ró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:
• (a1, a2) — odpowiada a1< a2, zmiana (−2, +1, +1, 0)
• (b1, b2) — odpowiada b1< b2, zmiana (0, −1, 0, +1)
• (c1, c2) — odpowiada c1< c2, zmiana (0, 0, −1, +1)
• (a1, b1) — odpowiada a1< b1, zmiana (−1, 0, +1, 0)
• (a1, c1) — odpowiada a1> c1, zmiana (−1, +1, 0, 0)
• (a1, d1) — odpowiada a1> d1, zmiana (−1, +1, 0, 0)
• (d1, d2) — odpowiada d1< d2, zmiana (0, 0, 0, 0)
• (b1, c1) — odpowiada b1> c1, zmiana (0, 0, 0, 0)
• (b1, d1) — odpowiada b1> d1, zmiana (0, 0, 0, 0)
• (c1, d1) — odpowiada c1< d1, zmiana (0, 0, 0, 0)
Dowolny algorytm musi zadać co najmniej dn/2e 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 prze- chodzi wygrany) — ten krok zabiera n − 1 porównań,
• niech S zbiór elementów które przegrały z liderem, |S| = dlog ne
• wybierz lidera wśród elementów S — ten krok zabiera |S| − 1 = dlog ne − 1 porównań.
• razem n + dlog ne − 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 (x1, . . . , xm+r) oraz (y1, . . . , yn+r) będą dowolnymi ciągami liczbowymi takimi, że yi≤ xm+idla 1 ≤ i ≤ r. Jeśli elementy x oraz y posortujemy niezależnie tak, że x1 ≤ . . . ≤ xm+r oraz y1≤ . . . ≤ yn+r to nadal będziemy mieli yi≤ xm+i dla 1 ≤ i ≤ r.
Dowód. Po posortowaniu element xm+ijest 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 xm+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)
Lemat 2. Jeśli tablica jest h posortowana i k posortujemy, to nadal będzie h posortowana.
Dowód. Niech ai i ai+h elementy które po sortowaniu nie są h posortowane.
Niech Y ciąg zawierający ai, ai+k, ai+2k, . . .. Niech X ciąg zawierający ai+h, ai+h+k, ai+h+2k, . . .. Po k posortowaniu ciągi Y i X są uporządkowane, z poprzedniego lematu mamy jednak, że ai≤ ai+h — sprzeczność.
Lemat 3. Liczba porównań wymagana przy h posortowaniu tablicy rozmiaru n wynosi O(n2/h).
Dowód. Mamy h ciągów, każdy o długości n/h — stąd całkowity czas wynosi h · n2/h2= n2/h.
Lemat 4. Liczba porównań wymagana przy hi posortowaniu tablicy rozmiaru n, która jest hi+1 i hi+2 posortowana wynosi O(nhi+1hi+2/hi) (przy założeniu, że hi+1 i hi+2 są względnie pierwsze)
Dowód. Trzeba pokazać, że jeśli ciąg jest hi+1 i hi+2 posortowany, to jeśli k ≥ hi+1hi+2, to ai≤ ai+k.
Lemat 5. Dla ciągu h = {2i− 1 : i ∈ N } algorytm ShellSort ma złożoność O(n√
n).
Dowód. (Knuth, tom III, strona 95)
Niech Bi koszt i–tej fazy, t = dlog ne. Dla pierwszy t/2 przebiegów h ≥√ n, ponieważ koszt jednej fazy jest ograniczona przez O(n2/h) stąd sumaryczny koszt jest rzędu O(n1.5). Dla pozostałych przebiegów możemy skorzystać z poprzedniego lematu, koszt pojedynczej fazy jest równy Bi= O(nhi+2hi+1/hi), więc sumaryczny koszt tych faz jest również rzędu O(n1.5).
Lemat 6. Dla ciągu h = {2i3j : i, j ∈ N } algorytm ShellSort ma złożoność O(n log2n).
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)
d
c a
b
• 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ń.