12. Algoritminsuunnittelun perusmenetelmiä

Samankaltaiset tiedostot
12. Algoritminsuunnittelun perusmenetelmiä

Algoritmit 2. Luento 8 To Timo Männikkö

811312A Tietorakenteet ja algoritmit, , Harjoitus 7, ratkaisu

A ja B pelaavat sarjan pelejä. Sarjan voittaja on se, joka ensin voittaa n peliä.

811312A Tietorakenteet ja algoritmit, VI Algoritmien suunnitteluparadigmoja

1.4 Funktioiden kertaluokat

Algoritmit 2. Luento 14 Ke Timo Männikkö

Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti

Algoritmit 1. Luento 11 Ti Timo Männikkö

f(n) = Ω(g(n)) jos ja vain jos g(n) = O(f(n))

10. Painotetut graafit

Vaihtoehtoinen tapa määritellä funktioita f : N R on

ICS-C2000 Tietojenkäsittelyteoria Kevät 2016

Algoritmit 1. Luento 13 Ti Timo Männikkö

2.2.1 Ratkaiseminen arvausta sovittamalla

Rekursio. Funktio f : N R määritellään yleensä antamalla lauseke funktion arvolle f (n). Vaihtoehtoinen tapa määritellä funktioita f : N R on

Algoritmit 1. Luento 12 Ke Timo Männikkö

Algoritmit 2. Luento 9 Ti Timo Männikkö

811120P Diskreetit rakenteet

Algoritmit 1. Luento 12 Ti Timo Männikkö

8. Lajittelu, joukot ja valinta

811312A Tietorakenteet ja algoritmit III Lajittelualgoritmeista

Olkoon S(n) kutsun merge-sort(a, p, q) tilavaativuus kun p q + 1 = n. Oletetaan merge toteutetuksi vakiotyötilassa (ei-triviaalia mutta mahdollista).

Numeeriset menetelmät

A TIETORAKENTEET JA ALGORITMIT

Algoritmit 1. Luento 3 Ti Timo Männikkö

ALGORITMIT 1 DEMOVASTAUKSET KEVÄT 2012

Epädeterministisen Turingin koneen N laskentaa syötteellä x on usein hyödyllistä ajatella laskentapuuna

58131 Tietorakenteet (kevät 2009) Harjoitus 6, ratkaisuja (Antti Laaksonen)

4 Tehokkuus ja algoritmien suunnittelu

Rekursioyhtälön ratkaisutapa #1: iteratiivinen korvaus

Algoritmit 2. Luento 13 Ti Timo Männikkö

Algoritmit 1. Luento 14 Ke Timo Männikkö

Johdatus diskreettiin matematiikkaan Harjoitus 5, Ratkaise rekursioyhtälö

M = (Q, Σ, Γ, δ, q 0, q acc, q rej )

Algoritmit 2. Demot Timo Männikkö

Algoritmit 2. Luento 10 To Timo Männikkö

58131 Tietorakenteet ja algoritmit (syksy 2015)

Algoritmit 2. Luento 3 Ti Timo Männikkö

Todistus: Aiemmin esitetyn mukaan jos A ja A ovat rekursiivisesti lueteltavia, niin A on rekursiivinen.

Algoritmit 1. Luento 2 Ke Timo Männikkö

ja λ 2 = 2x 1r 0 x 2 + 2x 1r 0 x 2

Valitaan alkio x 1 A B ja merkitään A 1 = A { x 1 }. Perinnöllisyyden nojalla A 1 I.

Tietorakenteet, laskuharjoitus 7, ratkaisuja

Algoritmit 1. Demot Timo Männikkö

Algoritmit 1. Demot Timo Männikkö

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

1. Esitä rekursiivinen määritelmä lukujonolle

10. Painotetut graafit

Algoritmit 2. Luento 3 Ti Timo Männikkö

Algoritmit 2. Luento 9 Ti Timo Männikkö

on rekursiivisesti numeroituva, mutta ei rekursiivinen.

Miten käydä läpi puun alkiot (traversal)?

Algoritmit 2. Luento 1 Ti Timo Männikkö

Algoritmi on periaatteellisella tasolla seuraava:

Algoritmianalyysin perusteet

Hakupuut. tässä luvussa tarkastelemme puita tiedon tallennusrakenteina

Kannan vektorit siis virittävät aliavaruuden, ja lisäksi kanta on vapaa. Lauseesta 7.6 saadaan seuraava hyvin käyttökelpoinen tulos:

Kielenä ilmaisten Hilbertin kymmenes ongelma on D = { p p on polynomi, jolla on kokonaislukujuuri }

Luku 8. Aluekyselyt. 8.1 Summataulukko

Algoritmit 2. Luento 2 To Timo Männikkö

Algoritmit 2. Luento 14 To Timo Männikkö

Algoritmit 2. Luento 13 Ti Timo Männikkö

Tietorakenteet ja algoritmit - syksy

Harjoitus 6 ( )

A TIETORAKENTEET JA ALGORITMIT

Algoritmit 2. Luento 2 Ke Timo Männikkö

Rinnakkaistietokoneet luento S

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Harjoitus 6 ( )

Tietorakenteet ja algoritmit. Kertaus. Ari Korhonen

Sekalaiset tehtävät, 11. syyskuuta 2005, sivu 1 / 13. Tehtäviä

Laskennan teoria (kevät 2006) Harjoitus 3, ratkaisuja

Luento 9: Yhtälörajoitukset optimoinnissa

58131 Tietorakenteet ja algoritmit (kevät 2016) Ensimmäinen välikoe, malliratkaisut

Johdatus matematiikkaan

Vasen johto S AB ab ab esittää jäsennyspuun kasvattamista vasemmalta alkaen:

4. Joukkojen käsittely

811312A Tietorakenteet ja algoritmit, , Harjoitus 3, Ratkaisu

Algoritmit 1. Luento 13 Ma Timo Männikkö

on Abelin ryhmä kertolaskun suhteen. Tämän joukon alkioiden lukumäärää merkitään

Nopea kertolasku, Karatsuban algoritmi

Diskreetin matematiikan perusteet Laskuharjoitus 2 / vko 9

Tiraka, yhteenveto tenttiinlukua varten

Algoritmit 2. Luento 13 Ti Timo Männikkö

TKT20001 Tietorakenteet ja algoritmit Erilliskoe , malliratkaisut (Jyrki Kivinen)

58131 Tietorakenteet ja algoritmit (kevät 2014) Uusinta- ja erilliskoe, , vastauksia

3. Hakupuut. B-puu on hakupuun laji, joka sopii mm. tietokantasovelluksiin, joissa rakenne on talletettu kiintolevylle eikä keskusmuistiin.

Numeeriset menetelmät TIEA381. Luento 12. Kirsi Valjus. Jyväskylän yliopisto. Luento 12 () Numeeriset menetelmät / 33

a k+1 = 2a k + 1 = 2(2 k 1) + 1 = 2 k+1 1. xxxxxx xxxxxx xxxxxx xxxxxx

REKURSIO. Rekursiivinen ohjelma Kutsuu itseään. Rekursiivinen rakenne. Rakenne sisältyy itseensä. Rekursiivinen funktio. On määritelty itsensä avulla

2. Eukleideen algoritmi

Esimerkkejä polynomisista ja ei-polynomisista ongelmista

isomeerejä yhteensä yhdeksän kappaletta.

Lineaarialgebra ja matriisilaskenta II. LM2, Kesä /141

Miten osoitetaan joukot samoiksi?

(p j b (i, j) + p i b (j, i)) (p j b (i, j) + p i (1 b (i, j)) p i. tähän. Palaamme sanakirjaongelmaan vielä tasoitetun analyysin yhteydessä.

Algoritmit 2. Demot Timo Männikkö

Datatähti 2019 loppu

Transkriptio:

12. Algoritminsuunnittelun perusmenetelmiä Ei vain toteuteta tietorakenteita algoritmeilla, vaan myös tietorakenteita käytetään tyypillisesti erilaisten algoritmien yhteydessä. Kun nämä tietojenkäsittelytieteen laajat käsitealat ovat olennaisesti tekemisissä keskenään, on luonnollista tässäkin yhteydessä perehtyä hieman algoritmien tehokkaaseen suunnitteluun. Edellä käytiin läpi melko eriytyneitä menetelmiä merkkijonojen käsittelyä varten. Seuraavassa sen sijaan pohditaan sangen yleisiä algoritmin kehittämisen perusperiaatteita. Ne ovat hajota ja hallitse, dynaaminen ohjelmointi ja ahne menetelmä, joita on aiemmin käsitelty toista lukuunottamatta. Hajota ja hallitse menetelmää käsiteltiin lomituslajittelun, pikalajittelun ja pikavalinnan yhteydessä luvussa 8. Ahne menetelmä esiteltiin painotetuilla graafeilla luvussa 10. Nyt kaikkia kolmea perusperiaatetta tarkastellaan lyhyesti yleisessäkin mielessä. 12. luku 607 12.1. Hajota ja hallitse Hajota ja hallitse menetelmä (divide and conquer) on yleinen, rekursiota käyttävä tekniikka, jossa kokonaisuus jaetaan kulloinkin osiin ja nämä ratkaistaan rekursiivisesti. Rekursiosta palattaessa yhdistetään palautettu tulos muihin osiin ja lopulta palataan rekursiosta kokonaan pois, jolloin päädytään lopputulokseen. Hajota ja hallitse menetelmää noudattavan algoritmin suoritusajan analysoimiseksi sovelletaan rekursiorelaatiota eli yhtälöä (recurrence relation). Esitetään suoritusaika ongelman koon n funktiona T(n). Käytetään yhtälöä funktion T arvoihin nähden, kun nämä ovat kooltaan pienempiä ongelmia kuin n. Esim. lomituslajittelun algoritmissa saadaan seuraava rekursioyhtälö symbolin b ollessa vakio ja olettamalla yksinkertaistuksena, että n on 2:sen potenssi. b, n < 2 T ( n) = 2T ( n / 2) + bn, n 2 12. luku 608 Kun oletetaan arvon n olevan em. sopiva arvo, ei tarvitse käyttää sen yhteydessä lattia tai kattofunktiota; oletus ei ole kovinkaan rajoittava, sillä voitaisiinhan esim. lisätä muutama ylimääräinen valealkio käsiteltävään tietoon, jolloin saataisiiin sen kokonaismäärä halutun suuruiseksi. Lomituslajittelun tapauksessa tulee tietysti T(n) kertaluokkaan O(n log n), joka on sen suoritusaika. Tässä pohditaan kuitenkin asiaa yleisellä tasolla. Iteratiivinen korvausmenetelmä Yksi tapa ratkaista hajota ja hallitse menetelmästä juontuva rekursioyhtälö on soveltaa iteratiivista korvausta (iterative substitution). Oletetaan arvon n olevan niin suuri, että voidaan toistuvasti korvata yhtälön oikealla puolella itse rekursioyhtälöä siihen sijoittaen (arvoilla n/2, n/4, n/8 jne.). Esim. lomituslajittelulla korvaaminen tuottaa ensimmäisellä askeleella yhtälön T(n) = 2(2T(n/2 2 ) + b(n/2)) + bn = 2 2 T(n/2 2 ) + 2bn. 12. luku 609 Toisella askeleella eli korvauksella saadaan T(n) = 2 2 (2T(n/2 3 ) + b(n/2 2 )) + 2bn = 2 3 T(n/2 3 ) + 3bn. Jatkamalla näin voidaan todeta yleisen suljetun muodon lomituslajittelulle olevan T(n) = 2 i T(n/2 i ) + ibn. Yleisen muodon tunnistamisen lisäksi olennaista on löytää perustilanne, jossa yhtälön oikealla puolella ei enää esiinny muuttujaa n askelia on tavallaan toistettu tarpeeksi kauan, jotta tähän lopetusiteraatioon on päädytty. Lomituslajittelulla se on T(n) = b, kun n = 2 i eli i = log n, joka osoittaa, että on T(n) = bn + bn log n. 12. luku 610

Tämä merkitsee, että T(n) on yhtä kuin O(bn log n) eli O(n log n). Rekursiopuu Toinen tapa luonnehtia rekursioyhtälöitä on käyttää rekursiopuuta (recursion tree). Iteratiivisen korvauksen tavoin tämä tekniikka käyttää toistettua korvausta ratkaisemaan rekursioyhtälön. Se eroaa kuitenkin visuaalisena lähestymistapana algebrallisesta iteratiivisesta korvauksesta. Piirretään rekursiopuu R, joka edustaa rekursioyhtälön eri korvauksia. Jokaisella solmulla on tähän liittyvä arvo n. Lisäksi solmuun liitetään rekursioyhtälön ei rekursiivinen osa, sillä tämä vastaa hajota ja hallitse menetelmässä osaongelmien lomitukseen tarvittavaa aikaa. Koko rekursioyhtälö ratkaistaan sitten laskemalla yhteen solmuihin liittyvät arvot. Tällöin lasketaan laskemalla arvot yhteen jokaisella tasolla ja lopuksi laskemalla nämä osasummat yhteen, joka on rekursioyhtälön arvo. Esim. 12.1. Olkoon rekursioyhtälö oheinen. b, n < 3 T ( n) = 3T ( n /3) + bn, n 3 Tällainen rekursioyhtälö saataisiin mm. muuttamalla lomituslajittelu sellaiseksi, jossa jaetaan lajittelematon sekvenssi kolmeen yhtä suureen osaan kahden asemesta. Tämän rekursiopuun jokaisella sisäsolmulla on kolme lasta, koska jaettiin kolmeen osaan ja näille suoritetaan rekursiiviset kutsut. Sisäsolmuihin lisätään ei rekursiivisen laskennan osuus, joka sillä hetkellä on arvo bn tasolla yhteensä. Tämä vastaa lajiteltujen lomituksen vaatimaa aikaa. Kun puun korkeus on log 3 n, niin funktion T(n) ratkaisuksi saadaan lopulta O(n log 3 n). 12. luku 611 12. luku 612 Arvaa ja testaa menetelmä Monesti käyttökelpoinen on myös heurististyylinen arvaa ja testaa menetelmä (guess and test). Siinä tehdään ensiksi älykäs arvaus rekursioyhtälön suljetun muodon ratkaisuksi ja sitten osoitetaan arvaus oikeaksi tavallisesti induktiolla. Arvaus löydetään intuitiivisesti kokemuksen perusteella samankaltaisista tapauksista. Muistetaan etsittävän ylärajaa ordon eli O:n laskennassa. Niinpä voidaan arvata eräänlaisen binäärihaun avulla, jossa esitetään arvaus ja jos tämä ei tuota haluttua tulosta, esitetään edeltävää nopeammin kasvava funktio. Jos tämä on liian suuri, otetaan em. kahden välistä ehdokas jne. Tekniikka on usein tuloksellinen, koska rekursioyhtälöihin liittyvä aritmetiikka on melko yksinkertaista. Esimerkki valaissee menetelmää. Esim. 12.2. Olkoon rekursioyhtälö T(n) = 2T(n/2) + bn log n, kun perustapauksena on T(n) = b, missä n < 2. Tämä muistuttaa lomituslajittelun rekursioyhtälöä. Saatetaan näin ollen tehdä arvaus (huom. yläraja) T(n) cn log n jollekin vakiolle c 1. Oletetaan nyt, että n 2. Otetaan arvaus induktiooletukseksi, joka on tosi silloin, kun syötteen koko on pienempi kuin n. Silloin saadaan seuraava johto. 12. luku 613 12. luku 614

Kun edellytetään, että on c b, saadaan seuraava johto. T(n) = 2T(n/2) + bn log n 2(c(n/2) log (n/2)) + bn log n = cn(log n log 2) + bn log n = cn log n cn log 2 + bn log n. Tästä ei kuitenkaan saada millään tulosta, joka olisi pienempi tai yhtä suuri kuin cn log n jollekin n 2. Täten ensimmäinen arvaus ei ollut kelvollinen. Yritetään nopeammin kasvavaa arvausta T(n) cn log 2 n T(n) = 2T(n/2) + bn log n 2(c(n/2) log 2 (n/2)) + bn log n = cn(log 2 n 2 log n + 1) + bn log n = cn log 2 n 2cn log n + cn + bn log n cn log 2 n On osoitettu, että T(n) on yhtä kuin O(n log 2 n). Hallintamenetelmä jollekin vakiolle c 1. Jälleen lähdetään liikkeelle oletuksella n 2. Otetaan arvaus induktio oletukseksi, joka on tosi, kun syöte on pienempi kuin n. 12. luku 615 Edellä esitetyt lähestymistavat on luonteeltaan erityismenetelmiä (ad hoc). Hallintamenetelmä (master method) on sitä vastoin yleinen hajota ja hallitse suunnittelumenetelmällä tuotettuja algoritmien käsittelyä varten. Siinä on ohje erilaisten rekursioyhtälöiden asymptoottisen käyttäytymisen määräämiseksi. Sitä käytetään seuraavalle rekursioyhtälölle. 12. luku 616 c, n < d T ( n) = at ( n / b) + f ( n), n d Tässä edellytetään, että c 1 ja d 1 ovat kokonaislukuvakioita, a 1 ja b > 1 reaalilukuvakioita sekä f(n) on reaalifunktio, joka on positivinen arvoille n d. Tällainen voi esiintyä hajota ja hallitse menetelmää käytettäessä, kun jaetaan ongelma a osaongelmaan, kooltaan n/b, ja käsitellään nämä rekursiivisesti sekä lopuksi lomitetaan osaongelmien tulokset koko ongelman tulokseksi. Kun kaikki aiemmin kuvatut hajota ja hallitse menetelmää soveltavat tapaukset noudattavat tätä kaavaa, kyseessä on yleinen menettely. Ilman johtamisia esitetään seuraavat tulokset, jotka eroavat toisistaan siinä miten funktiota f(n) verrataan erikoisfunktioon n log b a. 1. Jos on pieni vakio ε > 0 ja f(n) on O(n log b a ε ), niin T(n) on Θ(n log b a ). 12. luku 617 2. Jos on vakio k 0 ja f(n) on Θ(n log b a log k n) niin T(n) on Θ(n log b a log k +1 n). 3. Jos on pienet vakiot ε > 0 ja δ < 1, f(n) on Ω(n log b a+ε ) sekä af(n/b) δf(n), n d, niin T(n) on Θ(f(n)). Ensimmäinen tapaus tarkoittaa tilannetta, jossa f(n) on polynomiaalisesti pienempi kuin erikoisfunkio n log b a. Toinen tapaus vastaa tilannetta, jossa f(n) on asymptoottisesti lähellä erikoisfunktiota. Kolmas tapaus esittää tilanteen, jossa f(n) on polynomiaalisesti suurempi kuin erikoisfunktio. Esim. 12.3. Tarpeelliset em. ehdot olettaen tarkastellaan rekursioyhtälöä T(n) = 4T(n/2) + n. Tässä tapauksessa on n log b a = n log 2 4 = n 2. Näin ollen kysymys on ensimmäisestä tapauksesta, sillä f(n) on O(n 2 ε ), kun ε = 1. Tällöin T(n) on hallintamenetelmän mukaan Θ(n 2 ). 12. luku 618

Esim. 12.4. Jälleen vakioiden ehdot olettaen olevan voimassa tarkastellaan rekursioyhtälöä T(n) = 2T(n/2) + n log n, joka on ratkaistavissa hallintamenetelmällä. Tällöin on n log b a = n log 2 2 = n. Tässä on toinen tapaus, kun k = 1, sillä f(n) on Θ(n log n). Tällöin T(n) on Θ(n log 2 n). 12.2. Dynaaminen ohjelmointi Dynaaminen ohjelmointi (dynamic programming) on nimestään (historialliset syyt) huolimatta algoritmien suunnittelumenetelmä, joka soveltuu hajota ja hallitse menetelmän tavoin moniin erilaisiin ongelmiin. Se on tehokas. Ongelmat, jotka alunperin näyttävät (esim. täydellisen käsittelyn mielessä) eksponentiaalisilta, voivat olla ratkaistavissa dynaamisella ohjelmoinnilla polynomiaalisessa ajassa. 12. luku 619 Matriisien ketjutulo Ennen yleistä menetelmää tarkastellaan dynaamista ohjelmointia matriisien ketjutulon klassisen ongelman yhteydessä. Lasketaan tässä n kaksiulotteisen matriisin tulo A = A 0 A 1 A 2 A n 1, missä A i on d i d i+1 matriisi ja i = 0, 1, 2,, n 1. Kun lasketaan tavalliseen tapaan matriisitulo d e matriisilla A ja e f matriisilla B, lasketaan tulon (i,j):s alkio seuraavasti: e 1 k= 0 A[ i, k] B[ k, j] Matriisien kertolasku on assosiatiivinen, ts. B(CD) = (BC)D. Niinpä A:n lauseke voidaan ympäröidä sulkein eri tavoin ja silti päätyä samaan lopputulokseen. Sulkeiden eri järjestyksillä ei ole kuitenkaan samaa määrää alkeisoperaatioita (alkioiden kertomisia ja välitulosten yhteenlaskuja), kuten esimerkistä havaitaan. 12. luku 620 Esim. 12.5. Olkoot B 2 10 matriisi, C 10 50 matriisi ja D 50 20 matriisi. Laskien B(CD) tarvitaan 2 10 20 + 10 50 20 = 10 400 kertolaskua, kun taas laskien (BC)D tarvitaan 2 10 50 + 2 50 20 = 3000 kertolaskua. Matriisien ketjutulon (matrix chain product) ongelmana on määrätä sovelias sulkeiden järjestys, joka minimoi tarvittavien skalaarikertomisten määrän. Yksinkertaista olisi luetella kaikki mahdolliset järjestykset, määrätä kertomisten määrät ja valita paras. Valitettavasti ratkaisu ei ole yleensä käyttökelpoinen, sillä eri järjestyksiä on eksponentiaalinen lukumäärä matriisien lukumäärän n suhteen. Täten tämä suorasukainen (raa an voiman) algoritmi ei tule kysymykseen. Suorituskykyä voidaan huomattavasti parantaa yo. lähtökohdasta. Ongelma jaetaan osaongelmiin (subproblems). Nyt määritellään sellaiseksi tilanne, että on laskettava paras sulkeiden järjestys lausekkeelle A i A i+1 A j. Viitatkoon N i,j kertomisten minimimäärään tämän osaongelman tapauksessa. Osaongelmille voidaan sitten esittää optimaalinen ratkaisu. Tätä ominaisuutta kutsutaan osaongelmaoptimaalisuusehdoksi (subproblem optimality condition). Siis lausekkeen A i A i+1 A j sulkeiden järjestyksen on oltava jokin muoto (A i A k )(A k+1 A j ), jossa k {i, i+1,, j 1}. Se on ratkaistava optimaalisesti, sillä jos ei olisi näin, pitäisi olla globaali koko ongelman ratkaisu, jossa tämä ongelma olisi ratkaistu alioptimaalisesti. Tämä on kuitenkin mahdotonta, sillä silloin seuraisi ristiriita, koska tällöin voitaisiin alioptimaalinen osaongelma redusoida optimaaliseksi, mikä redusoisi globaalia optimia. Nyt voidaan laskea seuraava minimointi: N i, j = j+ i k< j min{ Ni, k + Nk + 1, j + didk + 1d 1}, missä N i,i = 0, koska yksittäisen matriisin lausekkeen yhteydessä ei tarvita mitään laskentaa 12. luku 621 12. luku 622

Ei kannata jakaa kokonaisuutta täysin riippumattomiin osaongelmiin. Käytetään tapauksen N i,i yhtälöä ja samoin N i+1,i+1 (laskettu ennen seuraavaksi mainittavaa) tehokkaasti ylhäältä alas menetelmällä tapauksen N i,i+1 laskennassa. Talletetaan vaiheittain välitulokset, joita sitten hyödynnetään jatkossa. Kun on laskettu N i,i+1, pystytään laskemaan N i,i+2 jne. Saadaan näin yleisesti N i,j. Viimein saavutetaan N 0,n 1, joka on etsittävä loppuarvo. Dynaamisen ohjelmoinnin ketjutulon algoritmi on koodina 12.1. Algoritmissa on kolme sisäkkäistä silmukkaa, mistä nähdään seuraavan lauseen väite, kun silmukoita suoritetaan enintään n kertaa. Lause 12.1. Ketjutulon sulkujen optimaalinen järjestys (skalaarikertomisten minimimäärä) n kaksiulotteisella matriisilla on laskettavissa ajassa O(n 3 ). Algorithm Matrixchain(d 0,, d n ): Input: jono d 0,, d n kokonaislukuja Output: minimimäärä kertomisia N i,j, joka tarvitaan matriisitulon laskemista varten for i 0 to n 1 do N i,i 0 for b 1 to n 1 do for i 0 to n b 1 do j i + b N i,j + for k i to j 1 do N i,j min{n i,j, N i,k + N k+1,j + d i d k+1 d j+1 } Koodi 12.1. Dynaamisen ohjelmoinnin algoritmi matriisien ketjutuloa varten. 12. luku 623 12. luku 624 12.3. Ahne menetelmä Yleinen tekniikka Dynaamista ohjelmointia sovelletaan pääasiassa optimointiongelmissa (optimization), joissa halutaan löytää paras tapa tehdä jotakin. Toiminta sisältää kolme osiota: Yksinkertaiset osaongelmat: Kokonaisongelma on jotenkin jaettava osiin. Osaongelmien optimointi: Koko ongelman globaali optimi saadaan yhdistämällä osaongelmien optimaaliset ratkaisut. Ahnetta menetelmää (greedy method) on tarkastelu jo graafien yhteydessä luvussa 10. Nyt esitetään ahneen valinnan (greedy choice) ominaisuus. Ahneen valinnan ominaisuus Dynaamisen ohjelmoinnin kaltaisesti ahnetta menetelmää sovelletaan optimointiongelmiin. Edetään valintajonossa, joka alkaa jostakin tunnetusta aloitusehdosta ja tekee sitten iteratiivisesti valinnanmahdollisuuksien joukosta parhaalta näyttävän päätöksen. Tämä ei aina johda koko ongelman kannalta optimaaliseen ratkaisuun, mutta tavallisesti ainakin melkein parhaaseen. Monien ongelmien kohdalla tämä toimii optimaalisesti, ja sillloin puhutaan ahneen valinnan ominaisuudesta, missä globaali optimi saadaan valitsemalla toistuvasti lokaaleja optimeja. Osaongelmien päällekkäisyys: Toisistaan riippumattomien osaongelmien ratkaisut voivat sisältää puolestaan yhteisiä osia, mitä voidaan hyödyntää. 12. luku 625 12. luku 626

Käsitellään lopuksi merkkijonon tiivistyksessä (pakkauksessa) käytettävää ahnetta menetelmää. Tässä on jonkin aakkoston mukaan luotuja merkkijonoja X = x 1 x 2... x n. Halutaan koodata X tehokkaasti lyhyiksi bittijonoiksi Y. Sovelletaan Huffman koodia (Huffman code). Tavanomaiset koodit käyttävät vakiopituisia bittijonoja merkkien koodauksessa, esim. ASCII 7 bittiä ja Unicode 16 bittiä. Huffman koodissa on sitä vastoin vaihtelevanmittaisia bittijonoja. Optimointi perustuu merkkien esiintymisfrekvensseihin, missä on jokaisella merkillä c frekvenssi f(c) sen mukaan, miten usein c esiintyy merkkijonossa X. Huffman koodi säästää tilaa vakiomittaisiin verrattuna käyttäessään usein esiintyville merkeille lyhyitä koodeja ja harvinaisille edellisiä pidempiä koodeja. Merkkijonon X koodaamiseksi muutetaan sen jokainen merkki vakiomittaisesta vaihtelevanmittaiseksi ja katenoidaan nämä peräkkäin Y:ksi. Lisäksi on talletettava koodit taulukkoon, jotta koodatun merkkijonon purkaminen on mahdollista. Jotta koodaamisessa säilytetään koodien yksikäsitteisyys, vaaditaan, ettei mikään koodi saa olla toisen etuliite (prefix) (kuva 12.1.). rajoituksesta huolimatta säästöt vakiomittaiseen koodaukseen voivat olla huomattavat. 12. luku 627 Kuvassa 12.1. konstruoitu puu käsittää koodit kaikille tekstissä olleille merkeille. Puun jokainen vasen kaari vastaa bittiä 0 ja jokainen oikea bittiä 1 koodauksesssa. Esim. merkki s on nyt koodattu arvoksi 111011, joka on pidempi kuin useammin esiintyvän merkin r koodi 011. Huomattakoon myös etuliiteominaisuuden paikkansapitävyys. Lehdissä ovat merkit frekvensseineen. Sisäsolmuissa arvoina ovat lasten frekvenssiarvojen summat. Koodiesitys on eräin rajoituksin optimaalinen, ts. keskimäärin ottaen tämän lyhyempiä koodeja ei kyseisillä frekvenseillä näille merkeille voi saada. Algoritmin pseudokoodiesitys on koodina 12.2. Siitä todetaan lyhyesti, että optimaalinen etuliitekoodi on laskettavissa n merkin merkkijonolle ajassa O(n log n). 9 19 46 merkki frekvenssi a b d e f h i k n o r s t u v 9 5 1 3 7 3 1 1 1 4 1 5 1 2 1 1 27 10 12 15 a r e 5 5 7 d 3 5 b 2 h 1 1 (a) f 7 8 n 4 4 3 4 2 2 t 2 i k o s 1 1 1 1 1 1 (b) Kuva 12.1. Esimerkki Huffman koodista, kun X = a fast runner need never be afraid of the dark : (a) merkkien frekvenssit ja (b) X:n Huffman puu, josta koodit saadaan merkitsemällä kulku vasemmalle 0:ksi ja oikealle 1:ksi. Esim. merkin a koodi on 010. 12. luku 628 Algorithm Huffman(X): Input: n merkin merkkijono X Output: X:n koodipuu lasketaan X:n jokaisen merkin c frekvenssi f(c) alustetaan prioriteettijono Q for X:n jokaiselle merkille do luodaan yhden solmun puu T, jossa on c lisätään T prioriteettijonoon Q avaimella f(c) while Q.size()>1 do f 1 Q.minKey() T 1 Q.removeMinElement() f 2 Q.minKey() T 2 Q.removeMinElement() luodaan uusi puu T, jossa on T 1 vasempana alipuuna ja T 2 oikeana alipuuna lisätään T prioriteettijonoon Q avaimella f 1 + f 2 return puu Q.removeMinElement() Koodi 12.2. Huffman algoritmi. 2 u v 12. luku 629 12. luku 630