Kompleksnost programov in algoritmov
Kompleksnost programov Kompleksnost programov ali algoritmov se večinoma ukvarja s tem, koliko časa potrebuje nek program za svoje izvajanje, koliko virov pri tem uporablja, kako razumljiva je njegova koda. Tudi najbolj preproste probleme lahko rešujemo z različnimi metodami. Lahko jih tudi kodiramo na različne načine. Nekatere probleme lahko zakodiramo z več vrsticami, vendar morda isto dosežemo z le nekaj stavki v zanki. Velikost programa torej nima direktne povezave z njegovo kompleksnostjo. Kompleksnost pogosto navezujemo na urejanje in elementov v seznamih in njihovo iskanje. Sezname elementov lahko preiskujemo zaporedno, lahko pa uporabimo metodo “binarnega razreza”
Primer algoritma: Binarno iskanje Cilj Iskanje neke vrednosti v zbirki vrednosti Ideja Deli in vladaj Binary Search The binary search algorithm has roughly the same asserted outcome as sequential search, while the assumptions and body are quite different. To understand these differences, return to the example problem of finding a book on a shelf. Suppose, first, that we are searching for a specific book title. If we arranged the books in alphabetical order by title, we could speed up the search by a test/divide process. We start the search for a certain book title in the middle of the shelf. If the title of the middle book is the one we want, the search is over successfully. If the title we want precedes the middle title, then we know the book we seek must be on the first (left) half of the shelf, otherwise, it must be on the right half (or it might not be there at all). By looking at the middle book, we narrow the search down to one half of the original range. Now we repeat the process, working with the half of the shelf to which the search was narrowed in the previous step. Looking at the book in the middle of the new range, we either find the book we seek, or we narrow the search range again by a factor of two, to 1/4 of the original. Testing again narrows the range to one-half of 1/4, or 1/8. And so on, until we either find the book or deduce that it is not on the shelf. Note that after k steps, the algorithm has reduced the range by a factor of 2k. As soon as 2k equals or excedes the number of books, the process terminates. Note that 2k >= n is equivalent to k >= log2 n. The process just described is called binary search, probably because it systematically reduces the search range by a factor of 2 at each step. The essential features of the bookshelf we used are: The books are arranged in order by title (sorted collection). We can directly access any book (random access). Note that the order of the books is by title, the same as the item of data for which we are searching. This is a critical point. For example, having the books set up as described will do nothing to speed up our search for the $100 bill. We can test the middle book for the $100, but not finding it will not give us any information about which side of the test book the $100 my reside on. To find the $100, we have to go back to sequential search. Neither being sorted by title nor having direct access will help.
Binarno iskanje(2) Zahteve Učinkovitost 11 23 35 47 53 60 72 82 91 99 Zahteve Zbirka mora biti v obliki polja (“array”) Za skok na poljuben element polja uporabljamo indekse Zbirka naj bo urejena (sortirana) Učinkovitost Zelo hitro iskanje Ne potrebujemo dodatnega prostora
Ideja binarnega iskanja 0 1 2 3 4 5 6 7 11 23 35 47 53 60 72 82 47 35 < Iskalno območje: 0 – 7 Iskano število 35
Ideja binarnega iskanja 0 1 2 3 4 5 6 7 11 23 35 47 53 60 72 82 23 < 35 Iskalno območje: 0 – 3 Iskano število: 35
Ideja binarnega iskanja 0 1 2 3 4 5 6 7 11 23 35 47 53 60 72 82 35 == 35 Iskalno območje: 2 - 3 Iskano število 35
Kompleksnost programov
Primitivne operacije Osnovna računanja, ki jih izvaja algoritem Odkrijemo jih v psevdo kodi Precej neodvisne od programskega jezika Točna definicija niti ni pomembna Predvidevamo, da potrebujejo konstanten čas izvajanja Primeri: Ocenjevanje izraza Prirejanje vrednosti neki spremenljivki Indeksiranje polja Klic metode Povratek iz metode
Štetje primitivnih operacij S proučevanjem psevdo kode lahko določimo maksimalno število primitivnih operacij, potrebnih za izvedbo algoritma in v funkciji velikosti vhoda Algoritem: arrayMax (A,n) currentMax ←A[0] for (i=1;i<n; i++) if( A[i] currentMax then currentMax ← A[i] return currentMax Št operacij 2 2n 2(n 1) 1 Skupaj 6n 1 (i = 1 enkrat , i<n n krat , i++ (n-1) krat
Ocena časa izvajanja Algoritem arrayMax v najslabšem primeru izvede 6n 1 primitivnih operacij. Definirajmo: a = čas, potreben za najhitrejšo primitivno operacijo b = čas, potreben za najpočasnejšo primitivno operacijo Naj bo T(n) najslabši čas za arrayMax. Tedaj velja a (6n 1) T(n) b(6n 1) Torej je T(n) omejen z dvema linearnima funkcijama n a (6n 1) b(6n 1) čas T(n)
Učinkovitost Če imamo več algoritmov za reševanje danega problema, kateri je najhitrejši? Če imamo nek algoritem, ali je sploh uporaben oziroma dovolj učinkovit v praksi? Koliko časa potrebuje nek algoritem? Koliko pomnilniškega prostora potrebuje nek algoritem? V splošnem sta tako časovna kot prostorska zahtevnost algoritma odvisna od velikosti njegovega vhoda. DEMO
Učinkovitost: merjenje časa Merimo čas v sekundah? + uporabno v praksi – odvisno od jezika, prevajalnika in procesorja. Štetje korakov algoritma? + neodvisno od prevajalnika in procesorja – odvisno od razdrobljenosti korakov. Štetje značilnih operacij? (n.pr. aritmetičnih oper. v matematičnih algoritmih, primerjanj v iskalnih algoritmih) + odvisno od samega algoritma + merimo notranjo učinkovitost algoritma.
Primer: algoritmi potenciranja(1) Preprost algoritem potenciranja: Za izračun bn: 1. Nastavimo p na 1. 2. Za i = 1, …, n, ponavljamo: 2.1. p p krat b. 3. Končamo z odgovorom p.
Analiza algoritma potenciranja 1. Nastavimo p na 1. 2. Za i = 1, …, n, ponavljamo: 2.1. p p krat b. 3. Končamo z odgovorom p = bn Analiza (štetje množenj): Korak 2.1 izvaja množenje. Ta korak ponovimo n krat. Število množenj = n
Implementacija v Javi static int power (int b, int n) { // Return bn (where n is non-negative). int p = 1; for (int i = 1; i <= n; i++) p *= b; return p; }
Primer: Bister algoritem potenciranja Zamisel : b1000 = b500 ´ b500. Če poznamo b500, lahko izračunamo b1000 z le enim dodatnim množenjem! Bister algoritem potenciranja: Za izračun bn: 1. Nastavimo p na 1, nastavimo q na b, nastavimo m na n. 2. Dokler m > 0, ponavljamo: 2.1. Če je m lih, množimo p p krat q. 2.2. Razpolovimo m (pozabimo na ostanek). 2.3. Množimo q q krat q . 3. Končamo z odgovorom p. DEMO
Analiza bistrega algoritma potenciranja 1. Nastavimo p na 1, nastavimo q na b, nastavimo m na n. 2. Dokler m > 0, ponavljamo: 2.1. Če je m lih, množimo p p krat q. 2.2. Razpolovimo m (pozabimo na ostanek). 2.3. Množimo q q krat q . 3. Končamo z odgovorom p. Analiza (štetje množenj): Koraki 2.1–2.3 izvedejo skupaj največ 2 množenji. Ponavljamo jih, dokler moramo razpolavljati vrednost m (ob zanemarjanju ostanka), da ta doseže vrednost 0, torej, "floor(log2 n) + 1" krat. Maksimalno število računanj = 2(floor(log2 n) + 1) = 2 floor(log2 n) + 2
Implementacija v Javi static int power (int b, int n) { // Return bn (where n is non-negative). int p = 1, q = b, m = n; while (m > 0) { if (m%2 != 0) p *= q; m /= 2; q *= q; } return p; }
Primerjava algoritmov potenciranja 10 20 30 40 število množenj 50 n preprost algoritem bister algoritem
Performančna analiza Določanje časovnih in pomnilniških zahtev algoritma. Ocenjevanju časa pravimo analiza časovne kompleksnosti Ocenjevanju pomnilniških zahtev pravimo analiza prostorske kompleksnosti. Ker je pomnilnik cenen in ga imamo kar nekaj, redko izvajamo analizo prostorske kompleksnosti Čas je “drag” , zato analizo pogosto omejimo na časovno kompleksnost.
Kompleksnost Za veliko algoritmov težko ugotovimo točno število operacij. Analizo si poenostavimo: identificiramo izraz, ki najhitreje raste zanemarimo izraze s počasnejšo rastjo v najhitreje rastočem izrazu zanemarimo konstantni faktor. Tako dobljena formula predstavlja časovno kompleksnost algoritma. Osredotočena je na rast časovne zahtevnosti algoritma. Podobno velja za prostorsko zahtevnost.
Časovna kompleksnost algoritma potenciranja Analiza preprostega algoritma potenciranja (štetje množenj): Število množenj = n Čas, potreben za izvedbo, je sorazmeren z n. Časovna kompleksnost je reda n. To zapišemo kot O(n).
Čas.kompleksnost bistrega algoritma pot. Bister algoritem potenciranja (štetje množenj): Maks. število množenj = 2 floor(log2 n) + 2 Zanemarimo izraz s počasnejšo rastjo, +2. Poenostavimo na 2 floor(log2 n) nato na floor(log2 n) Zanemarimo konstantni faktor, 2. nato na log2 n Časovna kompleksnost je reda log n. kar zapišemo kot O(log n). Zanemarimo floor(), ki v povprečju odšteje 0.5, kar je konstanta.
Primerjava čas. kompleksnosti algoritmov potenc. 10 20 30 40 n 50 n log n
Notacija O Vidimo, da je algoritem O(log n) boljši od algoritma O(n) pri velikih vrednostih n. O(log n) predstavlja počasnejšo rast kot O(n). Kompleksnost O(X) pomeni “of order X”, torej rast, sorazmerno z X. Pri tem smo zanemarili izraze s počasnejšo rastjo in konstantne faktorje.
Notacija O primeri kompleksnosti O(1) konstanten čas (izvedljivo) O(log n) logaritmični čas (izvedljivo) O(n) linearen čas (izvedljivo) O(n log n) log linear čas (izvedljivo) O(n2) kvadratični čas (včasih izvedljivo) O(n3) kubični čas (včasih izvedljivo) O(2n) eksponenčni čas (redko izvedljivo)
Primerjava časov rasti 1 log n 3.3 4.3 4.9 5.3 n 10 20 30 40 n log n 33 86 147 213 n2 100 400 900 1,600 n3 1,000 8,000 27,000 64,000 2n 1,024 1.0 milijon 1.1 miliarda 1.1 trilijon
Grafična ponazoritev časov rasti 10 20 30 40 60 80 100 50 n n2 n log n 2n n log n
Primerjava v sekundah Imejmo problem, v katerem moramo obdelati n podatkov. Za reševanje problema imejmo na voljo več algoritmov. Predpostavimo, da na danem procesorju ti algoritmi potrebujejo za svojo izvedbo naslednje čase: AlgoritemLog: 0.3 log2 n sec Algoritem Lin: 0.1 n sec Algoritem LogLin: 0.03 n log2 n sec Algoritem Quad: 0.01 n2 sec Algoritem Cub: 0.001 n3 sec Algoritem Exp: 0.0001 2n sec
Primerjava v sekundah (2) Primerjamo, koliko podatkov (n) lahko obdela posamezen algoritem v 1, 2, …, 10 sekundah: Log Lin LogLin Quad Cub Exp 10 20 30 40 50 60 70 80 90 100 0:00 n 0:08 0:07 0:10 0:06 0:09 0:04 0:01 0:02 0:03 0:05
Računska kompleksnost Primerja rast dveh funkcij Neodvisnost od množenja s konstanto in od efektov nižjega reda Metrika Notacija “Veliki O” O() Notacija “Veliki Omega” () Notacija “Veliki Theta” ()
Notacija "veliki O" (big O) In mathematics, computer science, and related fields, big O notation describes the limiting behavior of a function when the argument tends towards a particular value or infinity, usually in terms of simpler functions. Big O notation allows its users to simplify functions in order to concentrate on their growth rates: different functions with the same growth rate may be represented using the same O notation. Big O notation is also called Big Oh notation, Landau notation, Bachmann–Landau notation, and asymptotic notation. A description of a function in terms of big O notation usually only provides an upper bound on the growth rate of the function; associated with big O notation are several related notations, using the symbols o, Ω, ω, and Θ, to describe other kinds of bounds on asymptotic growth rates.
Notacija "veliki O" Naj bo n nenegativno celo število, ki predstavlja velikost vhoda v nek algoritem Naj bosta f(n) in g(n) dve pozitivni funkciji, ki predstavljata število osnovnih operacij (instrukcij), ki jih algoritem potrebuje za svojo izvedbo cg(n) f(n) f(n) je sčasoma navzgor omejena z g(n) n0
Notacija veliki “Omega” f(n) = (g(n)) iff c, n0 > 0 s.t. n ≥ n0 , 0 ≤ cg(n) ≤ f(n) f(n) cg(n) f(n) je sčasoma navzdol omejena z g(n) n0
Notacija veliki “Theta” f(n) = (g(n)) iff c1, c2, n0 > 0 s.t. 0 ≤ c1g(n) ≤ f(n) ≤ c2g(n), n >= n0 f(n) c1g(n) n0 c2g(n) f(n) ima dolgoročno enako rast kot g(n)
Analogija z realnimi števili f(n) = O(g(n)) (a ≤ b) f(n) = (g(n)) (a ≥ b) f(n) = (g(n)) (a = b) …Ta analogija ni povsem točna, vendar je tako razmišljanje o kmpleksnosti funkcije prikladno Svarilo: “Skrite konstante” v teh notacijah imajo pri realnih številih praktično posledico.
Primeri 3n2 + 17 (1), (n), (n2) spodnje meje O(n2), O(n3), ... zgornje meje (n2) točna meja
Notacija Veliki O (2) f(n)=O(g(n)) če obstaja pozitivna konstanta C in nenegativno celo število n0 tako, da velja f(n) Cg(n) za vse nn0. Pravimo, da je g(n) zgornja meja za f(n). cg(n) = cn2 f(n) = n n0
Primeri F(n) = O(1) F(n) = O(log(n)) F(n) = 1 F(n) = 2 F(n) = c (konstanta) F(n) = O(log(n)) F(n) = 2log(n) F(n) = 3log2(4n5) + 1 F(n) = c1logc2(c3nc4) + O(log(n)) + O(1)
Primeri (2) F(n) = O(n) F(n) = O(nlog(n)) F(n) = 2log(n) F(n) = n F(n) = c1n + O(n) + O(log(n)) F(n) = O(nlog(n)) F(n) = 3n + 2 F(n) = nlog(n) F(n) = 3nlog4(5n7) + 2n F(n) = c1nlogc2(c3nc4) + O(nlog(n)) + O(n)
Primeri (3) F(n) = O(n2) F(n) = 3nlog(n) + 2n F(n) = n2 F(n) = 3n2 + 2n + 1 F(n) = c1n2 + O(n2) + O(nlog(n))
Povzetek o notaciji Veliki-O O(1) < O(log n) < O(n) < O(n log n) < O(n2) < O(n3) < O(an) Polynomial time is considered manageable. But algorithms that take exponential time are not very practical for larger n. Tower of Hanoi, for example, takes a few milliseconds for a small number of disks (n = 10 or n = 20), but it takes forever (billions of years) for 64 disks.
Analiza kompleksnosti Ocenimo n = velikost vhoda Izoliramo vsako atomarno aktivnost (operacijo), ki jo moramo upoštevati Najdimo f(n) = število atomarnih aktivnosti, izvedenih pri vhodu velikosti n Kompleksnost algoritma = kompleksnost f(n) Algorithm Complexity To apply the notation and theory of computational complexity to algorithms is a four step process. First discover a measure of size of input to the algorithm. Second, decide on a notion of atomic computational activity that captures the work of the algorithm. Third, find the function f(n) = the number of atomic computations performed on input of size n. Finally, the complexity of the algorithm is the complexity of f(n). We illustrate this process in several examples as we conclude this chapter, as well as in various places throughout the remainder of the course.
Časovna kompleksnost zanke for (j = 0; j < n; ++j) { // 3 atomarne operacije } Kompleksnost = (3n) = (n) Algorithm Complexity - Loops Despite the facetious remarks we made earlier about how "obvious" it is that a simple fixed-bound loop terminates, it actually is true that simple loops are straightforward to analyze. Usually it is clear when and why they terminate and what computational work is accomplished in each iteration of the loop body. If, for example we define an atomic computation as a call to a comparison operator, there might be three such calls in the loop body. That situation is depicted in the slide. The complexity of the loop is defined to be the complexity of the function f(n) = (no. of atomics in loop body) x (no. of iterations of loop) = (3) x (n) = 3n Thus, the complexity of this loop is equal to Θ(f(n)) = Θ(3n) = Θ(n) The situation is often not quite this simple, however.
Zanke s stavkom break for (j = 0; j < n; ++j) { // 3 atomarne operacije if (pogoj) break; } Zgornja meja= O(4n) = O(n) Spodnja meja= (4) = (1) Kompleksnost= O(n) Algorithm Complexity - Loops with Break The case of a loop with a conditional breakout is shown in this slide. In the cases where the loop runs to normal termination, the run time of the loop is correctly modelled by the same function as for the simple loop above. But in other cases, the loop may terminate sooner. These cases are data dependent; that is, the runtime of the loop varies from an upper bound of 3n = O(n) to a lower bound of 3 = Ω(1), depending on the specific input to the loop. We cannot conclude that the algorithm has complexity Θ(n) because the lower bound condition >= Ω(n) does not hold. Therefore, the best we can conclude is that the loop has complexity <= O(n).
Zaporedje zank for (j = 0; j < n; ++j) { // 3 atomarne operacije } Kompleksnost = (3n + 5n) = (n) Algorithm Complexity - Loops in Sequence This slide shows two loops one following the other in the source code. These are sometimes referred to as concatenated loops. Concatenated program blocks execute in sequence, one after another. Therefore the runtime of two concatenated blocks is the sum of the runtimes of the individual blocks. For the situation depicted on the slide, the runtime is bounded above by O(3n + 5n) <= O(n).
Vgnezdene zanke for (j = 0; j < n; ++j) { // 2 atomarni operaciji for (k = 0; k < n; ++k) { // 3 atomarne operacije } Algorithm Complexity - Loops Nested This slide shows two loops one inside the other in the source code. These are sometimes referred to as composed loops. The runtime of two composed blocks is the product of the runtimes of the individual blocks. Thus, the runtime of the composed loops depicted in the slide is bounded above by O((2 + 3n)n) <= O(2n + 3n2)) <= O(n2). Kompleksnost = ((2 + 3n)n) = (n2)
Zaporedni stavki Komleksnost = O(2n) + O((2+3n)n) = O(n) + O(n2) for (i = 0; i < n; ++i) { // 1 operacija if(condition) break; } for (j = 0; j < n; ++j) { // operacija for (k = 0; k < n; ++k) { // 3 operacije
If-then-else Kompleksnost = = O(1) + max ( O(1), O(N)) = O(1) + O(N) if(condition) i = 0; else for(j = 0; j < n; j++) a[j] = j;
Zaporedno iskanje Imamo neurejen vektor a[ ], iščemo, če v njem nastopa X for (i = 0; i < n; i++) { if (a[i] == X) return true; } return false; Algorithm Complexity - Sequential Search This slide shows the usual (more efficient) version of sequential search in which there is early breakout of the loop immediately upon successful search. This is modelled by the simple loop with break, and hence has complexity <= O(n). Velikost vhoda: n = a.size() Kompleksnost = O(n)
Binarno iskanje = O(log(n)) Imamo urejen vektor a[].V njem iščemo lokacijo elementa X unsigned int binary_search(vector<int> a, int X) { unsigned int low = 0, high = a.size()-1; while (low <= high) { int mid = (low + high) / 2; if (a[mid] < X) low = mid + 1; else if( a[mid] > X ) high = mid - 1; else return mid; } return NOT_FOUND; Algorithm Complexity - Binary Search This algorithm consists of a single loop that does not have any abnormal breakout termination, so the complexity of the loop is essentially the number of loop iterations as a function of n = size of input. Some helpful observations (derived from our previous analysis of correctness) follow. Observation 1. The loop terminates after the search range is reduced to size <= 1. Observation 2. The search range is n = size for the first iteration of the loop. Observation 3. The search range is reduced by 1/2 at each iteration. Thus, the loop runs for as many times as we can divide n by 2 before reaching the value 1. The first k sizes in these iterations are n, n/2, n/4, n/8, ..., n/2k-1, and the loop termination condition is n/2k-1 == 1 equivalent to n == 2k-1 equivalent to log2n == k-1. The last inequality shows that the loop terminates in log2n iterations (give or take one). The complexity of binary search is therefore (log2n). An exercise for this chapter is to verify the three observations. Velikost vhoda: n = a.size() Kompleksnost: O( k iteracij x (1 primerjava + 1 prirejanje) v zanki) = O(log(n))
Štetje iteracij pri binarnem iskanju unsigned int binary_search(vector<int> a, int X) { unsigned int low = 0, high = a.size()-1; while (low <= high) { int mid = (low + high)/2; if (a[mid] < X) low = mid + 1; else if( a[mid] > X ) high = mid - 1; else return mid; } return NOT_FOUND; Št.iteracij prostor iskanja 1 n 2 n/2 3 n/4 k n/2(k-1)
Štetje iteracij pri binarnem iskanju (2) unsigned int binary_search(vector<int> a, int X) { unsigned int low = 0, high = a.size()-1; while (low <= high) { int mid = (low + high)/2; if (a[mid] < X) low = mid + 1; else if( a[mid] > X ) high = mid - 1; else return mid; } return NOT_FOUND; n/2(k-1) = 1 n = 2(k-1) log2(n) = k - 1 log2(n) + 1 = k Funkcija kompleksnosti f(n) = log(n) iteracij x 1 primerjanje/zanko = (log(n))
Rekurzija To je v resnici navadna zanka, zakrinkana v rekurzijo kompleksnost = O(n) long factorial( int n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); } long fib( int n ) if ( n <= 1) return fib( n – 1 ) + fib( n – 2 ); Fibonaccijevo zaporedje: kompleksnost= O( (3/2)N ) torej eksponencialno !!
kompleksnost iskanja maksimuma? double M=x[0]; for i=1 to n-1 do if (x[i] > M) M=x[i]; endif endfor return M; T(n) = a+(n-1)(b+a) = O(n) Pri tem je “a” čas enega prirejanja in “b” je čas ene primerjave Tako “a” kot “b” sta konstanti, odvisni od aparaturne opreme Opazimo, da nam veliki O prihrani Relativno nepomembne aritmetične podrobnosti Odvisnost od aparaturne opreme
Euklidov algoritem Poišči največji skupni delitelj med m in n ob predpostavki m ≥ n Kompleksnost = O(log(N))
Potenciranje Izračunaj xn Primeri: Kompleksnost= O( logN ) x11 = x5 * x5 * x x5 = x2 * x2 * x x2 = x * x Kompleksnost= O( logN ) Zakaj tega nismo računali z rekurzijo na naslednji način? pow(x,n/2)*pow(x,n/2)*x
Notacija "Veliki O" v praksi Pri računanju kompleksnosti, f(n) je dejanska funkcija g(n) je poenostavljena verzija funkcije Ker pogosto gledamo časovno kompleksnost f(n), uporabljamo namesto f(n) zapis T(n)
Načini poenostavljanja Če je T(n) vsota konstantnega števila izrazov, izpustimo vse izraze razen najbolj dominantnega (največjega) Izpustimo vse množilnike tega izraza Kar ostane, je poenostavljena g(n). Primeri: amnm + am-1nm-1+...+ a1n+ a0=O(nm). n2-n+log n = O(n2)
Značilnosti notacije veliki O Notacija "veliki O" je mehanizem poenostavitve ocene časa oziroma pomnilniškega prostora. Izgubili smo na natančnosti, pridobili na enostavnosti ocene Obdržali smo dovolj informacije o občutku za hitrost (ceno) algoritma in za primerjavo konkurenčnih algoritmov.
Primeri formul 1+2+3+…+n= n(n+1)/2 = O(n2). 12+22+32+…+n2= n(n+1)(2n+1)/6 = O(n3) 1+x+x2+x3+…+xn=(x n+1 – 1)/(x-1) = O(xn).
Primer: Hanojski stolpiči(1) Na podstavku imamo tri navpične paličice. Na palici 1 imamo stolpič iz več ploščic različne velikosti. Največja ploščica je na dnu, najmanjša na vrhu stolpiča. Naenkrat lahko premaknemo eno ploščico iz katerekoli palice na katerokoli palico. Nikdar ne smemo postaviti večje ploščice na manjšo. Problem: premik stolpiča ploščic iz palice 1 na palico 2. Animacija (z dvema ploščicama): 1 2 3 1 2 3 1 2 3 1 2 3
Primer: Hanojski stolpiči(2) Algoritem Hanojskih stolpičev: To move a tower of n disks from pole source to pole dest: 1. If n = 1: 1.1. Move a single disk from source to dest. 2. If n > 1: 2.1. Let spare be the remaining pole, other than source and dest. 2.2. Move a tower of (n–1) disks from source to spare. 2.3. Move a single disk from source to dest. 2.4. Move a tower of (n–1) disks from spare to dest. 3. Terminate.
Primer: Hanojski stolpiči(3) Animacija (s 6 ploščicami): 1. If n = 1: 1.1. Move a single disk from source to dest. 2. If n > 1: 2.1. Let spare be the remaining pole, other than source and dest. 2.2. Move a tower of (n–1) disks from source to spare. 2.3. Move a single disk from source to dest. 2.4. Move a tower of (n–1) disks from spare to dest. 3. Terminate. source dest spare 1. If n = 1: 1.1. Move a single disk from source to dest. 2. If n > 1: 2.1. Let spare be the remaining pole, other than source and dest. 2.2. Move a tower of (n–1) disks from source to spare. 2.3. Move a single disk from source to dest. 2.4. Move a tower of (n–1) disks from spare to dest. 3. Terminate. source dest spare 1. If n = 1: 1.1. Move a single disk from source to dest. 2. If n > 1: 2.1. Let spare be the remaining pole, other than source and dest. 2.2. Move a tower of (n–1) disks from source to spare. 2.3. Move a single disk from source to dest. 2.4. Move a tower of (n–1) disks from spare to dest. 3. Terminate. source dest spare 1. If n = 1: 1.1. Move a single disk from source to dest. 2. If n > 1: 2.1. Let spare be the remaining pole, other than source and dest. 2.2. Move a tower of (n–1) disks from source to spare. 2.3. Move a single disk from source to dest. 2.4. Move a tower of (n–1) disks from spare to dest. 3. Terminate. source dest spare 1. If n = 1: 1.1. Move a single disk from source to dest. 2. If n > 1: 2.1. Let spare be the remaining pole, other than source and dest. 2.2. Move a tower of (n–1) disks from source to spare. 2.3. Move a single disk from source to dest. 2.4. Move a tower of (n–1) disks from spare to dest. 3. Terminate. source dest 1. If n = 1: 1.1. Move a single disk from source to dest. 2. If n > 1: 2.1. Let spare be the remaining pole, other than source and dest. 2.2. Move a tower of (n–1) disks from source to spare. 2.3. Move a single disk from source to dest. 2.4. Move a tower of (n–1) disks from spare to dest. 3. Terminate. source dest spare
Primer: Hanojski stolpiči(4) Analiza (štetje premikov): Naj bo moves(n) število premikov, potrebnih za premik stolpiča z n ploščicami. Tedaj: moves(n) = 1 if n = 1 moves(n) = 1 + 2 moves(n–1) if n > 1 Rešitev: moves(n) = 2n – 1 Časovna kompleksnost je O(2n). WEB DEMO