Algoritmi i logička struktura programa Sadržaj UVOD ............................................................................................................................................... 3 ALGORITMI U RAČUNALNOJ ZNANOSTI ................................................................................ 4 PRIKAZ ALGORITAMA ..................................................................................................................................... 5 STRUKTURA ALGORITMA ............................................................................................................................. 6 STRUKTURA PROGRAMA .............................................................................................................................. 9 ANALIZA ALGORITAMA........................................................................................................... 10 VREMENSKA SLOŽENOST .......................................................................................................................... 10 PROSTORNA SLOŽENOST ........................................................................................................................... 17 VRSTE ALGORITAMA ............................................................................................................... 18 PODIJELI PA VLADAJ ..................................................................................................................................... 18 DINAMIČKO PROGRAMIRANJE ................................................................................................................ 23 POHELPNI PRISTUP ...................................................................................................................................... 30 BACKTRACKING .............................................................................................................................................. 34 ALGORITMI SORTIRANJA ....................................................................................................... 38 SELECTION SORT............................................................................................................................................ 39 INESERTION SORT ......................................................................................................................................... 39 BUBBLE SORT................................................................................................................................................... 40 QUICK SORT....................................................................................................................................................... 40 ZAKLJUČAK................................................................................................................................. 43 LITERATURA .............................................................................................................................. 44 2 Algoritmi i logička struktura programa UVOD Priču o algoritmima možemo započeti kao i svaku klasičnu priču. Jednom davno živio je u Bagdadu pisac, matematičar, astronom i geograf po imenu Muhammed ibn Musa al Khowarizmi. Vjerojatno nije ni sanjao, dok je daleke 852. godine pisao knjigu Kitab al jabar w'al-muqubala, da će od toga nastati čak dva uzroka glavobolje Ďacima i studentima deset, jedanaest stoljeća nakon toga. Njegov al jabar je postala algebra, a od njegovog prezimena al Khowarizmi je nastao naziv algoritmi. On je u svojoj knjizi prikazao rješenja nekih aritmetičkih problema u obliku uputstava koja su se sastojala od točno odreĎenih pravila. Upravo su ta uputsva (algoritmi) danas postala važno i samostalno područje računalne znanosti. Ali što je zapravo algoritam? U računalnoj obradi podataka, algoritam je skup postupaka (preciznih uputa) koje treba učiniti da bi se riješio odreĎeni zadatak. To su zapravo, toliko precizne upute da za njihovo rješavanje nije potrebna inteligencija, odnosno dat problem moramo svesti na manje potprobleme koje je trivijalno riješiti. I upravo taj način razmišljanja, koji programeru omogućuje da pravilno postavi složeni problem, te ga raščlani na manje potprobleme, čini od njega umjetnika baš kao i glazbenika ili slikara. Algoritmi su svuda oko nas. Naučeni postupak množenja, recept u kuharici, upute za uporabu, proces spajanja gena, sve su to algoritmi koji nas okružuju i s kojima se svakodnevno susrećemo u svim granama ljudske znanosti. Upravo zbog tolike zastupljenosti i proširenosti algoritama u ovom ću seminaru obraditi, ne bazirajući se na programski jezik, već samo na teorijski dio, neke od najosnovnijih algoritama računalne znanosti. 3 Algoritmi i logička struktura programa ALGORITMI U RAČUNALNOJ ZNANOSTI Algoritam je, u općem slučaju, konačni red operatora, elementarnih obrada i pravila o njihovoj primjeni u cilju dobivanja rješenja nekog problema. IzvoĎenje svakog operatora predstavlja jedan algoritamski korak. Nažalost, sa računalnog gledišta ova definicija nije dovoljna pa se mora nadopuniti još nekim uvjetima koje algoritam mora zadovoljiti. To su: 1) Definiranost 2) Konačnost 3) Rezultat Pod pojmom definiranost smatramo da svaka operacija ili pravilo mora imati definirano samo jedno značenje, tj. rezultat jedne operacije je jednoznačno definiran. Svaki korak algoritma, mora biti takav da bi ga, u principu, mogao izvesti čovjek koristeći papir i olovku, za konačno vrijeme. Odnosno, algoritam se mora zaustaviti u konačnom vremenu nakon konačnog broja koraka, a vrijeme izvršavanja algoritma mora biti razumno kratko. Po završetku rada algoritma mora postojati mogućnost da se ustanovi je li algoritam postigao svoji cilj ili nije. Odnosno, je li došao do nekog rezultata. 4 Algoritmi i logička struktura programa PRIKAZ ALGORITAMA Postoje različite mogućnosti prikazivanja algoritma, no najčešće se koristi grafički prikaz pod imenom dijagram tijeka programa („flowchart“). Dijagramom tijeka, svaka je akcija prikazana točno odreĎenim grafičkim simbolom čime se osigurava jednostavnost i jednoznačnost algoritma. GRAFIČKI SIMBOL: ZNAČENJE: TERMINATOR UNOŠENJE PODATAKA IZDAVANJE PODATAKA OBRADA PODATAKA ODLUKA POVEZIVANJE ALGORITAMSKIH KORAKA 5 Algoritmi i logička struktura programa STRUKTURA ALGORITMA Pod sturkturom algoritma smatramo redoslijed izvršavanja algoritamskih koraka. Razlikujemo 3 osnovne algoritamske stukture: 1) LINIJSKA 2) RAZGRANATA 3) CIKLIČKA LINIJSKA STRUKTURA Kod linijske strukture algoritamski koraci se izvršavaju jedan za drugim redoslijedom kojim su napisani. Algoritam za zamjenu kotača na automobilu. Početak 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. Pripremi dizalicu Pripremi rezervni kotač Olabavi vijke Podigni auto Odvrni vijke Skini kotač Stavi rezervni kotač Zavrni vijke Spusti auto Zategni vijke Spremi dizalicu Spremi kotač Kraj 6 Algoritmi i logička struktura programa RAZGRANATA STRUKTURA Razgranata struktura je struktura u kojoj tok zavisi o ispunjenosti nekog uvjeta. Početak 1. Stani ispred semafora 2. Pogledaj semafor ZELENO NE DA Prijeđi cestu Čekaj zeleno i prijeđi cestu Kraj Stajemo ispred semfaora, nakon toga gledamo je li uvjet ispunjen, odnosno svijetli li zeleno svjetlo na semaforu. Ukoliko svijetli, prelazimo cestu, u suprotnom čekamo zeleno svjetlo i tek onda prelazimo cestu. 7 Algoritmi i logička struktura programa CIKLIČKA STRUKTURA U cikličkoj strukturi postoji odreĎen broj koraka koji se ponavlja više puta. Ako je broj ponavljanja poznat struktura je konstantna (tzv. brojčani ciklus), a ako broj ponavljanja nije poznat već zavisi o ispunjenosti nekog uvjeta, struktura je promjenjiva (tzv. uvjetni ciklus) Brojčani ciklus: Uvjetni ciklus Napuni 10 boca vodom. Prijelaz preko ulice bez semafora Početak Početak Otvori vodu Stani ispred kolnika Ponavljaj 10 puta Pogledaj lijevo i desno NE Uzmi praznu bocu Nema vozila DA Napuni je vodom Sačekaj malo Zatvori bocu Prijeđi ulicu Odloži punu bocu Kraj Zatvori vodu Početak 8 Algoritmi i logička struktura programa STRUKTURA PROGRAMA Svaki se program sastoji od naredbi koje se formiraju koristeći riječnik iz riječnika odreĎenog programskog jezika i naziva koje programer dodjeljuje memorijskim lokacijama. U te memorijske lokacije uskladišteni su podaci s kojima se radi. Pišući program moramo se držati odreĎenih pravila, odnosno sintakse. Tako npr. svaka naredba završava sa oznakom za kraj naredbe. Najčešće je to točka zarez (;). Naredbe se izvršavaju redosljedom kojim su napisane, ukoliko taj redoslijed nije izmjenjen posebnim naredbama za izmjenu toka programa. Naredbe se mogu podijeliti u dvije kategorije. Izvršne naredbe, odnosno one koje se u procesu prevoĎenja programa prevode u odgovarajući strojni jezik i neizvršne- one kojima se daju upute prevoditelju neophodne za prevoĎenje programa (npr. definiranje imena memorijskih lokacija, definiranje tipova podataka itd.). S obzirom na to da su namjenjene prevoditelju, one se koriste samo za prevoĎenje i ne prevode se u strojni jezik. U većini programskih jezika, u programu se na početku pravi odjeljak za neizvršne naredbe, a poslije toga slijedi blok sa izvršnim naredbama. Tako se prevoditelj prvo upoznaje s napucima za prevoĎenje programa a tek onda slijedeći te naputke i pravila, učitava izvršne naredbe i prevodi ih u strojni jezik. U svim programskim jezicima znakovi za formiranje elemenata su alfabetski znakovi (velika i mala slova engleske abecede), numerički (znamenke od 0 do 9) i specijalni (+, -, =,...). Alfabetski i numerički znakovi zajedno se nazivaju alfa-numerički. 9 Algoritmi i logička struktura programa ANALIZA ALGORITAMA Analiza algoritama je iznimno važna u dizajniranju i programiranju algoritama. Često postoji više načina koji vode do istog rješenja. Mi moramo odabrati onaj najefikasniji. U odreĎivanju efikasnosti algoritma, najčešće gledamo vremensku i prostornu složenost. Vremenska skoženost je vrijeme potrebno za izvoĎenje odreĎenog algoritma. Ne mjeri se u sekundama kao što bi bilo logično, već se mjeri u nekim osnovnim mjernim jedinicama kao što su strojne instrukcije, aritmetičke operacije itd. Brzina se odreĎuje usporeĎivanjem algoritama, a ne usporeĎivanjem računala ili arihitekture računala. Prostorna složenost je memorija potrebna za izvoĎenje algoritma. Jedinica mjere može biti bit, bajt, riječ, cijeli broj, itd. Prostorna složenost je manje ograničavajuća jer algoritam istu memorijsku lokaciju može koristiti više puta tijekom izvoĎenja. VREMENSKA SLOŽENOST Različite vremenske složenosti najjednostavnije ćemo prikazati na primjeru. Izvrednjavanje polinoma Zadan je niz realnih brojeva an , an 1 , ..., a0 i realni broj x. Izračunajte vrijednost polinoma: Pn ( x) an xn an 1xn 1 an 2 xn 2 ... a1x a0 Rješenje 1) Pristupimo problemu preko indukcije. Svodimo rješavanje zadanog problema na rješavanje manjeg problema. Pokušajmo ukloniti videći koeficijent an . Pretpostavimo da znamo izračunati Pn 1 ( x) an 1xn 1 an 2 xn 2 ... a1x a0 . Baza indukcije je a0 . Sada rješavamo problem pomoću rješenja manjeg problema ( Pn 1 ( x) ). Korak indukcije je: Pn ( x) an xn 10 Pn 1 ( x) . Algoritmi i logička struktura programa Ovaj algoritam očito daje točno rješenje, meĎutim nije efikasan. Izvršenje zahtijeva n (n 1) (n 2) ... 1 n(n 1) množenja i n zbrajanja. 2 Rješenje 2) Bolje rješenje dobivamo ako uzmemo jaču pretpostavku indukcije: Znamo riješiti Pn 1 ( x) i znamo izračunati x n 1 . Sada u koraku indukcije x n dobijemo iz x n 1 uz jedno množenje, a zatim an x n uz još jedno množenje. Pn ( x) zahtjeva još jedno množenje i zbrajanje. Ukupno imamo 2n množenja i n zbrajanja. Iako smo u koraku indukcije zahtijevali više, ukupan broj operacija je znatno manji. Ovaj algoritam je jednostavan, efikasan i naoko optimalan, no postoji i bolji algoritam. Rjesenje 3) Poukušajmo ukloniti prvi koeficijent a0 . Zapišimo: Pn ( x) (an xn 1 an 1xn 2 ... a1 ) x a0 Pn' ( x) x a0 . Pretpostavka indukcije: znamo izračunati Pn' ( x) . Iz njega, jednim množenjem i jednim zbrajanjem dobijemo Pn ( x) . Ako polinom raspišemo do kraja dobijemo: Pn ( x) ((...((an x an 1 ) x an 2 ) x an 3 ) x ...) x a1 ) x a0 . Ovaj postupak izvrednjavanja polinoma zovemo Hornerovo pravilo. Vidimo da za cijeli postupak koristimo samo n množenja i n zbrajanja, te jednu doadtnu memorijsku lokaciju. 11 Algoritmi i logička struktura programa BIG OH NOTATION Kao što smo već rekli, u suvremenom računarstvu vrlo važnu ulogi ima odreĎivanje složenosti algoritma. Složenost algoritma iskazuje se asimptotskim ponašanjem niza ( an ) koji predstavlja gornju granicu za broj računskih operacija (ili broj usporeĎivanja) dovoljnih da bi se algoritam realizirao. Kompleksnost algoritma definira se na različite načine. Ovisno o problemu, najčešće želimo izrazti složenost u ovisnosti od ulaznih podataka. Npr. Euklidov algoritam za pronalaženje najveće zajedničke mjere ovisi o brojevima a i b. Gaussov algoritam za rješavanje linearnog sustava n jednaĎba s n nepoznanica ovisi o ulaznom broju n. Jasno je da an definiran sa an 1 2 n raste brže od niza bn 100 veliki n, jer kvadratna funkcija raste brže od linearne, ili točnije lim x 1000n za dovoljno bn = 0. an Stoga, kažemo da niz ( an ) asimptotski dominira niz (bn ) ako postoje konstante n0 i M>0 takve da je: (bn ) M ( an ) , za sve n n0 . Označimo sa O(an ) skup svih nizova (bn ) koji su asimptotski dominirani slijedom ( an ) . Skup O(an ) zovemo veliki O od ( an ) i pišemo: bn O(an ) (an ) asimptotski dominira (bn ) . Svrha ove oznake je da naĎe što jednostavniji niz ( an ) koji dominira (bn ) i opisuje brzinu rasta. Oznaka O nam omogućuje da asimptotsko ponašanje slijedova opišemo samo s onim što je zaista najbitnije. Primjer 1) Ako vrijeme izvršavanja nekog algoritma raste po funkciji T (n) 4n 2 2n 2 za dovoljno veliki n može se slobodno zanemariti član 2n zbog toga što će član 4n 2 biti toliko velik da iako zanemarimo 2n neće biti bitnijih promjena u rezultatu. 12 Algoritmi i logička struktura programa Na sličan način možemo zanemariti i konstante. Ukoliko T (n) 100, 000, 000n2 , U (n) n3 će za n veći od 100,000,000 biti veći od n imamo k. Primjer 2) Na ovom grafu primjećujemo da funkcija g(n) asimptotski dominira funkciju f(n) jer postoje takve konstante c i k da je za svaki broj n k funkcija cg(n) > f(n). Primjer 3) f ( n) Funkcija n 2 3n 4 asimptotski je dominirana funkcijom g (n) n2 , jer ukoliko uzmemo konstantu M = 2 i n0 = 11 uočit ćemo da je 2 n 2 > n 2 3n 4 , n 11. Ovaj primjer odlično prikazuje ono što definicija govori. Sljedeća tablica prikazuje porast broja n u odreĎenim funkcijama: N log2N N N log2N N2 N3 2N N! 8 3 8 24 64 512 256 40300 16 4 16 36 256 4096 6.5×104 > 2×1013 64 6 64 384 4096 262144 1.84×1019 > 1089 Vrijedi spomenuti da 1012 s čini više od 20 000 godina. Broj 64! Je veći od 1080, koliko iznosi procjena za broj atoma u vidljivom svemiru. 13 Algoritmi i logička struktura programa Npr. složenost Cramerova algoritma za rješavanje linearnog sustava n jednadžaba s n nepoznanica ima čak faktorijeličnu složenost (O(n!)), s druge strane, možemo isti problem riješiti Gaussovim algoritmom koji ima kubnu složenost (O(n3)). Tako će npr. 20 jednadžaba s 20 nepoznanica Gaussov algoritam riješiti za djelić sekunde, dok će Cramerovim pravilom trebati nekoliko stotina tisuća godina. Zašto je uopće važna brzina izvoĎenja algoritma? Osim što bi svatko volio doživjeti rezultat algoritma možemo za primjer uzeti i digitalnu kameru od 1 Mpx. Recimo da algoritam koji procesira sliku na ekran ima složenost O(n2), ukoliko treba mikrosekunda za procesuirati jedan pixel obraĎivanje bi trajalo tjednima, a za sliku od 3 Mpx i mjesecima. U računalstvu se algoritam smatra dobrim ako mu je kompleksnost najviše polinomijalnog rasta. Algoritmi s eskponencijalnom kompleksnošću, a pogotovo s faktorijelnom smatraju se nepovoljni za primjenu. SLOŽENOST POTENCIRANJA Binarni prikaz prirodnog broja Standardna zadaća svakog računala je pretvaranje prirodnih brojeva iz dekadskog sustava u binarni sustav. Taj se prikaz dobiva uzastopnim djeljenjem sa 2. Algoritam: 1) Učitaj n, stavi i = 1 2) Podijeli n sa 2, naĎi kvocijent q i ostatak r; spremi r kao ai r 3) Ako je q = 0 zaustavi program 4) Stavi n = q 5) Stavi i = i + 1, prijeĎi na korak 2 Na kraju treba ispisati broj n pomoću bitova n n a0 a1 2 ... ak 2k , pri čemu je ai 0,1 , ak brojeva ai . 14 ak ak 1...a1a0 . Taj zapis shvaćamo kao 1 . Brojač i nam treba zbog ispisa Algoritmi i logička struktura programa Pogledajmo jednostavan primjer: i n q r 0 27 13 1 = a0 1 13 6 1 = a1 2 6 3 0 = a2 3 3 1 1 = a3 4 1 0 1 = a4 Prema tome je: 27 a0 a1 2 a2 22 a3 23 a4 24 1 2 23 24 , tj. 2710 = 110112. Očito je da je broj operacija dijeljenja jednak broju binarnih znamenaka (bitova) broja n. Odnosno, složenost ovog algoritma je samo logaritamska. Označimo sa h(n) broja operacija, tada je h(n) log 2 n 1 , dakle vrlo povoljna. DOKAZ: Neka je k najveći prirodan broj takav da je 2k n 2k 1. Broj znamenki u binarnom zapisu jednak je k+1, jer računamo svih k+1 potenciju baze 2 u binarnom obliku broja n, od nulte do k-te: n a0 20 a1 21 ... ak 2k . Logaritmirajući ovu nejednakost po bazi 2 dobivamo k log 2 n k 1, dotično k = log 2 n . Dakle broj znamenki je jednak log 2 n +1. Potenciranje prirodnim brojem n Neka je zadan realni broj a > 0 i n N. Vrlo jednostavan algoritam za izračunavanje an zasniva se na petlji „računaj članove slijeda xi = xi a, gdje je i = 1, 2, ...., n, x1=a. Dakako, xn = an je dobiven nakon n-1 uzastopnih koraka pa je složenost jednak O(n). Zanimljivo je da postoji algoritam koji ovaj problem riješava u bitno manje koraka, čak logaritamskom složenošću. Ideja je slijedeća. Pogledajmo najprije an gdje je n = 2k. Možemo pisati: an (...(a 2 ) 2 ...) 2 , 15 Algoritmi i logička struktura programa gdje se u eksponentu broj 2 pojavljuje k puta. Na temelju ovog identiteta vidimo da možemo proračun a n programirati pomoću petlje koja sadrži samo k uzastopnih kvadrata. Kako je n 2 k , složenost ovog algoritma je u ovom slučaju jednaka O(log2n), logaritamska. Ovu je ideju moguće modificirati tako da se dobije jednostavan algoritam za izračunavanje a n i to logaritamske složenosti za bilo koji n, ne nužno 2k. Ilustrirajmo primjerom: Uzmimo broj x a 27 , tj n = 27. Napišimo eksponent n u binarnom zapisu: 27 1 2 23 2 4 . Onda je x a 27 4 3 a2 a2 a2 a a16 a8 a 2 a . Za a16 nam trebaju 4 uzastopna kvadrata broja a. MeĎutim njima su dobiveni i a 8 i a 2 . Zatim imamo još 3 množenja, dakle ukupno samo 7 za razliku od 26 koja bismo imali u prvom algoritmu potenciranja. Ideja je da se za algoritam brzog potenciranja iskoristi algoritam za pretvaranje broja iz dekadske baze u binarnu. Uz male modifikacije tog algoritma dobit ćemo algoritam za potenciranje u logaritamskom vremenu. Tako dobiveni algoritam za brzo potenciranje glasi: 1) Učitaj a, n i stavi x = 1 2) Podijeli n sa 2, naĎi kvocijent q i ostatak r 3) Ako je r = 1 stavi x = x a 4) Ako je q = 0 zaustavi program 5) Stavi n = q 6) Stavi a = a a i prijeĎi na korak 2 Pogledajmo što se dogaĎa ako je n = 27. i n q r a x 0 27 13 1 a a 2 1 13 6 1 a a3 2 6 3 0 a4 a3 3 3 1 1 a8 a11 4 1 0 1 a16 a27 16 Algoritmi i logička struktura programa Analiza istog algoritma: Algoritam nešto računa jedino u slijedeća tri koraka: korak broj 2 – dijeli, korak broj 3 – množi i korak broj 6 – množi. Broj operacija označimo sa h(n). To je mjera za složenost dotičnog algoritma. Pri svakom prolazu kroz petlju provodi se dijeljenje u drugom koraku. Taj broj je jednak log 2 n +1. Prema tome, ukupan broj operacija je najviše h(n) 3(log 2 n 1) , odnosno h(n) O(log 2 n) . Ovaj algoritam ne možemo više optimizirati. PROSTORNA SLOŽENOST Pod prostornom složenošću podrazumjevamo koliko prostora u memoriji zauzimaju podaci koje odreĎeni algoritam generira. OdreĎuje se zbrajanjem veličine memorijskih lokacija koje ti podaci zauzimaju. Broj bitova u binarnom obliku broja n jednak je B = log 2 n +1. Ova nam relacija omogućava da log 2 n log 2 n B složenost algoritma izrazimo kao 1 log 2 n 1 onda je B 1 log 2 n 2B 1 n funkciju od B. Kako je B , odnosno, 2B Drugim riječima, ako broj svih bitova broja B raste linearno, onda n raste eksponencijalno, kao 2B i obratno. Stoga je razumno memorijsku složenost definirati na slijedeći način: m( B) h(2B ) , gdje je h ( n ) vremenska složenost. Npr. Algoritam brzog potenciranja ima logaritamsku vremensku složenost stoga pišemo: m( B) h(2B ) 3(log2 n 1) 3( B 1) , odnosno, O(B), dok za sporo potenciranje imamo: h(n)=n –1 , tj. m( B) h(2 B ) 2 B 1 , O(2B ) , dakle eksponencijalna vremenska složenost. 17 Algoritmi i logička struktura programa VRSTE ALGORITAMA Iskustveno je pronaĎeno nekoliko općenitih strategija za oblikovanje algoritama. Najvažnije su: 1) Podijeli pa vladaj (divide and conquer) 2) Dinamičko programiranje 3) Pohlepni pristup (Greedy) 4) Backtracking (Strategija odustajanja) Generalno, svaki problem traži neki svoj pristup, ne možemo sve algoritme svrstati u ove četiri kategorije, ali su se ove strategije pokazale uspješne za mnoge probleme, pa je to razlog zašto se najviše koriste u rješavanju novih problema. PODIJELI PA VLADAJ Strategija podijeli pa vladaj jedna je od najprimjenjivijih strategija za oblikovanje algoritama. Ideja je da se zadani problem „razbije“ u nekoliko manjih istovrsnih problema. Tako da se rješenje polaznog problema može relativno lako konstruirati iz rješenja manjih problema. Dobiveni algoritam je rekurzivan, jer se svaki od manjih problema i dalje „razbija“ u manje probleme. Nužna pretpostavka ove tehnike je da postoji neka relativno jednostavna tehnika rješavanja dovoljno malog problema. Kojom metodom rješavamo te „dovoljno male“ probleme? Odgovor na to pitanje glavni je koncept podijeli pa vladaj metode. Uzmimo u obzir slijedeće: imamo algoritam koji rješava neki problem veličine n u cn2 koraka. Dok drugi algoritam isi problem rješava na slijedeći način. Podijeli ga na 3 manja potproblema i iskombinira ta tri rješenja u dn koraka. Pretpostavimo da potprobleme rješavamo koristeći prvi algoritam, znači u cn2 koraka. Označimo sa: T1 (n) - složenost prvog algoritma, a sa T2 ( n) - složenost drugog algoritma 18 Algoritmi i logička struktura programa Tada je T1 (n) cn2 , dok je T2 (n) 3T1 (n / 2) dn 3 2 (cn ) dn 4 Za male vrijednosti varijable n prvi algoritam će biti brži od durgog, no za velike vrijednosti n-a drgui algoritam će biti efikasniji. Množenje dugačkih cijelih brojeva Klasičan algoritam množenje dva broja glasi: Množi x sa svakom znamenkom y-a i zbrajaj djelomične rezultate. Taj algoritam zahtijeva računanje n produkata veličine n, pa je složenost algoritma O(n2). Strategija podijeli pa vladaj svodi se na slijedeći algoritam: Svaki od brojeva x i y podijelimo na dva dijela duljine n bitova , zbog jednostavnosti 2 se uzima da je n potencija broja 2. n A x x y C B n 2 A 2x D n x I tada je produkt jednak: AB 2 n 2 A 22 y C 22 B n D n A 2x2 AB 2 2 x y B x AC 2 B n ( AD BC ) 2 n 2 BD Ovim smo algoritmom jedno množenje velikih n-bitnih brojeva zamijenili s 4 množenja malih n bitnih brojeva, tri zbrajanja brojeva duljine najviše 2n bitov, te dva pomicanja bitova 2 n n 2 (množenje s 2 i 2 ). Dalje, AC, AD, BC i BD množimo na sličan način kao i xy, pošto zbrajanje i pomicanje bitova zahtijevaju O(n) koraka ukupna složenost je: T (n) 4 T (n / 2) O(n) Kostanta 4 je zbog toga što imamo 4 množenja: AC, AD, BC, BD. Izračunavanjem ove rekurzivne relacije ustanovili bismo da je složenost ovog algoritma O(n2), dakle ista kao i kod običnog (školskog) množenja. 19 Algoritmi i logička struktura programa Dakle ,strategija podijeli pa vladaj daje algoritam iste složenosti kao klasični algoritam. Ali naravno, algoritam možemo poboljšati. Naime, matematičar C. F. Gauss jednom je prilikom primijetio da umnožak dva kompleksna broja, (a+bi)(c+di)=ac-bd+(bc+ad)i, ima 4 množenja, ali da se u biti može svesti i na 3 množenja: ac, bd, (a+b)(c+d), jer je bc+ad=(a+b)(c+d)-ac-bd. Gledajući s Veliko O strane to i nije takva razlika no razlike se primijete ukoliko primjenjujemo rekurziju. Tako složenost ovog programa izgleda ovako: T ( n) 3T ( n / 2) O( n) . Dakle, složenost je O(nlog2 3 ) , odnosno O (n1.59 ) . 20 Algoritmi i logička struktura programa Primjer 1) Problem je utvrditi postoji li u listi element sa zadanom vrijednošću. Strategija podijeli pa vladaj dovodi nas do sljiedećeg algoritma: 1) Što je lista dulja, teže ju je pretražiti pa ju dijelimo u npr. 3 manje liste i u svakoj se zasebno traži zadana vrijednost. 2) Tražena vrijednost se javlja u početnoj listi ako se javaj u bar jednoj manjoj listi, znači konačno rješenje se dobije primjenom logičke operacije „ili“ na djelomična riješenja. Ako je u početku lista sortirana ovaj algoritam se može bitno pojednostaviti i to tako da listu podijelimo na 3 manje pa će srednja lista imati samo jedan element, a preostale dvije su jednake duljine. Jedna operacija usporeĎivanja nam omogućuje da se vrijednost odmah pronaĎe ili da se odbace dvije liste. Taj algoritam naziva se binarno pretraživanje. Recimo da tražimo broj 4 u listi: 1 3 3 4 6 6 7 8 9 9 Listu dijelimo na 3 dijela: Lista: 1 3 3 4 6 6 7899 UsporeĎujemo 6 sa 4 ukoliko je 6 = 4 to je rješenje, inače ako je 6 > 4 gledamo lijevu listu inače desnu. Lista: 1 3 3 4 6 Opet dijelimo na 3 dijela. Lista: 1 3 3 46 UsporeĎuejmo 4 sa 3. Uzimamo desnu podlistu. Lista: 4 6 Lista: Nema lijeve 4 6 Rijesenje je 4. Složenost ovog algoritam je Tmax (n) O(log n) . Jedna od varijacija podijeli pa vladaj algoritama je „smanji pa vladaj“ metoda. Riječ je o podijeli pa vladaj algoritmu u kojemu riješenje ne ovisi o svim podproblemima već samo o jednom. Primjer toga je binarno pretraživanje, kao i pronalaženje najvećeg elemnta u listi. 21 Algoritmi i logička struktura programa Primjer 2) Problem je pronaći najveći elemnt u listi. Algoritam radi na sličan način kao i prijašnji. Početnu listu podijelimo na 2 dijela. PronaĎemo najveći element u lijevoj podlisti te ga usporedimo s najvećim elementom u desnoj podlisti i ispišemo veći. 1 1 1 10 10 12 12 10 14 17 12 10 17 15 14 17 15 14 17 2 2 15 Dobili smo da je točno rješenje 17. 22 7 7 15 17 7 2 15 17 7 Algoritmi i logička struktura programa DINAMIČKO PROGRAMIRANJE Metoda „podijeli pa vladaj“ dovela nas je do mnogih zanimljivih rješenja. Dijelili smo problem na manje probleme i spajali ih u konačni rezultat. U dinamičkom programiranju, princip svodimo do krajnjih granica, kada neznamo točno koje potprobleme moramo riješiti, pa ih riješimo sve, pohranimo ih, te ih koristimo za daljnje rješavanje većih problema. U strategiji dinamičkog programiranja javljaju se dva glavna problema. Prvo; nekad nije moguće spojiti dva manja potproblema u veći. Drugo; moguće je da postoji neprihvatljivo veliki broj potproblema koje treba riješiti. Nekad, broj potproblema koje treba riješiti raste eksponencijalno s veličinom zadanog problema, a rekurzijom se isti potproblemi rješavaju više puta što dovodi do nepotrebnog gubljenja vremena. U takvim situacijama dinamičko programiranje dobro doĎe. Algoritmi tipa „podijeli pa vladaj“ idu od vrha prema dolje, a algoritmi dinamičkog programiranja idu najčešće od dna prema gore. Prvo se rješavaju problemi manje veličine, pa nešto veći, itd. Sve dok se ne dosegne potpuna veličina zadanog problema. Vrlo je važan redoslijed ispunjavanja tablice. Strategija dinamičkog programiranja zahtijeva ponekad rješavanje potproblema koji nisu potrebni za rješenje problema, no to je još uvijek isplativije od rješavanja istih problema mnogo puta. Računanje fibonaccijevih brojeva Strategija podijeli pa vladaj daje rekurzivnu funkciju koja glasi: F0 1 ; F1 1 ; Fn Fn 1 Fn 2 , n > 1 Ako izračunamo složenost ovog algoritma, dobit ćemo da je ona jednaka O(1.618n); dakle vrijeme izvršavanja raste eksponencijalno s veličinom ulaza. Strategija dinamičkog programiranja dovodi nas do puno boljeg rješenja. Prvo se riješi trivijalan problem za F0 i F1 , iza njih se izračuna F2 , pa F3 iz F2 i F1 , itd. Time se prvo riješe jednostavniji problemi i spreme se njihovi rezultati koji se dalje koriste za rješavanje malo složenijih problema, čiji se rezultati takoĎer spremaju za daljnje rješavanje malo složenijih problema itd. 23 Algoritmi i logička struktura programa Složenost ovog algoritma je O(n), dakle linearna, što je bitno brže od prvog algoritma. Prostorna složenost ovog algoritma je minimalno povećana (samo za dvije cjelobrojne varijable). Problem određivanja šanse za pobjedu u nadmetanju Problem je slijdeći. Dva natjecatelja A i B se nadmeću u nekoj sportskoj igri koja je podijeljena u dijelove, setove, runde ili partije. U svakom dijelu igre točno jedan igrač bilježi jedan bod. Igra traje sve dok jedan igrač ne osvoji ukupno n bodova. Pretpostavlja se da su igrači podjednako jaki, tako da svaki ima 50% šanse da dobije bod u bilo kojem dijelu igre. Označimo sa P(i, j) vjerojatnost da će A biti konačan pobjednik u situaciji kada A treba i bodova do pobjede, a B treba još j bodova do pobjede. Npr. ako je n = 4, ako je A dobio 2 boda, a B dobio 1 bod, tada je i = 2, a j = 3 i traži se vjerojatnost P(2,3). Očigledno je da A ima veće šanse za pobjedu. Tražimo algoritam koji za zadane i, j računa P(i, j). Vrijedi relacija: P(i, j) = 1 za i = 0, j > 0 0 za i > 0, j = 0 1 1 P(i 1, j ) P(i, j 1) 2 2 za i > 0, j > 0 Prva dva reda odgovrajau situaciji kada je jedan od igrača pokupio sve bodove, treći red opisuje situaciju kada se igra bar još jedan dio igre u kojem A ima 50% šanse da dobije bod. Ako dobije vjerojatnost konačne pobjede mu je P(i-1, j), a ako izgubi vjerojatnost je P(i, j-1). Gore navedena relacija može se iskoristiti za rekurzivno računanje P(i, j) na koje vodi strategija podijeli pa vladaj no tako dobiveni algoritam je neefikasan jer se iste vrijednosti računaju mnogo puta. 24 Algoritmi i logička struktura programa P(2,3) P(1,3) P(0,3) P(1,2) P(2,2) P(1,2) P(2,1) P(0,2) P(1,1) P(0,2) P(1,1) P(0,1) P(1,0) P(1,1) P(2,0) P(0,1) P(1,0) P(0,1) P(1,0) Ukupno se računa: P(0,1)-3 puta; P(1,0)-3 puta; P(0,2)-2 puta; P(1,1)-3 puta; P(2,0)-1 put; P(1,2)-2 puta; P(0,3)-1 put; P(1,3)-1 put; P(2,2)-1 put; P(2,1)-1 put; P(2,3)-1 put. Ukupno: 19 računanja. Složenost ovog algoritma je O(2n), dakle vrlo velika. Strategija dinamičkog programiranja vodi na ispunjavanje tablice pri računanju P(i, j) u kojoj i odgovara stupcima a j retcima. 1 2 21 32 13 16 15 16 1 4 11 32 1 2 11 16 7 8 1 3 3 16 5 16 1 2 3 4 1 2 1 16 1 8 1 4 1 2 1 1 0 0 0 0 4 3 2 1 0 0 Bilo koji unutarnji element može se dobiti kao aritmetička sredina elementa ispod i desno od njega. Tablica se popunjava od doljnjeg desnog kuta. Složenost ovog algoritma je kvadratna, dakle puno bolja od eksponencijalne složenosti algoritma dobivenog „podijeli pa vladaj“ metodom. 25 Algoritmi i logička struktura programa Računanje n povrh m Probajmo slijedeći problem riješiti rekurzivno. Znamo sljedeće: n 1; 0 n n 1; n n 1 n 1 m m 1 m Dakle, možemo napisati funkciju: T n 0 1; T n n 1; T n m T n 1 m 1 T n 1 m Provodeći ovaj algoritam zaključujemo da je on vrlo spor. Npr. za računanje 33 povrh 16 treba mu preko 30 sekundi na Core2Duo 1.93 GHz procesoru. Iako rješenje na prvi pogled izgleda jednostavno, ovakva složenost se javlja upravo zbog toga što se mnogo puta poziva rekurzija sa istim parametrima. Slično kao i kod prijašnjeg problema, algoritam možemo poboljšati dinamičkim programiranjem. Ideja je slijedeća: Sve n povrh m koje izračunamo pamtimo u tablicu, a rekurziju koristimo samo ukoliko neki par još nismo izračunali. Ovakav pristup problemu naziva se „Top down“ pristup, jer krećemo od konačnog problema. Postoji i drugi način da dinamičkim programiranjem riješimo problem, a naziva se „Bottom up“ pristup. Drguim riječima, krećemo od početka i redak po redak ispunjavamo tablicu. Taj algoritam je obična simulacija paskalovog trokuta. 1 1 1 1 1 1 2 1 3 4 3 6 1 4 . . 26 1 Algoritmi i logička struktura programa Problem 0/1 ranca (Knapsack problem) Tijekom pljačke, lopov pronaĎe neke stvari koje ne može ponijeti jer ima premalu torbu. Njegova torba može nositi teret od maksimalno W kilograma. Pronašao je n stvari težine w1, w2, w3,...,wn i vrijednostima v1, v2, v3,..., vn. Problem je odrediti maksimalnu vrijednost koju može ponijeti u torbi. Recimo da torba može ponijeti 10 kg. Stvar Težina Vrijednost 1 6 kg 30 kn 2 3 kg 14 kn 3 4 kg 16 kn 4 2 kg 9 kn Dvije su varijacije ovog problema. Ako postoji neograničena količina stvari, optimalan izbor bi bio pokupiti stvar broj 1 i dvije stvari broj 4, dakle vrijednost bi bila 48 kn. S drgue strane, ako postoji samo jedan uzorak svake stvari, onda je optimalan izbro stvar 1 i stvar 4, ukupne vrijednosti od 46 kn. Problem bi se mogao riješti algritmom tipa podijeli pa vladaj koji bi generirao sve moguće podskupove skupa predmeta {O1, O2, ..., On} i izabrati onaj s najvećom vrijednošću uz dopuštenu težinu. Inače, takav se algoritam naziva algoritam grube sile, „brute force“). Složenost algoritma je eksponencijalna, O(2n), jer je potrebno pronaći i provjeriti sve moguće podskupove. Broj kombinacija k-tog razreda od n elemenata bez ponavljanja – binomni koeficijenti – je: n! k !( n k )! n k . Služeći se dinamičkim programiranjem problem možemo riješiti složenošću O(Wn). 27 Algoritmi i logička struktura programa Ranac sa ponavljanjem Počnimo s algoritmom kada je dozvoljeno ponavljanje. Kao i uvijek da bi dinamički riještli problem moramo si postaviti pitanje „Koji su potproblemi?“ Označimo sa K(w) najveću moguću vrijednost koju može ponijeti ruksak kapaciteta W. Probajmo to izraziti kao potproblem. Dakle, ako je K(w) optimalna solucija za težinu w, te sadrži stvar i, onda izbacivanje i-te stvari iz torbe dobivamo optimalnu soluciju za težinu w - wi. Drugim riječima, K ( w) K ( w wi ) vi , za neki i . Pošto ne znamo točno koji je element najbolje dodati, isprobajmo sve kombinacije: K (w) max K (w wi ) vi , i:wi w gdje je uvjet da je maksimalna vrijednost prazne torbe jenaka 0. Ranac bez ponavljanja Druga varijanta je kad ponavljanja nisu dozvoljena, odnosno kad ima samo jedan uzorak svake stvari. Sada funkciju K(w) moramo malo izmjeniti jer u prvom slučaju ne znamo koje smo stvari stavili u torbu a koje nismo. Stoga, dodajemo novi parametar, 0 j n. K(w, j) sada predstavalja najveću moguću vrijednost torbe kapaciteta w i stvari od 1 do j. Kako sada možemo problem K(w, j) izraziti pomoću potproblema? Jednostavno, samo gledamo je li je j-ti element potreban da bi smo dosegli maksimalnu vrijednost pri kapacitetu w. K (w, j ) max K (w wj , j 1) v j , K (w, j 1) . Ovaj algoritam, baš kao i prošli, složenosti je O(nW). 28 Algoritmi i logička struktura programa Najkraći put među čvorovima u grafu Zadan je graf sa N čvorova. IzmeĎu odreĎenih čvorova postoji odreĎena udaljenost. Traži se najkraći put izmeĎu svih čvorova zadanog grafa. Dan je slijedeći graf: Moramo pronaći najmanju udaljenost čvora A od ostalih čvorova, čvora B od ostalih čvorova, itd. sve dok ne pronaĎemo sve najkraće udaljenosti. Da bi riješili ovaj problem moramo ga svesti na manje potprobleme. Što je ovdje potproblem? Pošto tražimo najmanju udaljenost čvora u od čvora v, a da kao meĎučvorove možemo koristit sve čvorove u grafu, potproblemom možemo smatrati, pronaći najkraći put od u do v koristeći samo k čvorova. Kada je k jednak 1 potproblem je jednostavan. Porastom broja k raste i težina problema. Rješenje polaznog problema dobivamo za k = N – 1. Označimo sa dist(u, v, k) najmanju udaljenost od čvora u do v, a da pritom kao meĎučvorove koristimo samo čvorove od {1,...,k}. Pri čemu je dist(u, v, 0) direktna veza izmeĎu čvorova u i v, ako ona postoji. Sada možemo reći da koristeći čvor k dobivamo manju udaljenost ako i samo ako je udaljenost od čvora u do čvora k zbrojena sa udaljenošću od čvora k do čvora v manja od udaljenosti čvora u do čvora v koristeći čvorove od {1, ... , k – 1}. Odnosno, dist (u , k , k 1) dist (k , v, k 1) 29 dist (u , v, k 1) . Algoritmi i logička struktura programa POHELPNI PRISTUP U mnogim algoritmima opitmizacije potrebno je napraviti niz izbora. Strategiju dinamičkog programiranja često koristimo pri rješavanju takvih problema, gdje se optimalno rješenje traži primjenom rekurzije računajći od najmanjih problema prema složenijim („bottom up“). Nažalost, strategija dinamičkog programiranja nekad dovodi do neefikasnih algoritama, najčešće zbog predugih vremenskih izvršavanja. Stoga, u takvim slučajevima alternativnu tehniku koju koristimo je strategija pohlepnog algoritma. Ova strategija najčešće dovodi do jednostavnih i veoma brzih algoritama, ali nažalost nije toliko moćna kao dinamičko programiranje. Tehnika pohelpnog pristupa ne daje uvijek optimalno rješenje, no iako neda optimalno rješenje, često vodi na novu strategiju razvoja algoritma koji je efikasniji od prijašnjeg. Tehnikom pohelpnog pristupa, rješenje zadanog algoritma se konstruira u nizu koraka. U svakom se koraku bira mogućnost koja je lokalno optimalna u nekom smislu. Zamisao je da će nas takvi optimalni koraci devesti do globalnog optimalnog rješenja. Kako bismo ilustrirali što je to pohlepni pristup razradit ćemo nekoliko primjera: Trgovac vraća mušteriji iznos od 62 kn, na raspolaganju su mu novčanice od 50, 20, 10, 5 i 1 kn. Večina će ljudi instiktivno vratiti jednu novčanicu od 50 kn, jednu od 10 kn i dvije od 1 kn. Takav algoritma vraća odreĎen iznos uz najkraću moguću listu novčanica. Znači, izabere se najveća novčanica koja ne prelazi ukupnu sumu, stavlja se na listu za vraćanje, oduzme se od ukupnog iznosa, te se postupak ponavlja sve dok se ne vrati odreĎen iznos. Ovakva strategija nas je dovela do najefikasnijeg rješenja, ali samo zahvaljujući specijalnim svojstvima odreĎenih novčanica. Primjer kad pohelpni pristup ne funkcionira je slijedeći. Što ako je potrebno vratiti 15 kn pomoću novčanica od 11, 5 i 1 kn. Pohlepni algoritam prvo vraća 11 kn i tada mu preostaje samo da vrati četiri novčanice od 1 kn. Znači ukupno 5 novčanica, dok bi optimalno rješenje bilo vratiti 3 puta po 5 kn. Na sličan način pohelpni pristup možemo primijeniti i na problem 0/1 ranca. Generalizirajmo prijašnji problem. Dano je n novčanica kojima možemo isplaćivati iznos: {A[0], A[1],...,A[N-1]}. I dan nam je iznos koji trebamo vratiti. Već smo uočili da pohlepni pristup ne daje uvijek optimalno rješenje. Stoga je bolje koristiti dinamičko programiranje. 30 Algoritmi i logička struktura programa Označimo sa P(n, k) minimalan br. novčanica da se isplati iznos od k kuna koristeći novčanice od A[0] do A[n]. Možemo zaključiti da ako moramo isplatiti 14 kn novčanicama od 8, 5 i 2 kn, da nam je optimalno rješenje: ili ako isplaćujemo 14 kn bez zadnje novčanice ili ako dodajemo i zadnju novčanicu i gledamo minimalno rješenje za k – A[n]. Zato pišemo: P(n, k ) min P(n 1, k ),1 P(n, k A n ) . Problem trgovačkog putnika (TSP) Trgovački putnik želi posjetiti niz gradova i vratiti se u početni. Ako znamo vrijeme putovanja izmeĎu svaka dva grada, kako napraviti plan putovanja da svaki grad osim polaznog posjeti jednom a da ukupno vrijeme putovanja bude najmanje moguće? Gradove predočavamo čvorovima, a put bridovima grafa. Udaljenost računamo kao euklidsku udaljenost dviju točaka u ravnini. Ovaj problem je NP težak, što znači da ne postoji polinomijalni algoritma koji ga egzaktno rješava. Evo približnog pohlepnog algoritma: Označimo sa Eg skup svih bridova u polaznom grafu. Cilj nam je u svakom trenutku odrediti što bi nam bilo najbolje. U skup E stavljamo najkraći brid iz skupa Eg \ E sa svojstvom da; 1) Brid b ne prouzroči da neki vrh T bude trećeg stupnja (odnosno, da iz tog grada ima 3 različita puta – prema 3 različita grada) 2) Brid b neće zatvoriti krug, osim ako to nije zadnji grad (odnosno u skupu E se nelazi N – 1 bridova) 31 Algoritmi i logička struktura programa Rješenje spremamo u graf T(V, E) koji je na početku prazan. Gledajući gornji graf, dodajemo najkraći brid. To je (d, e) = 3. Sada u skupu E imamo {(d,e)}. Dalje dodajemo najkraći brid koji zadovoljva oba uvjeta. To su: (b, c), (a, b), (e, f). Svi su oni duljine 5. Vidimo da nije važan redoslijed kojim dodajemo u graf. Sada je E = {(d, e), (b, c), (a, b), (e, f)}. Naš graf T igleda: Idući najkraći brid u grafu je (a, c) ali on bi zatvorio krug pa ga ne dodajemo, slično postupamo i sa bridom (f, d). Idući najkraći je (c, d) duljine 14. On zadovoljava oba uvjeta te ga dodajemo. Od svih ostalih bridova jedino brid (a, f) zadovoljava uvjete. Dodajemo ga. Dobili smo konačno rješenje duljine 50. 32 Algoritmi i logička struktura programa Opet možemo uočiti da pohlepni pristup ne daje optimalno rješenje jer je najkraći put jednak 48.39, graf izgleda ovako: Primjer 1) Mirko je na dar dobio čokoladu sa m × n kockica. On je jako praznovjeran i moram ju prvo razlomiti na djelove koji su kvadrati (veličine k × k kockica). Mirko želi što prije moguće početi jesti čokoladu pa ga zanima koliko je najmanje lomljena potrebno. Vidimo da smo ovu čokoladu sa 5 × 3 kockica lomili 3 puta. Pogledajmo najprije pohelpni pristup: Ako je m > n, odlomimo kvadrat dimenzija n × n i komad čokolade dimenzija (m – n) × n, a ako je n > m, odlomimo kvadrat dimenzija m × m i ostane nam komad čokolade dimenzija m × (n – m). Dakle, u ovom algoritmu prvo odlomimo najveći mogući kvadrat, pa najveći mogući kvadrat od ostatka itd. Ali se takoĎer lako uočava da to nije optimalno rješenje (čokolada 5 × 6). Kao i problem sa novčanicama, tako i ovaj možemo riješiti dinamičkim programiranjem. Označimo sa P(m, n) minimalan broj lomova čokolade sa m × n kockica. Čokoladu možemo prelomiti nakon 1, 2, 3,..., (m-1) retka ili nakon 1, 2, 3,..., (n-1) stupca. Ako prelomimo nakon i-tog retka onda je broj lomova jednak: P(i, n) + P(m-i, n) +1 33 Algoritmi i logička struktura programa Stoga tražimo optimalno mjesto za lom. P(m, n) = 0 ako je m = n. P(m, n) min mini P(m i, n) P(i, n) 1 , min j P(m, n j) P(m, j) 1 Koristeći gornju formulu možemo jednostavno „top down“ pristupom izračunati broj lomova. BACKTRACKING Strategija odustajanja ili backtracking jedna je od vrlo općenitih tehnika koje se primjenjuje za teške kombinatorne probleme. Rješenje nekog problema traži se sistematskim ispitivanjem svih mogućih kombinacija. Prostor rješenja prikazujemo ureĎenim stablom gdje korijen predstavlja sve moguće n-torke, a dijete korijena predstavlja sve n-torke gdje prva komponenta (x1) ima odreĎenu vrijednost. Rješenje problema odgovara jednom listu stabla problema. Kako bi lakše dočarali ureĎeno stablo, prikazat ćemo primjer: Ako se rješenje problema može prikazati kao ureĎena trojka (n=3) gdje je x1 iz skupa S1 = {1, 2, 3, 4}, x2 iz skupa S2 = {1, 2} i x3 iz skupa S3 = {1, 2, 3}, prostor rješenja se prikazuje gornjim grafom. Pri rješavanju problema koristimo osnovni rekurzivni algoritam koji simultano generira i ispituje čvorove u stablu rješenja. Ako čvor predstavlja rješenje poduzima se neka odgovarajuća akcija, a ako čvor nije rješenje generiraju se njegova djeca. Na početku imamo samo korijen stabla. Završetak je kada se pronaĎe rješenje. Pošto veličina prostora rješenja raste eksponencijalno dobar algoritam strategije povlačenja nikad ne genrira cijelo stablo, 34 Algoritmi i logička struktura programa nego odustaje od grana za koje uspije utvrditi da ne vode do rješenja. U postupku generiranja čvorova provjeravaju se ograničenja koja n-torka mora zadovoljiti da bi zaista bila rješenje. Strategija povlačenja često se rabi u problemima optimizacije gdje se od svih mogućih rješenja traži ono koje je najoptimalnije. Optimalnost se mjeri funkcjom koju je potrebno minimalizirati ili maksimalizirati. Ovisno o problemu funkciju interpretiramo kao cijenu, zaradu, trošak ili slično. Osim što odustajemo od grana koje ne vode do rješenja odustajemo i od onih grana koje ne vode do boljeg rješenja. Takva varijanta algoritma, naziva se algoritam grananja i preskakanja (branch and bound). Problem n kraljica Na šahovskoj ploči veličine n × n polja treba postaviti n kraljica tako da se one meĎusobno ne napadaju. Rješenje: Očito je da svaka kraljica mora biti u posebnom retku ploče, pa onda možemo uzeti da je i-ta kraljica u i-tom rektu, stoga se rješenje problem može prikazati kao n-torka (x1, x2, x3, ..., xn), gdje je xi indeks stupca u kojem se nalazi i-ta kraljica. Organičenja koje se moraju zadovoljiti izvode se iz zahtjeva da se niti jedan par kraljica ne smije nalaziti u istom stupcu i istoj dijagonali. Kao primjer razmotrimo situaciju kada je n = 4. Strategija povlačenja generira stablo rješenja: 35 Algoritmi i logička struktura programa Crtkano su označeni oni čvorovi koje je algoritam odbacio odmah u postupku generiranja jer krše ograničenja. Rad algoritma prekinut je čim je naĎeno prvo rješenje. Rješenje je: {2, 4, 1, 3}. Slično ovom primjeru možemo napraviti algoritam za odreĎivanje optimalnog poteza u igri križić kružić. Križić – kružić Zadana je tablica za igru križić kružić na kojoj su već odigrani neki pozeti. Treba utvrditi koji igrač ima strategiju koja vodi do pobjede, te koji je potez optimalan. Pretopostavimo da je na potezu igrač „x“. Ako postoji način da on pobijedi, toj se situaciji dodijeli oznaka 1. Ako ne može pobijediti ali može igrati neriješeno, situacija se označava sa 0, a ako gubi kako god igrao situacija se označava s -1. Slično tome ako je „o“ na potezu, situaciju u kojoj on može pobijediti označimo sa -1, kada može igrati najviše neriješeno označimo sa 0 i kada gubi sa 1. Zadana je slijedeća situacija: 36 Algoritmi i logička struktura programa Na potezu je križić i ima 3 slobodna polja na koja može odigrati. Ovdje ide stablo rješenja.. al mi se nije dalo crtat! Strategijom odustajanja dobijemo stablo situacija. Lijevo dijete dobiva oznaku 1 jer je pobjeda za „x“. Desno dijete je dobilo oznako 0 jer je na pozetu „o“, a ako on odigra prvi potez rezultat je neriješen, a ako odigra drugi potez, pobejĎuje „x“. Srednje djete dobiva oznaku -1 jer je ovisno o tome što igra „o“, „x“ gubi ili igra neriješeno. Iz ovog je jasno da je oznaka svakog čvora u kojemu je na potezu „o“ jednaka minimumu svih oznaka djece i analogno ako je na potezu „x“ oznaka čvora je maksimum svih oznaka djece. Dakle algoritam rješavanja je slijedeći: Pobjednika odreĎujemo konstruirajući stablo svih situacija dostižljivih iz zadane situacije; korijen stabla je zadana situacija ,a djeca nekog čvora su situacije do kojih se može doći jednim potezom iz tog čvora. Listovi stabla predstavljaju kraj igre. 37 Algoritmi i logička struktura programa ALGORITMI SORTIRANJA Kao uvod u algoritme sortiranja proučit ćemo nekoliko osnovnih metoda koje su prikladne za sortiranje manjih datoteka ili datoteka sa specijalnom strukturom. Postoji nekoliko razloga zbog kojih je dobro analizirati ove jednostavnije metode; npr., predstavljaju nam relativno jednostavan način da naučimo terminologiju i jednostavnije mehanizme sortiranja. Na taj način dobivamo dobru pozadinu za daljnje proučavanje složenijih algoritama. Nadalje, nekada je bolje koristiti jednostavnije metode nego one složenije. Algoritam sortiranja se u programu najčešće koriste samo jedanput, ili samo nekoliko puta; ako je broj elemenata koje treba sortirati relativno mali (< 500), efikasnije je iskoristiti jednostavniju metodu sortiranja. Osim datoteka sa malim brojem elemenata, relativno lako se sortiraju i datoteke koje su skoro sortirane. U takvim situacijama takoĎer je bolje koristiti jednostavnije metode. Kao i obično, najvažnija stvar, koja nas zanima, je brzina sortiranja, odnostno vremenska složenost. Vidjet ćemo da elementarne tehnike sortiranja su kvadratne složenosti (O(n2)), dok oni najbriži algoritmi sortiranja sortiraju brzinom N log N, O(N log N). Slijedeća stvar koju moramo uzeti u obzir je prostorna složenost. Teoretski se algoritmi sortiranja dijele u tri skupine: algoritmi koji sortiraju na mjestu i ne zahtijevaju dodatnu memoriju, algoritmi koji koriste vezane liste („linked list“) i zahtijevaju N dodatnih mjesta u memoriji za pokazivače i algoritmi kojima je potrebna dodatna memorija da čuvaju još jedan niz. 38 Algoritmi i logička struktura programa SELECTION SORT Jedan od najjednostavnijih algoritama sortiranja je selection sort, a radi na slijedeći način: Prvo pronaĎe najmanji element u nizu i zamijeni ga sa elementom na prvom mjestu, zatim pronaĎe drugi najmanji element i zamjeni ga sa elementom na drugom mjestu i tako dalje dok ne sortiramo cijeli niz. Metoda se naziva selection sort zbog toga što neprestano odabire najamanji preostali element u nizu. U prvom koraku pretražujemo sve elemente u nizu (N), u drugom koraku pretražujemo N-1 elemente itd do N-tog koraka gdje pretražujemo samo jedan element. Stoga je složenost algoritma kvadratna, O(N2). INESERTION SORT Baš kao što i samo ime govori algoritam ubacuje svaki član na njegovo mjesto u završnoj listi. Najednostavnija implementacija ovog algoritma zahtijeva dvije liste – početnu listu i listu u koju ubacujemo sortirane elemente. Da bi uštedjeli memoriju možemo koristit i jednu listu tako što ćemo trenutni element zamjeniti s prethodnikom ukoliko je prethodnik veći te postupak i taj postupak ćemo ponavljati dok element ne doĎe nas svoje mjesto. Sličan algoritam čovjek nesvjesno koristi pri slaganju karata u nekoj kartaškoj igri. Kao i prethodni, ovaj algoritam je kvadratne složenosti. Iako su svi jednostavni algoritmi sortiranja kvadratne složenosti, ovaj algoritam je najefikasniji. Čak duplo efikasniji od „bubble sorta“ i 40% efikasniji od „selection sorta“. 39 Algoritmi i logička struktura programa BUBBLE SORT Bubble sort je najstariji i najjednostavniji algoritam sortiranja koji se koristi, ali nažalost i najsporiji je. Bubble sort radi tako što svaki element usporeĎuje sa susjednim elementom i zamijenjuje ukoliko je to potrebno. Algoritam ponavlja proces sve dok ne proĎe kroz sve elemente a da nije napravio ni jednu zamjenu. Ovaj algoritam je najsporiji u praktičnoj primjeni, no ako je lista kojim slučajem već sortirana on će ju proći u linearnom vremenu, za razliku od „insertion sorta“ i „selection sorta“. QUICK SORT Definitivno, quick sort, je jedan od najkorištenijih algoritama za sortiranje. Algoritam je izmišljen 1960. godine, a izmislio ga je C.A.R. Hoare i od tad ga mnogi ljudi proučavaju u nadi da ga poboljšaju. Vrlo je popularan upravo zbog toga što ga nije teško implementirati, a i funkcionira sa različitim tipovima podataka i najvažnije od svega, najbrži je danas poznati algoritam za sortiranje. Vremanska složenost mu je N log N, a u najgorem slučaju kvadratna. Algoritam je zasnovan na strategiji podijeli pa vladaj i svodi se na slijdeće: 1) Odabriemo jednog člana niza, tzv. pivota 2) Raspodijelimo niz tako da sve članove manje od pivota stavimo lijevo od njega, a sve članove veće od pivota stavimo na njegovu desnu stranu. Očito je da je pivot na svom mjestu (na tom će mjestu bit i u sortiranoj listi). 3) Rekurzivno sortiramo svaki podniz na isti način (imamo 2 podniza, prvi je s lijeve strane pivota, a drugi s desne strane, pivot nam je slučajno odabrani element u nizu, stoga slučajno odabrani element može biti i najmanji i najveći član pa imamo samo jedan podniz). 40 Algoritmi i logička struktura programa Ovdje ide još jedna slika!! Rekurzija se prekida na nizovima od jednog ili niti jednog elementa, koji su sami po sebi sortirani. Quick sort, u svojoj iteraciji stavlja barem jedan element niza na svoju konačnu poziciju. Možemo primijetiti da ako u nizu od N članova uzememo da je pivot maksimalan element onda ćemo ga staviti na kraj, u koliko u slijdećiem podnizu opet odaberemo maksimalan element opet ćemo ga staviti na kraj i dobiti samo jedan podniz, stoga ako stalno uzimamo najveći element u nekom nizu složenost će biti kvadratna. U ovom algoritmu ključno je kako ćemo dovesti pivota na pravo mjesto. Odnosno, efikasno moramo izvesti podjelu na potprobleme. Dovođenje pivotnog elementa na pavo mjesto Primjer) Imamo listu L = (26, 5, 27, 1, 61, 11, 59, 15, 48, 19) Uzmimo da je pivotni element a1 – 26. Postavimo dva kursora (l i r): Kursor l kreće od drugog elementa dok kursor r kreće od zadnjeg elementa. Kursor l ide udesno dok ne naiĎe na element koji je veći od pivota dok kursor r ide nalijevo dok ne naiĎe na element koji je manji od pivota. Dakle, kursor l se pomiće do prvog većeg broja od 26. Kursor r se pomiće do prvog broja manjeg od 26, dakle ostaje na mjestu. Elementi na kojima su kursori stali se zamijene. 41 Algoritmi i logička struktura programa Kursori se nastavljaju pomicat po istoj logici sve dok se ne prekriže. Kada se prekriže pivotni element zamijenjujemo sa onim elementom na koji pokazuje kursor r. Sada niz izgleda: Primjećujemo da je pivotni element na pravome mjestu. Isti postupak primijenjujemo i na podliste. 42 Algoritmi i logička struktura programa ZAKLJUČAK Svakim danom sve više i više primjećujemo kako nas ogromni val nove tehnologije preplavljuje. Iako su nam sada samo stopala u vodi, ubrzo ćemo svi primijetiti da je tehnologija ta koja će nas u budućnosti kontrolirati, na taj način, što ćemo ovisiti o njoj kao što ovisimo o hrani i piću. Govorim ovo jer znam da se iza svake sprave, iza svakog računala i iza svakog stroja koji ima neku namjenu krije neki algoritam koji ga upravlja, koji mu govori što da radi. Razumijevanje načina na koji radi stroj može nas dovesti do znatne uštede novaca i vremena, jer je već i danas većina stvari kompjutorizirana, a razumijevanje algoritma kojim radi odreĎen stroj nam može znatno pomoći pri korištenju istoga. TakoĎer, algoritmi nemaju samo primjenu u računalsvu, jer informatika nije sama sebi svrha, odnosno samo s informatikom ne možemo skoro ništa. Kao što sam već rekao u uvodu, algoritmi su svuda oko nas, a sposobnost raščlanjivanja problema na manje probleme koje je relativno lako riješiti nam svakako mogu pomoći u svakidašnjem životu. Ukoliko pogledamo oko sebe, shvatit ćemo da je svaka stvar sačinjena od manjih stvari, a te manje stvari zajedno čine neku cjelinu koja funkcionira. Ista stvar je i sa odreĎenim problemom. Ukoliko shvatimo manji problem i uspijemo pronaći način na koji će više takvim malih problema sačiniti veliki, ustvari smo riješili problem. Najljepša stvar u cijeloj priči je ta što je odgovor na večinu pitanja već dan. Samo treba pogledat oko sebe i proučiti malo prirodu, jer mravi kad prolaze kroz mravinjak, oni prolaze najkraćim putem, baš isto što i trgovački putnik želi. Osim što sam ovim radom htio,izmeĎuostalog, pomoći budućim učenicima, koji se žele baviti informatikom, da lakše shvate odreĎene pristupe nekim problemima i da dobiju neke osnove koje će im itekako dobro doći kao budućim informatičarima, želio sam i razuvjeriti one ljude koji misle da su matematika i informatika nekreativne znanosti. Nekad se rješenja kriju u tako jednostavnim stvarima, prejednostavnim da bi čovjek, koji se na nekoj hijerahiji stavlja na vrh piramide, uspio shvatiti. 43 Algoritmi i logička struktura programa LITERATURA Bujanovid, Z., Jelaska, I., Puljid, K., Strukture podataka i algoritmi – skripta, PMF, Zagreb, 2008. Dsagupta, S., Papadimitrion, C. H., Vazirani, U. V., Algorithms, Berkley, USA, 2006. Drozdek, A., Data structures and Algorithms in C++, Brooks/Cole, USA, 2001. Knuth, D., The art of computer programming, Addison-Wesley, Stanford University, 1997. Manager, R., Marušid, M., Strukture podataka i algoritmi - skripta, PMF, Zagreb, 2003. Sedgewich, R., Algortihms, Addison-Wesley, USA, 1983. http://www.csc.liv.ac.uk/~ped/teachadmin/algor/intro.html http://www.nist.gov/dads/ 44
© Copyright 2024 Paperzz