Konstrukcija i analiza algoritama Nina Radojičić februar 2018. 1 Analiza algoritama, rekurentne relacije 1 Definicija: Neka su f i g dve pozitivne funkcije od argumenta n iz skupa N prirodnih brojeva. Kaže se da je f(n) = O(g(n)) ako postoje pozitivne konstante c i N 0 takve da za svako n > N 0 važi: f(n) c g(n). Važi: O(f(n)) + O(g(n)) = O(f(n) + g(n)), O(f(n)) O(g(n)) = O(f(n) g(n)), a ne važi: O(f(n)) O(g(n)) = O(f(n) g(n)) (kontraprimer: 3n = O(n), 2n = O(n)), O(f(n))/O(g(n)) = O(f(n)/g(n)) (kontraprimer: n = O(n), n = O(n 2 )). Definicija: Za funkciju g(n) kaže se da je asimptotska donja granica funkcije f(n) i piše se: f(n) = Ω(g(n)) ako postoje pozitivne konstante c i N 0 tako da za svako n > N 0 važi: f(n) > c g(n). Definicija: Ako za dve funkcije istovremeno važi f(n) = O(g(n)) i f(n) = Ω(g(n)), onda pišemo: f(n) = Θ(g(n)). 1 Materijal je nastao na ovnovu vežbi Vesne Marinković osmišljenih na osnovu knjiga: Algoritmi, Miodraga Živkovića i Discrete Mathematics and Its Applications, Kenneth H. Rosen 1
1. Dokazati da važi: T (n) = 2n 2 + n 1 = Θ(n 2 ). Rešenje: Potrebno je dokazati da postoje konsante c 1, c 2, N, takve da n N važi: c 1 n 2 2n 2 + n 1 c 2 n 2, to jest c 1 2 + 1 n 1 n 2 c 2 Pronalazimo prvo konstante c 1 i N 1 za koje je zadovoljena leva nejednakost, pa konstante c 2 i N 2 za koje je zadovoljena desna nejednakost. Odatle dobijamo c 1, c 2 i N = max{n1, N2}. Jedno rešenje je c 1 = 2, c 2 = 3, N = 1. 2. Dokazati da važi: 17n log 2 n 23n 10 = Θ(n log 2 n). Rešenje: Potrebno je dokazati da postoje konsante c 1, c 2, N tako da n N važi: c 1 n log 2 n 17n log 2 n 23n 10 c 2 n log 2 n, to jest c 1 17 23 log 2 n 10 n log 2 n c 2 Poslednja nejednakost važi za c 1 = 4, c 2 = 17, N = 4. Obrazložiti zašto nejednakost važi. 3. Navesti primer dve monotono rastuće funkcije (niza) f(n) i g(n), takve da nije ni f(n) = O(g(n)), ni g(n) = O(f(n)). Rešenje: Funkcije biramo tako da jedna od njih naglo raste za parne, a druga za neparne vrednosti broja n, na primer: Tada je: f(n) = g(n) = f(n) g(n) = { n!, n parno (n 1)! + 1, n neparno { (n 1)! + 1, n parno n!, n neparno (1) (2) { n(1 + 1 (n 1)! ) 1, n parno 1 n (1 + 1 (n 1)! ), n neparno (3) te ni f(n)/g(n) niti g(n)/f(n) nisu ograničene funkcije. Napomena: član +1 nam je bitan da bi funkcije bile rastuće, tj. da bi važilo f(2n + 1) > f(2n). Važi da je za parno n: a za neparno n: f(n)/g(n) n(1 + 1/1) 1 = n/2, g(n)/f(n) n/2. 2
4. Ako elementi niza a n zadovoljavaju rekurentnu relaciju: a n+2 + ba n+1 + ca n = 0, n 0, gde su b i c konstante i važi a 0 = 0, a 1 = 1, a 2 = 4 i a 3 = 37, odrediti a n. Uputstvo: Raspisati jednačinu za n = 0 i n = 1 i odatle dobiti koeficijente a, b i c, a onda rešiti odgovarajuću rekurentnu jednačinu. Konačno rešenje: a n = 1/10(7 n ( 3) n ). 5. Naći rekurentnu relaciju za broj nizova binarnih brojeva dužine n koji nemaju susedne nule. Uputstvo: Za n 1 označimo sa a n broj takvih nizova dužine n. Označimo sa a (0) n broj onih koji se završavaju nulom, a sa a (1) n broj onih koji se završavaju jedinicom. Važi: a n = a (0) n + a (1) n. Takodje važi: a 1 = 2. Ukoliko se broj x dužine n 1 završava jedinicom, možemo dodati 0 ili 1 na njegov kraj i tako dobijamo 2a (1) n 1 nizova koji se računaju u a n. Ukoliko se broj x dužine n 1 završava nulom, možemo na kraj dodati jedino jedinicu i tako dobijamo a (0) n 1 nizova koji se računaju u a n. Ova dva slučaja iscrpljuju sve mogućnosti, a medjusobno su disjunktna pa važi: a n = 2a (1) n 1 + a(0) n 1 Ako posmatramo proizvoljni niz y uračunat u a n 2, tada je broj y1 uračunat u a (1) n 1, a važi i obrnuto: ako je broj z1 uračunat u a(1) n 1, onda je z uračunato u a n 2, tj: Odakle sledi: a n 2 = a (1) n 1 a n = 2a (1) n 1 + a(0) n 1 = a(1) n 1 + (a(1) n 1 + a(0) n 1 ) = a(1) n 1 + a n 1 = a n 2 + a n 1 i možemo naći dve početne vrednosti: a 1 = 2, a 2 = 3,... 6. Rešiti rekurentnu jednačinu: za koju važi a 0 = 4 i a 1 = 13. Uputstvo: uvesti smenu b n = a 2 n. a 2 n+2 5a 2 n+1 + 4a 2 n = 0, n 0 3
7. Rešiti sledeću rekurentnu jednačinu: za koju važi T (1) = 1. n 1 T (n) = T (i) + 1, n 2 i=1 Uputstvo: oduzeti od T (n) vrednost T (n 1). Master teorema: Rešenje diferencne jednačine: T (n) = at ( n b ) + cnk gde su a,b, c, k 0, b 0 je: O(n log b a ), a > b k T (n) = O(n k log n), a = b k O(n k ), a < b k 8. Rešiti rekurentnu jednačinu: za koju važi T (1) = 1. T (n) = 2T ( n ) + 6n 1, n 2 2 Uputstvo: ukoliko je potrebno samo asimptotsko ponašanje, možemo iskoristiti master teoremu. Inače moramo da raspišemo sumu, uočimo pravilnost i dokažemo formulu. Konačno rešenje: T (n) = 6n log(n) + 1 9. Dokazati da niz T (n), rešenje diferencne jednačine: T (n) = 2T ( n/2 ) + 2n log 2 n, T (1) = 0 zadovoljava jednakost T (n) = O(n log 2 2 n). Rešenje: Treba pokazati da postoje konstante c > 0, N 0 > 0 tako da za n N 0 važi: T (n) c n log 2 (n). Za n = 1 dobijamo 0 = T (1) c 1 log 2 (1) = 0 pa tvrdjenje važi za proizvoljno c > 0. Treba još pokazati da ako nejednakost važi za n < N, onda važi i za n = N, pa će tvrdjenje važiti, na osnovu principa matematičke indukcije, za svaki prirodan broj n. T (N) = 2T ( N/2 ) + 2Nlog 2 (N) 2 c N/2 log 2 2( N/2 ) + 2Nlog 2 (N) cn(log 2 (N) 1) 2 + 2 N log 2 (N) = cnlog 2 2(N) + N( 2 c log 2 (N) + c + 2log 2 (N)) = cnlog 2 2(N) + N(2(1 c)log 2 (N) + c) Da li je ovo cn log 2 2(N)? Za c > 1 i N 2 važi: 2(1 c)log 2 (N) + c 2(1 c)log 2 (2) + c = 2 2c + c = 2 c pa za c > 2 važi 2 c 0 i onda važi i T (n) cn log 2 2(N). Stoga tvrdjenje važi za c 2 i N 1. 4
10. Problem P n sa parametrom n (n N) rešava se primenom algoritama A i B. Algoritam A rešava P n (n > 1) primenom algoritma B na P n 1, pri čemu se na svodjenje problema troši n vremenskih jedinica. Algoritam B rešava P n (n > 1) primenom algoritma A na P n 1, pri čemu se na svodjenje problema troši n vremenskih jedinica. Problem P 1 algoritam A rešava direktno za jednu vremensku jedinicu, a algoritam B za dve vremenske jedinice. Izračunati vreme izvršavanja algoritma A pri rešavanju problema P n. Rešenje: Neka je a n vreme potrebno algoritmu A da reši problem P n, a b n vreme potrebno algoritmu B da reši problem P n. Važi: a n = b n 1 + n, a 1 = 1 b n = a n 1 + n, b 1 = 2 Odavde se dobija: b n 1 = a n 2 + (n 1), odnosno: a n = a n 2 + (n 1) + n = a n 2 + 2n 1 Razlikujemo dva slučaja kada je indeks paran i kada je neparan: a 2n+1 = a 2n 1 + 2(2n + 1) 1 = a 2n 1 + 4n + 1 = a 2n 3 + (4n + 1) + (4n 3) =... = a 1 + (4n + 1) + (4n 3) +... + 5 n = a 1 + (4i + 1) i=1 = 1 + 3n + 2n 2 = (2n + 1)(n + 1) a 2n = a 2n 2 + 4n 1 = a 2n + (4n 1) + (4n 5) =... = a 2 + (4n 1) + (4n 5) +... + 7 n 2 = a 2 + (4i + 7) i=0 = 2n 2 + n + 1 = n(2n + 1) + 1 a n = { n(n+1) 2 + 1, n parno n(n+1) 2, n neparno (4) 5
Zadaci za vežbu 1. Dokazati da važi: 1 2 n2 3n = Θ(n 2 ) 2. Dokazati da važi: n2 n = O(3 n ) 3. Rešiti rekurentnu jednačinu: za koju važi T (1) = 5. 4. Rešiti rekurentnu jednačinu: T (n) = 2T (n 1) + 3n + 1, n 2 T (n) = T (n 3) + 5n 9, n 4 za koju važi T (1) = 1, T (2) = 6, T (3) = 13. 2 Dokazivanje korektnosti algoritma 2 Definicija: Invarijanta petlje je relacija izmedju promenljivih koja važi nakon svakog izvršenja bloka naredbi u okviru petlje. 1. Napisati algoritam za odredjivanje najvećeg zajedničkog delioca dva prirodna broja i dokazati korektnost napisanog algoritma. Rešenje: Euklidov algoritam se bazira na sledećem tvrdjenju: Tvrdjenje: NZD(a, b) = NZD(b, r), pri čemu je r = a mod b. Dokaz: za vežbu. Na osnovu ovog tvrdjenja imamo naredni pseudokod algoritma: Algoritam nzd(m,n) Ulaz: m,n; Izlaz: nzd; /* najveci zajednicki delioc brojeva m i n */ a:=max(m,n); b:=min(m,n); r:=b; while r>0 do r:=a mod b; a:=b; b:=r; end; nzd:=a; end. 2 Materijal je osmišljen na osnovu beleški sa vežbi Jelene Hadži-Purić 6
Pokažimo ispravnost ovog algoritma. Uočimo svojstvo nzd(m, n) = nzd(a, b) kao invarijantu petlje. Pokažimo indukcijom da ovo tvrdjenje važi. Baza: pre ulaska u petlju tvrdjenje važi jer važi: nzd(a, b) = nzd(max(m, n), min(m, n)) = nzd(m, n) Induktivni korak: Pretpostavimo da tvrdjenje nzd(a, b) = nzd(m, n) važi pre nekog izvršenja bloka naredbi u okviru petlje i pokažimo da važi i nakon toga. Nakon narednog izvršenja bloka menjaju se vrednosti promenljivih a i b na sledeći način: a = b b = a mod b Obzirom da važi, na osnovu gornjeg tvrdjenja, da je: nzd(a, b ) = nzd(b, a mod b) = nzd(a, b) a na osnovu induktivne hipoteze važi: nzd(m, n) = nzd(a, b) važi i: nzd(a, b ) = nzd(m, n), što je i trebalo dokazati. Da li se algoritam zaustavlja, tj da li će u nekom momentu vrednost promenljive r biti 0? Nakon svakog izvršenja bloka naredbi u okviru petlje promenljiva r ima strogo manju vrednost od prethodne, jer važi: r = a mod b = a mod r < r Dakle niz ostataka r je strogo opadajući niz nenegativnih celih brojeva, pa će nakon konačno mnogo koraka vrednost promenljive r postati 0. Na osnovu svojstva invarijante, nakon poslednjeg izvršavanja naredbi iz tela petlje važiće: nzd(a, b) = nzd(m, n), a b = 0 te je nzd(a, b) = nzd(a, 0) = a, pa pošto algoritam vraća vrednost a to i jeste nzd zadatih brojeva. 2. Neka je P funkcija koja prirodni broj n preslikava u prirodni broj sa istim ciframa, ali u obrnutom poretku (npr: P (12345) = 54321). Konstruisati algoritam koji za ulaznu vrednost - prirodan broj n, izračunava vrednost P (n). Dokazati korektnost napisanog algoritma. Rešenje: Algoritam Inverzija(n); Ulaz: n; Izlaz: m; /*vrednost broja sa obrnutim ciframa*/ m:=0; k:=n; 7
i:=0; while k>0 do m:=m*10+k mod 10; k:=k div 10; i:=i+1; end end. Dokaz korektnosti ćemo izvesti za brojeve n koji se ne zavravaju nulom. Dokažimo primenom matematičke indukcije da je relacija n = k 10 i + P (m) invarijanta petlje, tj. dokažimo da ova relacija važi pre ulaska u petlju i nakon svakog izvršenja bloka naredbi u okviru petlje. Takodje, dokažimo i da je tvrdjenje i je broj cifara broja m invarijanta petlje. Baza: Za i = 0 (dakle pre prvog izvršavanja petlje) imamo: n = k 10 0 + P (m) = k + P (0) = n + 0 i broj m ima nula cifara (smatramo da broj 0 ima nula cifara) pa tvrdjenje važi. Induktivni korak: Pretpostavimo da tvrdjenje važi za i (to jest da važi nakon i izvršavanja bloka naredbi u okviru petlje) i dokažimo da važi i za i + 1. Dakle, na osnovu induktivne hipoteze važi: n = k 10 i + P (m) Naredna vrednost promenljive k je k div 10, pa važi: k 10 i + P (m ) = (k div 10) 10 i+1 + P (m 10 + (k mod 10)) = (k div 10) 10 i+1 + P (m) + (k mod 10) 10 i = ((k div 10) 10 + (k mod 10)) 10 i + P (m) = k 10 i + P (m) = n, na osnovu induktivne hipoteze Pri prelasku na drugi red koristili smo induktivnu hipotezu da je i broj cifara broja m. Naravno, i ovo tvrdjenje treba dokazati za nove vrednosti m i i. Tvrdjenje važi, jer je i = i + 1, m = m 10 + k mod 10 Dokažimo još i da se algoritam zaustavlja. Na početku je k > 0 prirodan broj i nakon svakog prolaska kroz petlju k se smanjuje, tako da će u konačnom broju koraka k dostići vrednost 0. Tada algoritam završava sa radom i za k = 0 imamo da je n = P (m), pa je P (n) = m (jer za svako x N važi: P (P (x)) = x), gde je P (n) funkcija koja prirodan broj n preslikava u prirodan broj sa istim ciframa, ali u obrnutom poretku). 8
Napomena: Ako bismo želeli da obuhvatimo i brojeve koji se završavaju jednom ili sa više nula, algoritam bi bio isti, ali bi dokaz morao da se izmeni. Umesto pomenute invarijante petlje, trebalo bi uzeti relaciju: n = k 10 i + P (m) 10 j gde je j broj nula kojima se završava broj n. 3. Konstruisati algoritam koji za dati dekadni broj odredjuje niz cifara koji odgovara njegovom oktalnom zapisu. Dokazati korektnost tog algoritma. Odgovarajući kod dat je pseudokodom (nalik na Pascal): Algoritam Okt_cifre(n); Ulaz: n (dekadni broj) Izlaz: b (niz oktalnih cifara broja n) k:= 0; n_pom:= n; while (n_pom>0) do b[k]:=n_pom mod 8; k:=k+1; n_pom:=n_pom div 8; end end. Invarijanta petlje: Posmatrajmo broj n okt čije su oktalne cifre dobijene nakon k prolazaka kroz while petlju. Tada je invarijanta petlje: Pokažimo ovo indukcijom: n = n pom 8 k + n okt Baza: za k = 0, važi da n pom = n, n okt = 0, te je ovo tvrdjenje tačno. Induktivni korak: Neka je ovo tvrdjenje tačno za neko k i pokažimo da je tačno za k + 1. U narednom prolasku kroz ciklus dobijaju se nove vrednosti n pom, n okt za koje važi: n pom = n pom div 8 n okt = (n pom mod 8) 8 k + n okt Treba da pokažemo da važi: 9
n pom 8 k+1 + n okt = (n pom div 8) 8 k+1 + (n pom mod 8) 8 k + n okt = ((n pom div 8) 8 + (n pom mod 8)) 8 k + n okt = n pom 8 k + n okt = n, prema induktivnoj hipotezi Da li se algoritam zaustavlja? Tj. da li će u nekom momentu biti n pom <= 0? Niz vrednosti za n pom (kao celobrojni količnik pri deljenju) je monotono opadajući niz prirodnih brojeva, te će u nekom momentu postati nula. Nakon poslednjeg prolaska kroz petlju biće n pom = 0, te dobijamo n = n okt, pa je ispravnost algoritma dokazana. 10