A274101 TIETORAKENTEET JA ALGORITMIT ALGORITMIEN ANALYYSISTÄ 1.ratkaisu Laskentaaika hakkeri - optimoitu ALGORITMIANALYYSIÄ hyvä algoritmi hakkeri -optimoitu hyvä algoritmi Tehtävän koko Kuva mukailtu Antti Valmarin tekemästä TTY:n opintomonisteesta Tietorakenteet ja algoritmit. 20.9.2005 KyAMK - TiRak, syksy 2005 2 ALGORITMIEN ANALYYSISTÄ Algoritmien suoritus vie resursseja Laskenta-aikaa Muistia Analysoinnissa oletetaan, että käytössä on tavallinen tietokone Käskyt suoritetaan peräkkäin yksi kerrallaan Erityisesti suoritusaika kiinnostaa Miten alkioiden määrä ja algoritmi vaikuttavat suoritusaikaan? ALGORITMIEN ANALYYSISTÄ Tietorakenne vaikuttaa olennaisesti Voivat suosia joitakin operaatioita Esim. lisäys voi olla nopea, mutta kysely hidas tai päinvastoin Toisaalta jotkin algoritmit voivat erota juuri ko. operaatioiden käytön osalta Käyttökohde on tiedettävä tietorakenteita ja algoritmeja valittaessa Esimerkkinä tietoaineiston järjestäminen Jos aineiston tyypillinen koko on pieni yksinkertainen algoritmi riittää Jos miljoonia alkioita algoritmin tulee olla nopea 20.9.2005 KyAMK - TiRak, syksy 2005 3 20.9.2005 KyAMK - TiRak, syksy 2005 4
ALGORITMIEN ANALYYSISTÄ Algoritmin hyvyys voi siis olla Laskenta-aika Tarvittavan muistin määrä Koodin lyhyys Kurssilla keskitytään nimenomaan algoritmien laskenta-aikaan (eli suoritusaikaan) Merkitään algoritmin suoritusaikaa T(n):llä n on algoritmin käsittelemien tietojen lukumäärä Määritellään iso O ( Big Oh ): T(n) = O(f(n)), jos on olemassa c > 0 ja n 0 > 0 siten että T(n) <= c f(n), kun n >= n 0 Tämä tarkoittaa käytännössä, että f(n) on suuruusluokkayläraja suoritusajalle T(n) 20.9.2005 KyAMK - TiRak, syksy 2005 5 20.9.2005 KyAMK - TiRak, syksy 2005 6 Laskentaaika c f(n) T(n) = 5n 2 + 7n + 10 = O(n 2 ), T(n) sillä (ainakin) f(n) 5n 2 + 7n + 10 6 n 2, kun n 10 n 0 Tehtävän koko Eli tässä voitiin valita c=6 ja n 0 =10 20.9.2005 KyAMK - TiRak, syksy 2005 7 20.9.2005 KyAMK - TiRak, syksy 2005 8
800 700 600 500 400 300 200 100 6n 2 n 2 5n 2 +7n+10 2 4 6 8 10 12 n T1(n) = 1000 n = O(n) T2(n) = 0.5 n 2 = O(n 2 ) Esimerkissä n:n pienillä arvoilla T2(n) < T1(n) Syötekoon kasvaessa T1(n) < T2(n) n on tällöin jotenkin pienempi funktio kuin n 2 Olemme kiinnostuneita suoritusajasta n:n suurilla arvoilla voidaan päätellä (vähän epätäsmällisesti): mitä pienempi f(n) sitä nopeampi algoritmi 20.9.2005 KyAMK - TiRak, syksy 2005 9 20.9.2005 KyAMK - TiRak, syksy 2005 10 Iso O -notaatiota ilmoittaa, miten algoritmin suoritusaika käyttäytyy suurilla n:n arvoilla Tällöin O:n argumenttina on lähes aina jokin seuraavista ns. testifunktioista: 1 vakio n lineaarinen n 2 neliöllinen (kvadraattinen) n 3 kuutiollinen log n logaritminen log 2 n neliöllinen logaritminen Huom. log 2 n = (log n) 2 n log(n) 2 n eksponentiaalinen Huom.1 Algoritmianalyysin logaritmifunktiot ovat aina 2-kantaisia. log n tarkoittaa käytännössä log 2 n. Huom.2 Ei merkitä esim., että T(n) = O(3n 2 + 5 n log n + 13 n), vaan tässä tapauksessa T(n) = O(n 2 ). O-merkintään vain funktion f(n) nopeimmin kasvava termi. Huom.3 Olkoon T(n) = 6n + 5. Tällöin T(n)=O(n), mutta myös T(n)=O(n 2 ), T(n)=O(n 3 ) jne. O(f(n)) on suuruusluokkayläraja T(n):lle. Tässä tapauksessa paras (ja moraalisesti ainoa oikea) on O(n), koska aina on syytä pyrkiä esittämään mahdollisimman tiukka yläraja. 20.9.2005 KyAMK - TiRak, syksy 2005 11 20.9.2005 KyAMK - TiRak, syksy 2005 12
ISON O:N SUKULAISET Iso O ei ole ainoa funktio, jonka avulla voidaan luokitella algoritmien suoritusaikoja Se on kuitenkin eniten käytetty Ison O:n sukulaisia eli vastaavia luokittelufunktioita ovat: Iso Omega (Ω) Iso Theta (Θ) Pieni o ISON O:N SUKULAISET Iso Omega (Ω) T(n) = Ω(f(n)), jos on olemassa c > 0 ja n 0 > 0 s.e. T(n) c f(n), kun n n 0 Ω on muuten kuten iso O paitsi että se antaa suuruusluokka-alarajan T(n):lle T(n) on vähintään suuruusluokkaa f(n) 20.9.2005 KyAMK - TiRak, syksy 2005 13 20.9.2005 KyAMK - TiRak, syksy 2005 14 ISON O:N SUKULAISET ISON O:N SUKULAISET Iso Theta (Θ) T(n) = Θ(f(n)), jos ja vain jos T(n) = O(f(n)) ja T(n) = Ω(f(n)) Θ siis ilmaisee T(n):n suuruusluokan täsmälleen T(n) on täsmälleen suuruusluokkaa f(n) Jostain syystä sitä käytetään kuitenkin vähemmän kuin isoa O:ta Pieni o T(n) = o(f(n)), jos T(n) = O(f(n)) ja T(n)!= Θ(f(n)) Pikku o siis antaa aidon eli reilun ylärajan T(n):lle T(n):n suuruusluokka on varmasti pienempi kuin f(n) 20.9.2005 KyAMK - TiRak, syksy 2005 15 20.9.2005 KyAMK - TiRak, syksy 2005 16
ISON O:N SUKULAISET Olkoon T(n) = 7n log n + 12n + 1 Tällöin pätevät muun muassa seuraavat: T(n) = Ω(n), T(n) = Ω(n log n) T(n) = Θ(n log n) T(n) = o(n 2 ) Sen sijaan mm. seuraavat eivät päde: T(n) = Θ(n) T(n) = Ω(n 2 ) T(n) = o(n log n) ISON O:N LASKUSÄÄNTÖJÄ 1. Jos T 1 (n) = O(f(n)) ja T 2 (n) = O(g(n)), niin T 1 (n) + T 2 (n) = O(max(f(n), g(n))) Summattaessa nopeimmin kasvava voittaa eli peittää alleen hitaammat 2. Jos T 1 (n) = O(f(n)) ja T 2 (n) = O(g(n)), niin T 1 (n)*t 2 (n) = O(f(n)*g(n)) 3. Jos T(n) on k:nnen asteen polynomi, niin T(n) = O(n k ), ja tiukemmin arvioituna Θ(n k ) Tämä on itse asiassa kohdan 1. sovellus: polynomin nopeimmin kasvava termi määrää 4. Jos T(n) = log k n tai T(n) = log(n k ) niin, T(n) = O(n) Logaritmifunktio potensseineen kasvaa hitaammin kuin lineaarinen funktio, siis tosi hitaasti 20.9.2005 KyAMK - TiRak, syksy 2005 17 20.9.2005 KyAMK - TiRak, syksy 2005 18 OLETUKSIA - MITÄ ANALYSOIDAAN? OLETUKSIA - MITÄ ANALYSOIDAAN? Algoritmit toteutetaan tietokoneella, josta oletetaan käskyt suoritetaan peräkkäin jokainen alkeisoperaatio vie yhden yksikön aikaa sananpituus on kiinteä, esim. 32 bittiä muistia on käytössä äärettömästi (eli riittävästi ) Alkeisoperaatiolla tarkoitetaan esimerkiksi laskutoimitusta +, -, *, / sijoitusta muuttuja = lauseke (lausekkeen muodostamisaika laskettava erikseen) kahden lausekkeen arvon vertailu (lausekkeet taas erikseen) Onko oletus alkeisoperaatioiden suoritusaikojen yhtäsuuruudesta epärealistinen? Jos esimerkiksi sijoitus vie k-kertaisen ajan vertailuun nähden, n:n peräkkäisen vertailun ja sijoituksen viemä kokonaisaika on (1 + k)n eli kaikilla n:n arvoilla O(n) Näillä oletuksilla siis analysoidaan algoritmin suoritusaikaa T(n), missä n on algoritmin käsittelemien tietoalkioiden määrä Suoritusaikaa voidaan analysoida a) helpoimmassa tapauksessa T best (n) b) keskimääräisessä tapauksessa T avg (n) c) pahimmassa tapauksessa T worst (n) Kiinnostavimmat ovat kaksi viimeksimainitua, varsinkin viimeinen: algoritmi ei vie ainakaan tämän enempää aikaa 20.9.2005 KyAMK - TiRak, syksy 2005 19 20.9.2005 KyAMK - TiRak, syksy 2005 20
OLETUKSIA - MITÄ ANALYSOIDAAN? TAVALLISTEN RAKENTEIDEN T worst (n) usein helpompi määrittää kuin T avg (n) On mahdollista, että kaikilla on eri iso O, mutta on myös mahdollista, että isot O:t ovat samat Yksinkertaisella vaihtolajittelumenetelmällä (Selection Sort) kaikki kolme ovat O(n 2 ) Nopealla Quicksort-menetelmällä T best (n) = T avg (n) = O(n log n), mutta T worst (n) = O(n 2 ) Onneksi tämä pahin tapaus pystytään lähes aina välttämään 20.9.2005 KyAMK - TiRak, syksy 2005 21 Tarkastellaan pientä funktiota, joka laskee summan 1 + 2 + 3 +... n int summa( int n ) { // 1 int s = 0; // 2 for (int i = 1; i <= n; ++i){ // 3 s = s + i; // 4 } return s; // 5 } Lasketaan suoritusaika T(n) edellisin oletuksin: Mitä tehdään Montako kertaa 1: todellisen parametrin sijoitus muodolliseen 1 2: sijoitus 1 3: i:lle alkuarvo 1 vertailu i <= n, n kertaa n kasvatus ++i, n kertaa n 4: yhteenlasku ja sijoitus 2n 5: palautusarvo 1 ------- Yhteensä 4n + 4 Algoritmin suoritusaika on siis T(n) = O(n) eli algoritmi on aikavaativuudeltaan lineaarinen Rekursioluvussa esitettiin parempi algoritmi, jonka aikavaativuus on O(1), siis vakio 20.9.2005 KyAMK - TiRak, syksy 2005 22 TAVALLISTEN RAKENTEIDEN Sääntöjä: 1. Peräkkäisrakenne lause1; lause2;... Peräkkäisten lauseiden kokonaissuoritusaika = yksittäisten lauseiden suoritusaikojen summa O-luokan määrää yksittäisten lauseiden suurin O-luokka TAVALLISTEN RAKENTEIDEN 2. for-rakenne for ( int i = 0; i < n; ++i ) lause; Suoritusaika = n*(lauseen suoritusaika + testin ja etenemisen suoritusaika) Suoritusaika on siis: 1. O(n), jos lauseen suoritusaika = O(1) 2. O(n 2 ), jos lauseen suoritusaika = O(n) 3. olettaen, että testi ja eteneminen saadaan suoritetuksi ajassa O(1) (näin tapahtuu hyvin usein) 20.9.2005 KyAMK - TiRak, syksy 2005 23 20.9.2005 KyAMK - TiRak, syksy 2005 24
TAVALLISTEN RAKENTEIDEN 3. Sisäkkäinen for-rakenne TAVALLISTEN RAKENTEIDEN 4. if-rakenne for ( int i = 0; i < n; ++i ) for ( int j = 0; j < n; ++j ) lause; Suoritusaika = n*n*(lauseen suoritusaika + testin ja etenemisen suoritusaika) if ( ehto ) lause1; else lause2; // T1(n) // T2(n) Suoritusaika on 1. O(n 2 ), jos lauseen suoritusaika = O(1) 2. O(n 3 ), jos lauseen suoritusaika = O(n) 3. Suoritusaika enintään = ehdon suoritusaika + max(t 1 (n), T 2 (n)) Jos ehto voidaan selvittää ajassa O(1), koko rakenteen suoritusaika on max(t 1 (n), T 2 (n)) 20.9.2005 KyAMK - TiRak, syksy 2005 25 20.9.2005 KyAMK - TiRak, syksy 2005 26 ERÄIDEN ALGORITMILUOKKIEN ERÄIDEN ALGORITMILUOKKIEN Seuraavassa esitetään esimerkinomaisesti suoritusaikoja erilaisille algoritmeille Tulokset vedetään osittain hihasta Tarkat analyysit saattavat joissakin tapauksissa olla melko vaikeita, eikä niitä tässä (tilan ja varsinkin ajan puutteen :) ) vuoksi esitetä. 1. n-sääntö: Algoritmin aikavaatimus on O(n), jos O(1)-operaatioilla voidaan vähentää n:ää yhdellä. Summa 1 + 2 + 3 +... + n rekursiivisesti: int reksumma( int n ){ if ( n == 0 ) return 0; else return reksumma( n - 1 ) + n; } // Rekursio Peräkkäishaussa jokainen käynti n-alkioisessa taulukossa vähentää n:ää eli käymättömien alkioiden määrää 1:llä Peräkkäishaun aikavaatimus on O(n). Tällainen häntärekursio (tail recursion), missä rekursiivinen kutsu on rakenteen viimeinen lause, on oikeastaan vain piilotettu toistorakenne Esimerkin tapauksessa jokainen kutsu, O(1), pienentää käsiteltävien tapausten määrää 1:llä Koko algoritmin aikavaatimus on siten O(n) 20.9.2005 KyAMK - TiRak, syksy 2005 27 20.9.2005 KyAMK - TiRak, syksy 2005 28
ERÄIDEN ALGORITMILUOKKIEN 2. log n-sääntö: Algoritmin aikavaatimus on O(log n), jos O(1)-operaatioilla voidaan puolittaa n. Puolitushaussa jokaisella kierroksella tarkastamaton osa (vähintään) puolittuu Puolitus ja alkion tarkastaminen = O(1) koko haku on ohi ajassa O(log n) ERÄIDEN ALGORITMILUOKKIEN Eukleideen algoritmi: gcd(a,b) = gcd(b, a mod b) Voidaan osoittaa, että a eli suurempi parametri vähintään puolittuu aina kahdella jaolla algoritmin aikavaativuus on O(2 log n) eli O(log n), missä n on a:n bittimäärä Huom. O(log n) algoritmit ovat erittäin nopeita Niitä kannattaa tavoitella Harvoihin ongelmiin löytyy näin nopea algoritmi puolitustapausten joukko on eräs tällainen 20.9.2005 KyAMK - TiRak, syksy 2005 29 20.9.2005 KyAMK - TiRak, syksy 2005 30 ERÄIDEN ALGORITMILUOKKIEN SEURAAVALLA KERRALLA 3. Eksponenttisääntö: Algoritmin aikavaatimus on O(2 n ), jos n:n kasvaessa 1:llä ongelman koko kasvaa k-kertaiseksi, k > 1 Rekursiivinen Fibonacci-algoritmi: f(n) = f(n-1) + f(n-2) osoittautui hyvin tehottomaksi: sama f(k) lasketaan rekursion edetessä toistuvasti uudestaan Voidaan osoittaa, että algoritmin suoritusaika on eksponenttityyppinen ( k on noin 1.6) eli T(n) = O(2 n ) Huom. Eksponentialiset algoritmit ovat äärimmäisen hitaita niitä tulisi kaikin voimin välttää Valitettavasti monissa tunnetuissa käytännön ongelmissa i. ei tunneta parempaa algoritmia ii. ei voida kehittää parempaa algoritmia Lineaariset abstraktit datatyypit (ADT:t): lista, pino, jono, pakka Linkitetty lista Pinoesimerkkejä 20.9.2005 KyAMK - TiRak, syksy 2005 31 20.9.2005 KyAMK - TiRak, syksy 2005 32