1 / 143

Kurssin sisältö

Tietorakenteet ja algoritmit I Turun yliopisto, Informaatioteknologian laitos, periodi 2 / 2012 Lasse Bergroth. Kurssin sisältö. Kurssi perustuu oppikirjaan: Cormen, T. H. – Leiserson, C. E – Rivest, R. L – Stein, C.: ”Introduction to Algorithms” , 3. painos, MIT press (2009)

ata
Download Presentation

Kurssin sisältö

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Tietorakenteet ja algoritmit ITurun yliopisto, Informaatioteknologian laitos, periodi 2 / 2012Lasse Bergroth

  2. Kurssin sisältö • Kurssi perustuu oppikirjaan: Cormen, T. H. – Leiserson, C. E – Rivest, R. L – Stein, C.:”Introduction to Algorithms”, 3. painos, MIT press (2009) • Myös vanhemmat painokset (erityisesti 2. painos) oppikirjasta kelpaavat • Samaisesta oppikirjasta luennoidaan myös kurssi: • Tietorakenteet ja algoritmit II (aineopintotasoinen, keväällä 2013 periodilla 3)– jatkoa tälle kurssille • Kirjaa on aikaisemmin käytetty oppikirjana myös kurssilla: • Algoritmien suunnittelu ja analysointi (syventävä kurssi, syksyllä 2013 periodeilla 1 – 2) – luennoitsijana professori Olli Nevalainen • Monet kurssilla käsiteltävistä asioista löytyvät edelleen tästä kirjasta.

  3. Kurssin sisältö (jatkoa) Sisällysluettelo • Luentokalvoissa on käytetty samaa osa- ja lukunumerointia kuin oppikirjassa I Perusteet • Algoritmien asema tietojenkäsittelyssä - mitä algoritmiikka tutkii? • Algoritmiikan perusteet - perusmenetelmät algoritmien analyysiä varten • Funktioiden kasvunopeus - algoritmien suorituskyvyn mittaaminen • ”Hajota ja hallitse” -menettely - tehtävän ratkaiseminen osittamalla se alkuperäisen kaltaisiin mutta pienempiin osaongelmiin II Lajittelualgoritmit • Kekolajittelu- lajittelun suorittaminen käyttämällä aputietorakenteena kekoa • Pikalajittelu- käytännössä tehokkaaksi osoittautunut rekursiivinen yleinen lajittelumenetelmä • Lajittelu lineaarisessa ajassa- lajittelun nopeuttaminen käyttämällä hyväksi ennakkotietoja syötteen ominaisuuksista • Mediaanit ja järjestysstatistiikat - lajittelua helpomman tehtävän – syötteen i. suurimman alkion etsiminen

  4. Kurssin sisältö (jatkoa) III Tietorakenteet • Perustietorakenteet - jono, pino ja lista sekä vaihtoehtoja niiden toteuttamiseksi • Hajautustaulut - esitellään tekniikoita hajautustaulun toteuttamiseksi ja -funktion valitsemiseksi • Binääriset hakupuut - toteutus, eri selausjärjestykset - luennoidaan vasta kurssilla TRAK II, mutta kalvot esitetään TRAKLA-demonstraatioita varten. I Perusteet 1. Algoritmien asema tietojenkäsittelyssä • Algoritmilla tarkoitetaan hyvin määriteltyä toimintosarjaa, joka muuntaa syötteen (lähtötiedot) tulosteeksi. • Algoritmi ratkaisee jonkin reaalimaailman ongelman, ja ratkaisijana – eli algoritmin suorittajana – voi toimia joko ihminen tai (tieto)kone. Ongelma Algoritmi Syötteet Suorittaja Tuloste

  5. 1. Algoritmien asema tietojenkäsittelyssä • Algoritmeilta vaaditaan (yleensä) seuraavien kriteerien täyttämistä: 1) Yleisyyskriteeri: algoritmia on pystyttävä soveltamaan kaikille niille syötteille, jotka täyttävät sille asetettavan alkuehdon • 2) Deterministisyyskriteeri: kaikissa laskennan vaiheissa pitää olla yksikäsitteisesti tiedossa, miten suoritusta jatketaan tästä kriteeristä joustetaan sovellettaessa ns. vapaajärjesteisiä eli ei-imperatiivisia algoritmeja (funktionaalinen ja logiikkaohjelmointi) 3) Tuloksellisuuskriteeri: algoritmin on aina palautettava tulos, joka a) on oikeellinen jab) saavutetaan äärellisen ajan kuluessa tästä kriteeristä voidaan joustaa käytettäessä esimerkiksi heuristisia, likimääräis- tai todennäköisyysalgoritmeja, sillä toisinaan • ei-optimaalinen, epätarkka tai jopa tietyllä riskillä virheellinenkin lopputulos saattaa kaikesta huolimatta olla tyhjää parempi

  6. 1. Algoritmien asema tietojenkäsittelyssä • Algoritmiikka tutkii, miten tietokone saadaan yksinkertaisia käskyjä peräkkäin suorittamalla ratkaisemaan tehokkaasti reaalimaailman ongelmia. • Tietämys algoritmiikasta helpottaa hyödyntämään aikaisemmin löydettyjä ratkaisuja jonkin tehtävän ratkaisemiseksi. samankaltaiset alitehtävät, vaikkapa lajittelu, esiintyvät lukuisan eri ongelman ratkaisun vaiheina kaikkea ei siis välttämättä tarvitse ratkaista aina itse alkutekijöistä lähtien! • Vaikka valmiina saatavia algoritmeja on saatavilla paljon ja jopa valmiiksi ohjelmoituinakin, ei tämä kuitenkaan tee algoritmiikan osaamista vähemmän merkittäväksi tai suorastaan tarpeettomaksi, sillä  sopivan algoritmin valitseminen useasta ehdokkaasta pelkästään ”arpomalla” voi johtaa huonolla tuurilla varsin tehottomaan lopputulokseen  ohjelmoijan pitää pystyä ymmärtämään, miksi jokin tietty algoritmi sopii hänen tarkoituksiinsa paremmin kuin jokin toinen saman tehtävän ratkaiseva • Lisäksi kannattaa huomioida, että algoritmit ovat ohjelmointikielistä ja pienin varauksin myös tietokoneista riippumattomia  vaikka tietokoneet tehostuvat ja ohjelmointiympäristöt kehittyvät tämän tästä, algoritmit eivät kuitenkaan jää tämän kehityksen jalkoihin, vaan niiden käyttökelpoisuutta kuvaavat ominaisuudet säilyvät muutosten yli •  algoritmiikan opiskelulla saavutetut tiedot eivät siis käy vanhanaikaisiksi!

  7. 1. Algoritmien asema tietojenkäsittelyssä • Suomennettu sitaatti D. Harelin kirjasta The spirit of Computing (1992): • ”Algoritmiikka on enemmän kuin vain yksi tietojenkäsittelytieteen osa-alue. Se on tietojenkäsittelytieteen ydin, ja sen voidaan rehellisesti sanoa olevan relevantti useimpien tieteiden, liiketoiminnan ja teknologian kanssa.” • Tämän kurssin tärkeimpinä tavoitteina ovat siten: • Esitellä vuosien mittaan hyviksi osoittautuneita ideoita ja menetelmiä tiettyjen, käytännössä usein esiintyvien tehtävien ratkaisemisessa. • Antaa opiskelijalle riittävät perustiedot, mitä tarvitaan algoritmien tehokkuuden analysoimiseksi, jotta hän pystyisi punnitsemaan tarkastelemansa menetelmän hyvyyttä ja soveltuvuutta jonkin tietyn (ali)ongelman ratkaisemiseksi. • Tutustutaan seuraavaksi kahden vaihtoehtoisen menetelmän ominaisuuksiin arvon etsimiseksi järjestetystä vektorista:Syöte: yhteensä n alkiota (n > 0) sisältävä järjestetty vektori A, joka on indeksoitu välille [1..n] sekä etsittävä arvo x • Tuloste: alkion x sijaintipaikka (indeksi) vektorissa A. Ellei x:ää esiinny kyseisessä vektorissa, palautetaan arvo 0 merkkinä epäonnistuneesta hausta.Oletus: jos x esiintyy vektorissa Auseita kertoja peräkkäin (x:llä on duplikaatteja), voidaan palauttaa mikä tahansa x:n esiintymiskohdista

  8. 1. Algoritmien asema tietojenkäsittelyssä • Esimerkkisyöte: 11-paikkainen kokonaislukuvektori A, josta etsitään alkiota 16 • 1 2 3 4 5 6 7 8 9 10 11 • A = • Ratkaistaan tehtävä ensiksi käyttämällä ns. lineaarihakua:Lineaarihaku(A, n, x): 1. Selaa vektorin A alkioita vuoron perään aloittamalla paikasta 1. Merkitään kulloistakin tarkastelukohtaa muuttujalla i. 2. Jos A[i] = x jollain i:n arvolla, palauta kutsujalle arvo i ja poistu. 3. Jos yhtään alkiota ei ole enää tutkimatta, eli kaikilla i:n arvoilla 1, 2, …, nA[i] oli erisuuri kuin x, palauta arvo 0 ja poistu. • Esimerkissämme vektoriin joudutaan vektoriin kohdistamaan 7 hakua, kunnes etsitty alkio 16 löytyy.  alkiota haetaan järjestyksessä paikoista 1, 2, 3, 4, 5, 6 ja 7. • Analyysi:Parhaassa tapauksessa joudutaan tutkimaan ainoastaan yksi alkio, A[1], jos sieltä löytyy heti etsitty x.Pahimmassa tapauksessax esiintyy A:ssa ensi kerran vasta indeksissä n, tai sitten x:ää ei löydy lainkaan vektorista A. kaikki alkiot eli n kappaletta joudutaan tutkimaan.

  9. 1. Algoritmien asema tietojenkäsittelyssä • 1 2 3 4 5 6 7 8 9 10 11 A = • … ja sitten käyttämällä puolitushakua:Puolitushaku(A, n, x): 1. alaraja := 1; yläraja := n; • 2. keskikohta := A[(alaraja + yläraja)/2]; 3. Tutki paikassa A[keskikohta] oleva alkio. 4. Jos x = A[keskikohta], lopeta haku ja palauta indeksi keskikohta 5. Jos x < A[keskikohta] yläraja := keskikohta – 1 (* x ei keskikohdasta oikealle *) muutoin alaraja := keskikohta + 1 (* x ei keskikohdasta vasemmalle *) 6. Jos alaraja yläraja palaa takaisin riville 2 /* Vielä mahdollisia paikkoja x:lle */ muutoin lopeta haku ja palauta indeksi 0 /* Haku epäonnistui. */ • Esimerkissämme alkiota 16 etsittäisiin järjestyksessä paikoista 6, 9, 8 ja 7  tarvitaan ainoastaan 4 hakua • Analyysi:Parhaassa tapauksessa joudutaan nytkin tutkimaan ainoastaan yksi alkio, A[keskikohta], jos se on etsityn x:n sijaintipaikka.Pahimmassa tapauksessax löytyy A:sta ensi kerran vasta silloin, kun hakualue on puristunut yhden pituiseksi (alaraja = yläraja), tai sitten x:ää ei löydy lainkaan vektorista A. alkioita x joudutaan etsimään tarkalleen niin kauan kuin rivin 6 ehto toteutuu. Tämä on mahdollista enintään log2n kappaletta. • Esimerkkitapauksemme oli siten pahin tapaus (!) tarkastellulle syöteaineistolle. • MUTTA: puolitushausta ei ole iloa, jos syöte ei ole lajiteltu!

  10. 1. Algoritmien asema tietojenkäsittelyssä • Algoritmin tehokkuutta mitataan useimmiten sillä, miten paljon se vaatii aikaa ja/tai muistitilaa eli resursseja suoritusta varten. • Jotta algoritmin analyysi voidaan pitää yksinkertaisena, sovitaan, että jokainen suoritettava perusoperaatio vie aikaa yhden aikayksikön. • Algoritmin resurssivaativuus eli kompleksisuus ilmoitetaan yleensä ainoastaan kuvaamalla sen suuruus- eli kertaluokka ilman absoluuttisia mittauksia, jotka tietystikin vaihtelevat koneittain. Tähän tarkoitukseen käytetään joko - tai -notaatiota (theta / (iso) ordo). • Lineaarihaun aikavaativuus on kertaluokkaa (n). • Puolitushaulla se on sen sijaan kertaluokkaa (log2n). • Seuraava taulukko esittää funktioiden y = log2x ja lg x arvon riippuvuutta x:stä (Huom! Tällä kurssilla log x = log10x ja lg x = log2x): • Taulukosta voi selvästi havaita, että logaritmit reagoivat hyvin hitaasti syötteen koon n kasvuun.  mitä pidempi järjestetty vektori, sitä halvemmaksi puolitushaku pitkän päälle tulee! • Tosin myös järjestetyn vektorin lineaarihakua voidaan tehostaa, mutta pahin tapaus silti (n).

  11. 1. Algoritmien asema tietojenkäsittelyssä • Edellä tutustuttiin kahteen vaihtoehtoiseen hakumenetelmään. Nyt vertaillaan puolestaan kahta yleiskäyttöistä lajittelumenetelmää. • Seuraavassa oletetaan, että 1) Syötteenä annetaan n (n > 0) alkiota sisältävä vektori, joka sisältää alkiotA[1], A[2], … A[n]  indeksointi aloitetaan ykkösestä 2) Tulosteeksi halutaan täsmälleen syötteen alkuperäiset alkiot, mutta lajiteltuina ei-vähenevään suuruusjärjestykseen, eli lajittelun valmistuttuaA[1]  A[2]  …  A[n]. • Tarkastellaan ensiksi lisäyslajittelua, joka toimii seuraavasti: 1) Kierroslaskuri i saa silmukassa vuoron perään arvot 2, 3, …, n. Kierroksen i alkaessa A[1..i–1] on jo järjestetty. 2) Kopioidaan alkio A[i] muuttujaan j.Oletetaan, että siitä tulee järjestetyn osan k. pienin alkio. 3) Viedään j oikealle paikalleen. Kuitenkin ennen asetusta A[k] := j joudutaan kaikki alkiot väliltä A[k..i–1] siirtämään vähenevässä indeksijärjestyksessä yhdellä positiolla oikealle päin, ettei kyseisellä indeksialueella olevia arvoa menetetä kirjoittamalla niiden päälle. 4) Vaiheita 1–3 toistetaan, kunnes viimeinenkin alkio on viety oikealle paikalleen.  kun i kasvaa arvoon n + 1, on vektori valmiiksi lajiteltu.

  12. 1. Algoritmien asema tietojenkäsittelyssä • Selvitetään luennolla lisäyslajittelun etenemistä numeerisella esimerkillä, jossa edeltäneiden lineaari- ja puolitushakuesimerkkien vektorin A alkiot on sekoitettu nyt mielivaltaiseen järjestykseen. Alempana nähtävissä silmukan kierrokset i:n arvoilla 2–7. • 1 2 3 4 5 6 7 8 9 10 11A = i = 2i = 3 • i = 4 i = 5 i = 6i = 7

  13. 1. Algoritmien asema tietojenkäsittelyssä • Seuraavassa lisäyslajittelun eteneminen pääsilmukan viimeisillä kierroksilla (i:n arvot 8–11): • 1 2 3 4 5 6 7 8 9 10 11i = 8i = 9 • i = 10 • i = 11 •  i = 12, algoritmin suoritus päättyy, ja lopputuloksena saadaan A lajiteltuna: • Esimerkissä järjestetyt alkiot on värjätty punaisella, tutkittava alkio keltaisella ja vielä järjestämättömät valkoisella. Sininen nuoli kuvaa tutkittavan alkion siirtoa ja punainen nuoli alkupään alkioiden siirtoa yhdellä eteenpäin oikeasta reunasta aloittaen. • Havaitaan, että järjestetyn osan pidetessä vielä järjestämättömien, mutta alkuosaan kuuluvien siirtomatkat ja siirroista aiheutuva järjestetyn osan kopiointityö alkavat lisääntyä melkoisesti!

  14. 1. Algoritmien asema tietojenkäsittelyssä • Tarkastellaan seuraavaksi vaihtoehtoista lajittelualgoritmia, limityslajittelua, joka toimii seuraavasti (jos alla olevassa algoritmissa alkuloppu, ei tehdä mitään):Syöte: Vektorin AosavektorinA[alku], …, A[loppu] alkiot. Aluksi alku = 1 ja loppu = n. • JOS alku < loppu /* Vieläkö tarkasteltava osavektori voidaan osittaa (likimain) kahtia? */1) Laske taulukon keskikohta, joka on (alku + loppu)/2. 2) Lajittele rekursiivisesti alkuosa eli alkiot A[alku], … A[keskikohta]. 3) Lajittele rekursiivisesti loppuosa eli alkiot A[keskikohta + 1], … A[loppu]. 4) Limitä rekursiivisesti lajitellut vektorinpuoliskot lajitelluksi osavektoriksi. • Katsotaan seuraavaksi, miten limityslajittelu etenee samalle syöteaineistolle kuin edellä lisäyslajittelulla käsitellylle: • 1) Alkutilanne: 1 2 3 4 5 6 7 8 9 10 11  keskikohta (1 + 11) / 2 = 6  halkaisu osavektoreiksi A[1..6] ja A[7..11] 2) Tilanne 1. tason halkaisun jälkeen: • 1 2 3 4 5 6 7 8 9 10 11

  15. 1. Algoritmien asema tietojenkäsittelyssä •  keskikohdat = (1 + 6) / 2 = 3 ja (7 + 11) / 2 = 9   halkaisu osavektoreiksi A[1..3], A[4..5], A[7..9] ja A[10..11] • 3) Tilanne 2. tason halkaisujen jälkeen: 1 2 3 4 5 6 7 8 9 10 11  saadaan neljä osavektoria A[1..3], A[4..6], A[7..9] ja A[10..11], jotka halkaistaan edelleen paikoista 2, 5, 8 ja 10.  halkaisu kahdeksaksi osavektoriksi A[1..2], A[3..3], A[4..5], A[6..6], A[7..8], A[9..9], A[10..10] ja A[11..11] • 4) Tilanne 3. tason halkaisujen jälkeen: • 1 2 3 4 5 6 7 8 9 10 11 •  ainoastaan osavektoreiden 1, 3, ja 5 ositusta voidaan jatkaa  muut eli jo yhden mittaisiksi puristuneet osavektorit jäävät toistaiseksi ”lepäämään” ja odottamaan, että vielä halkaisemattomat osavektorit on käsitelty loppuun asti

  16. 1. Algoritmien asema tietojenkäsittelyssä 5) Tilanne 4. tason halkaisujen jälkeen: (vain halkaisuun osallistuneet osavektorit esitetty) 1 2 3 4 5 6 7 8 9 10 11 (15) (1) (25) (5) (8) Nyt on halkaisut suoritettu loppuun asti, joten rekursio alkaa palautua.  lähdetään kokoamaan ratkaisua limittämällä kullakin tasolla olevat osavektorit pareittain ei-vähenevään suuruusjärjestykseen  limityksessä tulokseen viedään aina tarkasteltavien osavektoreiden pienin alkio tarjolla olevista  parin muodostavat aina ne kaksi ositetta, jotka ovat muodostuneet rekursiossa yhden ja saman aktivaation aikana (algoritmin kohdat 2 ja 3)  tasolla 4 järjestetään siis pareittain osavektorit 1 – 2, 4 – 5 ja 7 – 8  saadaan aikaan seuraavat, kahden mittaiset järjestetyt osavektorit (kaikissa kolmessa parissa vaihtuu tällä kertaa järjestys alkuperäisestä):  palataan rekursiossa yksi taso ylöspäin tasolle 3, jossa viisi yhden mittaista osavektoria odottaa limitystä

  17. 1. Algoritmien asema tietojenkäsittelyssä • 6) Tilanne ennen tason 3 limitystä: 1 2 3 4 5 6 7 8 9 10 11 •  limitetään pareittain tason osavektorit 1 – 2, 3 – 4, 5 – 6 sekä 7 – 8  saadaan tulokseksi 4 järjestettyä osavektoria, joiden pituus on 2 tai 3  rekursion taso 3 päättyy, ja siirrytään takaisin tasolle 2 limitysvaiheeseen • 7) Tilanne ennen tason 2 limitystä:1 2 3 4 5 6 7 8 9 10 11  limitetään pareittain tason osavektorit 1 – 2 sekä 3 – 4 •  saadaan tulokseksi 2 järjestettyä osavektoria, joiden pituudet ovat 6 ja 5 •  rekursion taso 2 päättyy, ja palataan ylimmälle rekursiotasolle: alkuperäisen kutsun limityskomentoon

  18. 1. Algoritmien asema tietojenkäsittelyssä • 8) Tilanne ennen viimeistä eli tason 1 limitystä: 1 2 3 4 5 6 7 8 9 10 11 •  limitetään pareittain jäljellä olevat osavektorit 1 – 2  saadaan tulokseksi alkuperäinen vektori järjestettynä ei-vähenevään järjestykseen  koko limityslajittelualgoritmin suoritus päättyy • 9) Lopputilanne:1 2 3 4 5 6 7 8 9 10 11 • Muutamia ensi havaintoja limityslajittelun etenemisestä (tarkempi analyysi myöhemmin): 1) Halkaisuoperaatio on itse asiassa erittäin yksinkertainen: siihen kuuluu ainoastaan uuden osituskohdan määrääminen  mitään muita toimenpiteitä ei tapahdu 2) Kaikki alkiot käydään läpi ainoastaan niin monta kertaa, miten monelle rekursiotasolle ne osallistuvat 3) Syötteen koon kaksinkertaistuminen tuottaa vain yhden rekursiotason lisää!  alkioparien vertailujen määrä kasvaa hitaasti verrattuna lisäyslajitteluun

  19. 1. Algoritmien asema tietojenkäsittelyssä • Seuraavassa esitetään vielä äskeisen limityslajitteluesimerkin kutsupino, jotta nähtäisiin, miten suoritus etenee järjestyksessä (edellä näytettiin, mitä tapahtuu eri tasoilla):1. Limityslajittelu([31, 22, 15, 34, 11, 1, 16, 4, 25, 5, 8]), alku = 1, loppu = 11 • 2. Limityslajittelu([31, 22, 15, 34, 11, 1]), alku = 1, loppu = 6 • 3. Limityslajittelu([31, 22, 15]), alku = 1, loppu = 3 • 4. Limityslajittelu([31, 22]), alku = 1, loppu = 2 • 5. Limityslajittelu([31]) X (alku = loppu = 1: kutsu päättyy) • 6. Limityslajittelu([22]) X (alku = loppu = 2: kutsu päättyy) • Limitä([31], [22])  osavektori A[1..2] := [22, 31] • 7. Limityslajittelu([15]) X (alku = loppu = 3: kutsu päättyy) • Limitä([22, 31], [15])  osavektori A[1..3] := [15, 22, 31] • 8. Limityslajittelu([34, 11, 1]), alku = 4, loppu = 6 • 9. Limityslajittelu([34, 11]), alku = 4, loppu = 5 • 10. Limityslajittelu([34]) X (alku = loppu = 4: kutsu päättyy) • 11. Limityslajittelu([11]) X (alku = loppu = 5: kutsu päättyy) • Limitä([34], [11])  osavektori A[4..5] := [11, 34] • 12. Limityslajittelu([1]) X (alku = loppu = 6: kutsu päättyy) • Limitä([34, 11], [1])  osavektori A[4..6] := [1, 11, 34] • Limitä([15, 22, 31], [1, 11, 34])  osavektori A[1..6] := [1, 11, 15, 22, 31, 34] • 13. Limityslajittelu([16, 4, 25, 5, 8]), alku = 7, loppu = 11) • 14. Limityslajittelu([16, 4, 25]), alku = 7, loppu = 9 • 15. Limityslajittelu([16, 4]), alku = 7, loppu = 8 • 16. Limityslajittelu([16]) X (alku = loppu = 7: kutsu päättyy) • 17. Limityslajittelu([4]) X (alku = loppu = 8: kutsu päättyy) • Limitä([16, 4])  osavektori A[7..8] := [4, 16] • 18. Limityslajittelu([25]) X (alku = loppu = 9: kutsu päättyy) • Limitä([4, 16], [25])  osavektori A[7..9] := [4, 16, 25] • 19. Limityslajittelu([5, 8], alku = 10, loppu = 11 • 20. Limityslajittelu([5]) X (alku = loppu = 10: kutsu päättyy) • 21. Limityslajittelu([8]) X (alku = loppu = 11: kutsu päättyy) • Limitä([5], [8])  osavektori A[10..11] := [5, 8] • Limitä([4, 16, 25], [5, 8])  osavektori A[7..11] := [4, 5, 8, 16, 25] • Limitä[1, 11, 15, 22, 31, 34], [4, 5, 8, 16, 25])  vektori A[1..11] = [1, 4, 5, 8, 11, 15, 16, 22, 25, 31, 34]  tehtävä valmis

  20. 1. Algoritmien asema tietojenkäsittelyssä • Seuraavassa on taulukko, joka esittää muutamien käytännön lajittelumenetelmien toteutuneita suoritusaikoja, kun kone pystyy laskemaan miljardi alkeisoperaatiota sekunnissa • Taulukkoon ei ole merkitty aikakompleksisuuteen vaikuttavia korkeinta termiä alempiasteisia termejä • Tästä syystä esimerkiksi aikavaativuudeltaan samaa kertaluokkaa olevat limitys- ja pikalajittelu eroavat suoritusajoiltaan: pikalajittelu osoittautuu tehokkaammaksi •  selitys: pienemmät kertoimet alempiasteisilla termeillä kuin limityslajittelussa • Taulukosta on helposti pääteltävissä, että jo 50 000:n kokoisella syötteellä lisäyslajittelu on tuntuvasti hitaampi kuin muut menetelmät, mutta syötteen koon ollessa 2 000 000 se on jo kelvoton menetelmä ei ole siis yhdentekevää, mikä lajittelumenetelmä kannattaa valita pitkille syötteille! •  lyhyillä syötteillä menetelmän valinnalla on vähemmän merkitystä.

  21. 1. Algoritmien asema tietojenkäsittelyssä • Algoritmin suoritusaikaa kuvaava lauseke antaa verrattain hyvän ennakkokäsityksen siitä, miten tehokkaan tarkasteltavan algoritmin voi olettaa olevan sovellettavaksi. käytännön tehokkuus tosin paljastuu parhaiten lopulta vasta suorittamalla algoritmia – isot vakio- ja alempiasteisten termien kertoimet voivat tuntua käytännön suoritusajassa – mutta huono teoreettinen suoritusaika ei lupaa hyvää käytännön kannalta • Kaikki taulukossa esitetyt lajittelumenetelmät ovat aikavaativuudeltaan polynomiaalisia, mikä tarkoittaa sitä, että suoritusajan korkeinta astetta oleva termi on enintään jokin syötteen koon potenssi k, eli suoritusaika on tällöin (nk), missä k on vakio. • On kuitenkin olemassa myös sellaisia ongelmia, joita ei ole mahdollista ratkaista polynomiaalisessa ajassa, vaan niiden ratkaisemiseen kuluva aika on eksponentiaalinen. Tällöin syötteen kokoa kuvaava muuttuja n esiintyy suoritusaikalausekkeen eksponentissa, eli lauseke on muotoa T(n) = kn, missä k > 1, n > 0 (merkintä T(n) tarkoittaa kooltaan n olevan syötteen ratkaisemiseksi tarvittavaa aikaa). muun muassa Hanoin tornit on esimerkki algoritmista, jonka aikakompleksisuus on eksponentiaalinen, ja sitä kuvaa lauseke T(n) = 2n. mikäli oletetaan, että n:n kokoinen tehtävä vaatii tasan 2n alkeisoperaatiota, ja kone laskee miljardi (1 000 000 000) alkeisoperaatiota sekunnissa voidaan todeta, että • n = 20  T(20) = 220 = 1 048 576 alkeisoperaatiota  aikaa tarvitaan 0.001 s • n = 40  T(40) = 240 = 1 099 511 627 776 alkeisoperaatiota  aikaa tarvitaan  1099.5 s  18 min 20 s • n = 60  T(60) = 260 = 1 152 921 504 606 846 976 alkeisoperaatiota  aikaa tarvitaan  1 152 921 504.6 s  36 v 7 kk • Koneen tehon kymmenkertaistuminenkaan ei juuri lohduta, jos n = 60 ja T(n) = 2n … !

  22. 1. Algoritmien asema tietojenkäsittelyssä • Eksponentiaalisia ongelmia voidaan pitää käytännössä kelvottomina. • Tähän ryhmään kuuluvat lisäksi muun muassa seuraavat ongelmat: • Graafin väritys: voidaanko graafi värittää k värillä siten, että sen jokaisesta pisteestä lähtevät viivat ovat erivärisiä? • Hamiltonin sykli: löytyykö graafista polkua siten, että sitä pitkin kuljettaessa vieraillaan graafin jokaisessa pisteessä tarkalleen kerran ja viimeisestä pisteestä palataan vielä takaisin lähtöpaikkaan? • Kauppamatkustajan ongelma: löytyykö graafista Hamiltonin sykliä, jonka pituus on enintään ennalta asetetun ylärajan k suuruinen, kun graafin jokaiseen viivaan liittyy sen päätepisteiden etäisyyttä paino?

  23. 1. Algoritmien asema tietojenkäsittelyssä • Algoritmien esittämisestä tällä kurssilla:1) Aluksi esitetään sanallinen kuvaus siitä, että • Millaisesta ongelmasta on kyse • Miten algoritmi etsii ratkaisua tarkasteltavaan ongelmaan • Mitä annetaan syötteeksi ja mitä tulostetaan • Mitä erityisiä merkintöjä käytetään algoritmin kuvaamiseksi • 2) Algoritmista esitetään pseudokielinen ohjelma • Käytetty pseudokoodi muistuttaa melko lailla kursseilla JIT1 ja JIT2 käytettyä • Käskysulkuja, kuten myöskään ehto- ja toistorakenteiden lopetinsanoja ei käytetä, vaan ne on korvattu sisennyksillä (poikkeus: REPEAT-UNTIL), ellei se ole välttämätöntä selvyyden kannalta (esimerkiksi ehdon tai toiston vaikutusalueen ollessa hyvin pitkä) • 3) Esitetään algoritmin toimintaa havainnollistavia esimerkkejä • 4) Todetaan algoritmin oikeellisuus • 5) Analysoidaan algoritmin vaatima suoritusaika

  24. 2. Algoritmiikan perusteet 2.1 Algoritmien analysointi • Algoritmia analysoitaessa pyritään arvioimaan sen resurssien tarvetta. Tarkasteltavana resurssina on useimmiten aika, mutta myös sen muistinkulutusta voidaan mitata. • Näitä harvemmin voi tarkasteltavana resurssina olla myös jokin muu, kuten laitteistovaatimukset (tietoliikenteen tarpeisiin) yms. • Tällä kurssilla keskitytään lähestulkoon ainoastaan algoritmin ajankäytön analysointiin. Tätä merkitään termillä T(n), missä n edustaa syötteen kokoa, joka voi tarkoittaa esimerkiksi 1) käsiteltävien alkioiden lukumäärää (listan, taulukon tai muun tietorakenteen koko) 2) tietokantaan tallennettujen tietueiden lukumäärä (esimerkiksi B-puiden yhteydessä) 3) pisteiden ja kaarten määrää graafialgoritmeissa (molemmat arvot esitetään parina) • Miten arvioida algoritmien suoritusaikaa? 1) lasketaan ohjelman ns. alkeisaskeleiden eli yksinkertaisten operaatioiden kokonaismäärä 2) analyysissä oletetaan, että jokaisen samaa tyyppiä olevan alkeisaskeleen suorituskustannus on vakio (esimerkiksi minkä tahansa alkion välinen vertailu, kahden luvun välinen aritmeettinen operaatio jne.) • 3) useimmiten turvaudutaan yksistään algoritmin pahimman tapauksen analysointiin  ainakaan tätä kauemmin ei algoritmin suoritus voi teoriassa kestää! 4) tällä kurssilla ei kiinnitetä huomiota algoritmien empiiriseen testaamiseen!

  25. 2.1 Algoritmien analysointi • Ennen analysointiin ryhtymistä esitellään vielä muutamia kurssilla käytettävään pseudokoodiin liittyviä oletuksia:1) käskysulkuja eikä ehto- ja toistolauseiden lopettavia sanoja ei käytetä, ellei se ole esityksen selvyyden kannalta välttämätöntä (poikkeus: REPEAT-UNTIL); muutoin käytetään pelkkiä sisennyksiä • 2) kommentti merkitään merkkiparilla /* */, ja se voi jatkua rivinvaihdonkin ylitse 3) taulukoiden indeksointi alkaa aina ykkösestä • 4) kaikki käytetyt muuttujat ovat tällä kurssilla paikallisia. Sen sijaan jatko-osassa käytetään graafialgoritmeissa tapahtuman aikaleiman osoittamiseksi globaalia muuttujaa. • 5) taulukosta A[1..n] voidaan valita osataulukko A[i..j], missä 1  i, j n • 6) muuttujien tunnukset esitetään kursiivilla • 7) FOR-silmukan laskuria saa käyttää silmukan jo päätyttyäkin. Tällöin oletetaan, että laskurin silmukan jälkeinen arvo on sama, jolla toistoa ei enää jatkettu (yhden askeleen verran yli maksimin tai alle minimin) • 8) on käytettävissä funktio pituus(x), joka palauttaa argumenttina annettavan vektorin pituuden, eli vektorin pituustietoa ei tarvitse välttämättä välittää syötteenä

  26. 2.1 Algoritmien analysointi 9) asetusoperaattorina käytetään merkintää ”:=” 10) merkintä nil tarkoittaa tyhjää osoitinta linkitetyissä rakenteissa 11) loogisia lausekkeita lasketaan vasemmalta oikealle ja sen arvon määräämiseksi käytetään ns. oikosulkuevaluaatiota  jos lausekkeen totuusarvo käy jo ilmi, se kiinnitetään heti, kun tämä on mahdollista (vaikkei lauseketta olekaan käsitelty vielä loppuun asti)  esimerkki: IF x nil AND x.arvo > 6 jos x on tyhjä osoitin, ei oikeanpuoleista ehtoa mennä enää testaamaan, joten lauseke ei johda ajonaikaiseen virheeseen  vältytään usein sisäkkäisten ehtolauseiden kirjoittamiselta, jottei määrittelemätöntä arvoa käytäisi testaamassa 12) algoritmeissa esiintyvät pseudokielen varatut sanat (IF, FOR jne.) kirjoitetaan isolla ja ne myös lihavoidaan 13) samoin algoritmien nimet on kirjoitettu isolla 14) jos samalla algoritmin rivillä esiintyy useita käskyjä, niiden erottimena toimii puolipiste ”;” 15) syötteen kokoa (usein sama kuin alkioiden lukumäärä) merkitään tunnuksella n

  27. 2.1 Algoritmien analysointi • Aloitetaan algoritmien analysointi lähtemällä liikkeelle lisäyslajittelusta, jota käsiteltiin jo edellä sanallisen kuvauksen ja esimerkin avulla kalvopaketin sivuilla 11 – 13. • Lisäyslajittelun pseudokoodilistaus on esitetty seuraavassa:LISÄYSLAJITTELU(A)1 FORj := 2, 3, …, pituus(A) DO2 alkio := A[j] /* Otetaan paikassa j oleva alkio talteen käsiteltäväksi. */3 i := j – 14 WHILEi > 0 ANDA[i] > alkioDO5 A[i + 1] := A[i]6 i := i – 1 7 A[i + 1] := alkio • Lisäyslajittelu on esimerkki ns. minimitilassa toimivista lajittelualgoritmeista. Minimitilaisuudella tarkoitetaan, että menetelmä tarvitsee toimiakseen ainoastaan vakiomäärän työmuistia. Toisin sanoen, lajittelun suorittamiseksi tarvittavan lisämuistin määrä ei riipu syötteen koosta n. • Lisäyslajittelualgoritmissa ainoat tarpeelliset apumuuttujat ovat silmukkalaskurit i ja j sekä kullakin ulomman silmukan kierroksella tarkasteltavan alkion kopioimiseen tarvittava apumuuttuja alkio.

  28. 2.1 Algoritmien analysointi • Lähdetään nyt analysoimaan, montako kertaa algoritmin eri rivejä suoritetaan lajittelun ollessa käynnissä. Rivikohtaiset tiedot on koottu seuraavaan taulukkoon. • Taulukon riveillä 4 – 6 esiintyvä termi tj tarkoittaa, montako kertaa algoritmin rivillä 4 esiintyvän WHILE-silmukan alkuehtoa joudutaan testaamaan tietyllä j:n arvolla • Kannattaa huomioida, että rivin 1 FOR-silmukan alkuehtoa testataan yhden kerran enemmän kuin silmukassa on kierroksia. Viimeisellä kerralla ainoastaan todetaan, että laskurin arvo on kasvanut n + 1:een, eli suoritusta ei enää jatketa.

  29. 2.1 Algoritmien analysointi • Nyt pystytään edellisen taulukon avulla laskemaan algoritmin kokonaissuoritusaika summaamalla rivikohtaiset kustannukset: • T(n) = c1n + c2(n – 1) + c3(n – 1) + c4 + c5 + c6 + c7(n – 1) • Algoritmin kokonaissuoritusaika vaihtelee selvästikin sen mukaisesti, mikä tj:n arvoksi kulloinkin määräytyy. • Tutkitaan ensiksi paras tapaus: jos vektori A on jo alun perin järjestettynä ei-vähenevään suuruusjärjestykseen, ei eri kierroksilla käsiteltäviä alkioita eikä niiden edeltäjiä tarvitse siirtää minnekään (A[1]  A[2]  …  A[j – 1]  A[j] = alkio).  tällöin jokaisella ulomman eli FOR-silmukan kierroksella j (2, 3, …, n) WHILE-silmukan aloitusehdon jälkimmäinen osa (A[i] > alkio) on epätosi  silloin tj= 1 jokaiselle j:n arvolle 2, 3, … n. • Koska WHILE-ehtoa testataan aina vain kertaalleen, saadaan • = 1 + 1 + … + 1 = n – 1 (ykkösiä yhteensä n – 1 kappaletta) • Tällöin vastaavasti riveillä 5 ja 6 ei vierailla kertaakaan!

  30. 2.1 Algoritmien analysointi • Siten parhaassa tapauksessa: • T(n) = c1n + c2(n – 1) + c3(n – 1) + c4(n – 1) + c7(n – 1) = (c1 + c2 + c3 + c4 + c7)n – • (c2 + c3 + c4 + c7) = an + b •  edellä kertoimien c1, c2, c3, c4 ja c7 summa on nimetty uudelleen vakiolla a ja vastaavasti summa c2 + c3 + c4 + c7 vakiolla b •  syötteen koko n esiintyy lausekkeessa ainoastaan ensimmäisen asteen termissä •  parhaassa tapauksessa lisäyslajittelun suoritusaika on lineaarinen eli suoraan verrannollinen lajiteltavien alkioiden lukumäärään • Tarkastellaan nyt vastaavasti tilannetta pahimmassa tapauksessa, jolloin lajittelun suorittamiseksi tarvittavan työn määrä maksimoituu:  jos lajiteltava vektori on alun perin aidosti vähenevässä järjestyksessä, joudutaan jokaisella ulomman silmukan kierroksella testaamaan WHILE-silmukan aloitusehtoa j kertaa. Viimeisellä eli j. testauskerralla ehdon alkuosa i > 0 ei enää toteudu. •  tällöin T(n) = c1n + c2(n – 1) + c3(n – 1) + c4 + c5 + c6 + c7(n – 1) • Nyt pitäisi pystyä vielä avaamaan termien 4 – 6 summalausekkeet.

  31. 2.1 Algoritmien analysointi • Aritmeettisen sarjan Sn = summa voidaan määrätä seuraavasti: • Sn = 1 + 2 + 3 + … + n – 1 + n (alusta loppuun päin) • Sn = n + n – 1 + n – 2 + … + 2 + 1(lopusta alkuun päin) • __________________________________________________________________ • 2Sn =(n + 1) + (n + 1) + (n + 1) + … + (n + 1) + (n + 1) (termien 2-kertainen summa) • Koska yhteenlaskettavia on yhteensä n kappaletta, summa 2Sn = n(n + 1) Sn = n(n + 1)/2 • Siten lisäyslajittelun pahimmassa tapauksessa ... • = – 1 = n(n + 1)/2 – 1, ja vastaavasti • = = ½(n – 1)n, joten • … T(n) = c1n + c2(n – 1) + c3(n – 1) + c4(n(n + 1)/2 – 1) + c5n(n – 1)/2 + c6n(n – 1)/2 + c7(n – 1) • = ½(c4 + c5 + c6)n2 + (c1 + c2 + c3 + ½c4 + ½c5 + ½c6 + c7)n – (c2 + c3 + c4 + c7) • = an2 + bn + c, missä a = ½(c4 + c5 + c6), b = (c1 + c2 + c3 + ½c4 + ½c5 + ½c6 + c7) jac = -(c2 + c3 + c4 + c7) •  Lisäyslajittelun pahinta tapausta kuvaava suoritusaika on neliöllinen (syötteen koon 2. potenssi)

  32. 2.1 Algoritmien analysointi • Lisäyslajittelusta tekee aikavaativuudeltaan neliöllisen se, että siihen kuuluu kaksi sisäkkäistä silmukkaa, joiden kummankin suorituskertojen määrä riippuu n:stä teoreettinen silmukoiden sisältämien lauseiden suorituskertojen yläraja olisi n * n, mutta todellisuudessa tätä ei lisäyslajittelussa koskaan saavuteta (luennolla tästä esimerkki) • Silmukoiden sisällä suoritettavat yksittäiset operaatiot ovat kustannukseltaan vakioaikaisia esimerkiksi kahden alkion välinen vertailu, alkion siirto toiseen paikkaan yksittäisen tällaisen operaation kustannus ei ole riippuvainen syötteen koosta • Miksi analysoida juuri pahinta tapausta? Perusteita tälle:1) Saadaan yläraja algoritmin suoritusajalle: suoritus ei varmasti kestä pidempään!2) Pahin tapaus saattaa esiintyä verrattain usein  etsitään esimerkiksi vektorista tai linkitetystä listasta alkiota, jota siellä ei esiinny3) Niin sanotun ”keskimääräisen tapauksen” määritteleminen ei ole välttämättä aivan suoraviivaista, ja vaikka näin olisikin, se saattaa olla vaikeudeltaan samaa kertaluokkaa pahimman tapauksen kanssa!  ajatellaanpa esimerkkinä lisäyslajittelua • Oletetaan, että kierroksella j tutkittava alkio on aina suurempi kuin järjestetyn osan alkupuoliskon alkiot mutta aina pienempi kuin sen loppupuoliskon alkiot. • Tällöin tj  j / 2. Jos tämä sijoitetaan suoritusaikalausekkeeseen, saadaan = ½ = ¼n(n + 1) – ½ = ¼(n2 + n – 2) • Saatu lauseke on yhä edelleen neliöllinen n:n funktio!  keskimääräinen tapaus  pahin tapaus!

  33. 2.2 Algoritmien suunnittelu • Tietyn tehtävän suorittava algoritmi voidaan yleensä suunnitella ja toteuttaa usealla vaihtoehtoisella tavalla. • Yksi mahdollinen tapa algoritmin ratkaisutapa on rekursio. Rekursiivinen algoritmi pyrkii ratkaisemaan alkuperäisen tehtävän kokoamalla ratkaisun samankaltaisten, mutta alkuperäistä pienempien ongelmien osaratkaisuista. Siten tällainen algoritmi kutsuu itseään, kunnes tarkasteltava osaongelma on niin pieni, että se ratkeaa suoraan eli triviaalisti. • Kun algoritmin kontrolli siirtyy rekursiivisen kutsun kohdalle, algoritmista käynnistyy tällöin ns. uusi aktivaatio. Samalla aikaisempi aktivaatio keskeytyy odottamaan sitä, että uusi saadaan vietyä päätökseen.  Yhdestä algoritmista voi olla samanaikaisesti luotuna mielivaltaisen monta aktivaatiota, mutta vain yhtä niistä käsitellään kerrallaan: muut ovat ”lepäämässä” rekursiopinossa. Aktivaatioita käsitellään pinomaisesti, eli mitä aikaisemmin aktivaatio on käynnistynyt, sitä myöhemmin se päättyy (vrt. edellä esitetty limityslajittelun rekursiopino: viimeksi valmistuu alkuperäinen tehtävä). Jokaisella aktivaatiolla on omat paikalliset muuttujansa, vaikkakin ne ovat muodollisesti saman nimisiä toisten aktivaatioiden vastaavien muuttujien kanssa. Siten ei ole pelkoa, että vaikkapa nykyisessä aktivaatiossa tehtävä asetusa := a + b • muuttaisi aikaisemmin käynnistyneissä aktivaatioissa esiintyvän muuttujan a arvoa. • Suoraan eli ilman rekursiivista kutsua ratkeavaa tehtävää kutsutaan rekursion kannaksi tai perustapaukseksi.

  34. 2.2 Algoritmien suunnittelu • Jotta rekursiivinen ratkaiseminen olisi mahdollista, pitää seuraavien kahden ehdon täyttyä:1) Algoritmille on määriteltävä ainakin yksi perustapaus • 2) Joka kerta, kun muodostetaan uusi rekursiivinen kutsu, tehtävän pitää helpottua aikaisemmasta. Tämä tarkoittaa sitä, että kutsun suorittaminen vie lähemmäs jotain tällaista suoraan ratkeavaa tapausta. •  Elleivät molemmat ehdot täyty, rekursio ei milloinkaan pääty muutoin paitsi mahdollisesti tietokoneen ajonaikaisen muistipinon täyttymiseen, jos algoritmi ajautuu aina vain loitommas kaikista perustapauksesta (jokainen uusi rekursiivinen kutsu varaa muistia uusien aktivaatioiden paikallisia muuttujia varten, mikä ei voi jatkua loputtomiin) • Tarkastellaan seuraavaksi ns. hajota ja hallitse -tekniikkaa (lat. divide et impera), joka perustuu rekursioon ja jota käytetään hyväksi muun muassa lajittelualgoritmeissa. • Tekniikan voi todeta sisältävän kolme eri vaihetta: 1) Ellei ongelma ole triviaali, hajota se aliongelmiksi 2) Hallitse ratkaisemalla rekursiivisesti jokainen aliongelma3) Yhdistä aliongelmien ratkaisut alkuperäisen ongelman ratkaisuksi • Yksi tunnetuimmista hajota ja hallitse -algoritmeista on jo edellä alustavasti tarkasteltu limityslajittelu.

  35. 2.2 Algoritmien suunnittelu • Seuraava hahmotelma kuvaa limityslajittelun etenemistä • Hajota • Hallitse • Yhdistä

  36. 2.2 Algoritmien suunnittelu • Limityslajittelussa hajottaminen tapahtuu halkaisemalla tarkasteltava osavektori kahtia, hallitseminen ratkaisemalla syntyneet osaongelmat rekursiivisesti ja yhdistäminen limittämällä osaratkaisujen tulokset yhdeksi pidemmäksi järjestetyksi vektoriksi. • Hajottaminen vaatii pelkän keskikohdan määräämisen ja hallitseminen kaksi rekursiivista kutsua , eli ne ovat erittäin helppoja toimenpiteitä. Limitys vaatii sen sijaan enemmän työtä. Tarkastellaan seuraavaksi limitysalgoritmia. LIMITYS(A, p, q, r) 1 n1 := q – p + 1 2 n2 := r – q 3 Perusta apuvektorit V[1..n1 + 1] ja O[1..n2 + 1] 4 FORi := 1, 2, …, n1DO 5 V[i] := A[p + i – 1] 6 FORj := 1, 2, …, n2DO 7 O[i] := A[q + j] 8 V[n1 + 1] :=  9 O[n2 + 1] := 10 i := 1; j := 1; 11 FORk := p, p + 1, …, rDO12 IFV[i]  O[j]13 THENA[k] := V[i]14 i := i + 115 ELSEA[k] := O[j]16 j := j + 1

  37. 2.2 Algoritmien suunnittelu • Ennen limityslajittelun analyysiä mainittakoon vielä muutama sana algoritmien suoritusaikojen kasvunopeudesta • Lisäyslajittelun pahimman tapauksen suoritusajaksi laskettiin edelläT(n) = an2 + bn + c, missä a, b ja c ovat joitain nollasta eroavia vakioita • Koska jatkossa ollaan kiinnostuneita ainoastaan suoritusajan kasvunopeuden suuruusluokasta, tarkastellaan suoritusaikalausekkeesta ainoastaan korkeinta astetta olevaa termiä. alempiasteisten termien vaikutus heikkenee sitä mukaa, kun n kasvaa sama koskee myös korkeinta astetta olevan termin kerrointa  tarkastellaan myöhemmin asiaa koskevia esimerkkejä • Siten voidaan todeta, että lisäyslajittelun pahimman tapauksen suoritusaika on suuruusluokkaa (n2). • Seuraavassa esitetään limityslajittelualgoritmi kokonaisuudessaan …LIMITYSLAJITTELU(A, p, r)1 IFp < r2 THENq := (p + r)/23 LIMITYSLAJITTELU(A, p, q)4 LIMITYSLAJITTELU(A, q + 1, r)5 LIMITYS(A, p, q, r)

  38. 2.2 Algoritmien suunnittelu • … ja suoritetaan sille seuraavaksi analyysi: • Todetaan aluksi, että yhdistämisvaiheessa käytettävä algoritmi LIMITYS toimii ajassa O(n),sillä • Rivien 1 – 3 ja 8 – 10 suoritus vie vakioajan (pelkkiä asetuslauseita ja muistin varauksia) • Rivien 4 – 7 FOR-silmukoiden suoritus vie ajan O(n1+ n2) = O(n) • Rivien 11 – 16 FOR-silmukka pyörii n kertaa, ja kaikki siinä suoritettavat lauseet ovat vakioaikaisia • Sitten itse pääalgoritmi: • Rivin 1 testi: vakioaikainen eli O(1) • Rivillä 2 tapahtuva halkaisu: samoin vakioaikainen • Rivien 3 ja 4 rekursiiviset kutsut: T(n/2) + T(n/2) = 2T(n/2) • Rivin 5 limitys: O(n) • Siten saadaan:T(n) = (1), kun n = 1 = 2T(n/2) + (n) , kun n > 1 • Kannattaa huomioida, että (n) + (1) = (n). Vastaavasti (n2) + (n) = (n2). • Myöhemmin tullaan näyttämään toteen, että limityslajittelulle T(n) = (n log2n)

  39. 3 Funktioiden kasvunopeus 3.1 Asymptoottinen merkintätapa • Kuten jo edellä lyhyesti mainittiin, algoritmin aikavaativuus kuvataan yleensä esittämällä sen tarkan teoreettisen suoritusajan asemesta vain sen suuruus- eli kertaluokka. tällä tarkoitetaan sitä, miten algoritmin suoritusaika muuttuu suhteessa syötteen kokoon silloin, kun syötteen koon annetaan kasvaa rajatta • Yleisimmin käytetyt kuvaustavat ovat ns. - (theta) ja - (iso ordo) notaatiot • -merkinnällä tarkoitetaan ns. asymptoottista yhdistettyä ylä- ja alarajaa • Mikäli jonkin algoritmin suoritusaikaa kuvaa lauseke T(n) = (n), tarkoittaa se sitä, että kyseisen algoritmin ajankäyttö on kaikilla kelvollisilla syötteillä suoraan verrannollinen syötteen pituuteen n, kunhan n on riittävän iso. • Matemaattisesti -merkintä voidaan ilmaista seuraavasti:  c1, c2  R+ ja  n0  N siten, että(g(n)) = { f(n) | 0  c1g(n)  f(n)  c2g(n) jokaiselle n  n0 } • ”Suomennettuna” edellinen merkintä tarkoittaa, että algoritmin ajankäyttöä ilmaiseva funktio (lauseke) f(n) kuuluu kasvunopeusluokkaan (g(n)) silloin, kun löydetään mielivaltaiset kaksi positiivista reaalilukuvakiota c1 ja c2 sekä jokin syötteen koko n0  0 , josta lähtien lauseke c1g(n) pysyy aina pienempänä tai yhtä suurena kuin f(n), ja tämä puolestaan pysyy aina pienempänä tai yhtä suurena kuin c2g(n). funktion f(n)kuvaaja sijoittuu n:n arvosta n0 lähtien kuvaajien c1g(n) ja c2g(n) väliin sillä, miten f(n) käyttäytyy n0:aa pienemmillä n:n arvoilla, ei ole merkitystä!

  40. 3.1 Asymptoottinen merkintätapa • Esimerkki: Osoitetaan, että ½n2 – 3n = (n2) Nyt pitää pystyä löytämään sellaiset positiiviset reaalilukuvakiot c1 ja c2 sekä syötteen koko n0, josta lähtien on kaikilla n:n arvoilla voimassac1n2 ½n2 – 3n  c2n2 • Tarkastellaan aluksi epäyhtälön oikeaa puolta: ½n2 – 3n  c2n2  On helppo havaita, että jos c2:n paikalle sijoitetaan ½ tai tätä suurempi arvo, epäyhtälö on tosi kaikille ei-negatiivisille n:n arvoille (½n2 – 3n  ½n2). Siten voidaan valita vaikkapa c2 = ½.Sitten epäyhtälön vasen puoli:c1n2  ½n2 – 3n jaetaan epäyhtälö puolittain n2:lla  c1 ½– 3/n sitä mukaa kun n kasvaa, vähentäjä 3/n pienenee. Kun n saavuttaa arvon 7, tulee erotuksen ½– 3/n arvoksi aidosti positiivinen ½– 3/7. Tästä laventamalla saadaan edelleen = 7/14 – 6/14 = 1/14. n:n arvosta 7 eteenpäin erotus ½– 3/n vain kasvaa jatkuvasti. Siten voimme valita vakion c2 paikalle arvon 1/14 ja vakion n0 paikalle arvon 7. Vakion c1 paikalle valittiin jo edellä ½. siten (1/14)n2 ½n2 – 3n  ½n2, kun n  7, joten todetaan, että ½n2 – 3n = (n2).□

  41. 3.1 Asymptoottinen merkintätapa • Kannattaa huomioida, että algoritmien analyysissä on käytetään merkintätapaa f(n) = (x), missä x on jokin aikakompleksisuusluokka, siitä huolimatta, että (x) edustaa itse asiassa funktioiden joukkoa eikä mitään yksittäistä funktiota (oikeaoppisempi merkintä olisi f(n) (x)). • Esimerkki: Osoitetaan, että 6n3(n2) Tehdään vastaoletus ja uskotaan, että pystytään sittenkin löytämään sellaiset positiiviset reaalilukuvakiot c1 ja c2 sekä syötteen koko n0, josta lähtien on kaikilla n:n arvoilla voimassac1n2 6n3  c2n2 • Jaetaan epäyhtälö puolittain termillä n2 ja tarkastellaan aluksi epäyhtälön vasenta puolta:c1  6n Voidaan valita c1:n paikalle vaikkapa vakio 1, jolloin vasen puoli toteutuu kaikilla positiivisillan:n arvoilla.Mutta mitä mahtaa tapahtua epäyhtälön oikealla puolella? Saadaan:6n c2 Sitä mukaa kun n kasvaa, myös termin 6narvo kasvaa, mutta vakio c2 pysyy ennallaan. valittiinpa vakio c2 miten suureksi tahansa, ennemmin tai myöhemmin kohdataan sellainen n:n arvo, jolloin 6n ylittää vakion c2 arvon, toisin sanoen mille tahansa vakiolle c2 > 0 on voimassa = .  vastaoletus virheellinen, joten alkuperäinen väite pitää paikkansa. □

  42. 3.1 Asymptoottinen merkintätapa • Jotta funktio f(n) kuuluisi johonkin luokkaan (x), pitää f(n)-lausekkeen korkeimman asteen termin olla sama kuin kasvunopeusluokan termi x. • Lause: Jos p(n) on astetta k oleva polynomi, eli p(n) = , missä ai:t ovat vakioita ja ak > 0, niin silloin p(n) = (nk). •  Lauseen todistus sivuutetaan tässä, mutta todistaminen onnistuu jakamalla aikaisempien esimerkkien tapaan muodostettava kaksoisepäyhtälö termillä nk. • Esimerkki: Jos p(n) = ⅓n7 + 1192n6 + 4330n + 171009, niin p(n) = (n7). • Muut paitsi polynomin korkeinta astetta olevan termija sen mahdollinen n:stä riippuva kerroin voidaan jättää huomiotta kasvunopeutta ilmoitettaessa, sillä n:n kasvaessa tarpeeksi paljon mainittu termi tulee peittämään muiden termien vaikutuksen alleen. korkeinta astetta oleva termi ns. dominoi lauseketta n:n ollessa riittävän iso edellisessä esimerkissä seitsemännen asteen termin vaikutus tulee pitkän päälle kaikkein suurimmaksi, vaikka vielä esimerkiksi n:n arvolla 3000 toinen termeistä on arvoltaan tätä isompi. • Edellisen perusteella kannattaa muistaa, että vaikkapa lauseke p(n) = nlog2n + 4n + 1 (n), sillä n:stä riippuvan logaritmikertoimen ansiosta nlog2n kasvaa nopeammin kuin n. • Kannattaa lisäksi huomioida, että vakio on nollannen asteen polynomi, joten vakiofunktio kuuluu kasvunopeusluokkaan (n0) = (1).

  43. 3.1 Asymptoottinen merkintätapa • -merkinnällä tarkoitetaan ns. asymptoottista ylärajaa • Mikäli jonkin algoritmin suoritusaikaa kuvaa lauseke T(n) = (n), tarkoittaa se sitä, että kyseisen algoritmin ajankäyttö on kaikilla kelvollisilla syötteillä verrannollinen korkeintaan syötteen koon 1. potenssiin, mutta ylärajan ei tarvitse olla tiukka. algoritmi saa toimia tätä nopeamminkin (toisin kuin -merkinnän yhteydessä)! • Matemaattisesti -merkintä voidaan ilmaista seuraavasti:  c  R+ ja  n0  N siten, että(g(n)) = { f(n) | 0  f(n)  cg(n) jokaiselle n  n0 } mikäli funktio f(n) toteuttaa nämä ehdot , merkitään, että f(n) = (g(n)). • ”Suomennettuna” edellinen merkintä tarkoittaa, että algoritmin ajankäyttöä ilmaiseva funktio (lauseke) f(n) kuuluu kasvunopeusluokkaan (g(n)) silloin, kun löydetään mielivaltainen positiivinen reaalilukuvakio c sekä jokin syötteen koko n0  0 , josta lähtien f(n) pysyy aina pienempänä tai yhtä suurena kuin cg(n). •  funktion f(n)kuvaaja sijoittuu n:n arvosta n0 lähtien kuvaajan cg(n) alapuolelle. sillä, miten f(n) käyttäytyy n0:aa pienemmillä n:n arvoilla, ei ole merkitystä! • Kannattaa huomioida, että f(n) = (g(n))  f(n) = (g(n)). Sen sijaan päinvastaisesta ei ole mitään takeita (eli implikaatio ei ole yleisesti voimassa toiseen suuntaan)!

  44. 3.1 Asymptoottinen merkintätapa • Esimerkki: Lisäyslajittelun parhaan tapauksen aikakompleksisuus T(n) = an + b = O(n2), mutta se ei kuitenkaan ole suuruusluokkaa (n2). •  lauseketta an + b ei pysty rajoittamaan alhaalta termillä c1n2 millään kertoimen c1 arvolla! • -merkintää käytetään usein kuvaamaan algoritmin pahinta tapausta, eli se rajoittaa sen suoritusaikaa kaikilla kelvollisilla syötteillä vain ylhäältä päin. • Algoritmin aikavaativuus on polynomiaalinen, jos se kuuluu luokkaan O(nk), kun k on jokin ei-negatiivinen vakio. • Seuraavat laskusäännöt ovat voimassa O-merkinnöille. Ne pätevät myös  -merkinnöille, joten säännöistä voi ordot korvata kaikkialta thetoilla.Jos T1(n) = O(f(n)) ja T2(n) = O(g(n)), niin 1) T1(n) + T2(n) = max{O(f(n)), O(g(n))} • 2) T1(n)  T2(n) = O(f(n)  (g(n)) • Jos f(n) = (g(n))  = c  0.Esimerkki: Osoitetaan, että aritmeettisen sarjan summa = (n2). • Todistus: = ½n(n + 1) ja = ½ = ½ = ½. □

  45. 3.1 Asymptoottinen merkintätapa • Seuraavassa taulukossa on lueteltu muutamien, algoritmien analyysissä hyödyllisten funktioiden arvoja eri n:n arvoilla. • Hyviä muistisääntöjä: • 1) mikä tahansa kasvava logaritmifunktio kasvaa asymptoottisesti nopeammin kuin yksikään vakiofunktio (sen arvo ei tietystikään riipu lainkaan syötteen koosta) • 2) mikä tahansa kasvava potenssifunktio kasvaa nopeammin kuin yksikään logaritmifunktio • 3) mikä tahansa kasvava eksponenttifunktio kasvaa nopeammin kuin yksikään potenssifunktio

  46. 3.2 Matemaattisia merkintöjä ja funktioita • Seuraavassa esitetään muutamia kurssilla toistuvasti käytettäviä matemaattisia määritelmiä ja laskusääntöjä.1) Kertoma sekä katto- ja lattiafunktiot • Luonnollisen luvun nkertoma (merkitään n!) määritellään rekursiivisesti seuraavasti:n! = 1, jos n = 0 = n  (n – 1)!, jos n > 0 • Toisin sanoen, n! = 1  2  3  …  n, kun n > 0 • n!  nn kaikilla n > 0, ja lisäksi nnkuuluu kertomaa ylempään aikavaativuusluokkaan • Kattofunktiolla x tarkoitetaan luvun x pyöristämistä ylöspäin lähimpään kokonaislukuun. • Lattiafunktiolla x tarkoitetaan puolestaan luvun x pyöristämistä alaspäin lähimpään kokonaislukuun. • 2) Eksponenttifunktio • Oletetaan, että a > 0, sekä m ja n ovat mielivaltaisia reaalilukuja • a0 = 1 • a1 = a • a-1 = 1/a ja a-n = 1 / an • (am)n = amn = (an)m • aman = am + n • Mikäli a > 1, niin mille tahansa vakiolle b  R on voimassa seuraava tulos: = 0 •  tämä tarkoittaa sitä, että mikä tahansa eksponenttifunktio, jonka kantaluku a > 1, kasvaa nopeammin kuin yksikään polynomifunktio  sama voitaisiin ilmaista myös merkinnällä nb = (an), missä merkintä  (pikku-ordo) tarkoittaa ns. epätarkkaa ylärajaa (tarkempi esittely sivuutetaan tällä kurssilla)

  47. 3.2 Matemaattisia merkintöjä ja funktioita • 3) Logaritmit • lg n = log2n (2-kantainen logaritmijärjestelmä: tarvitaan useimmin tietojenkäsittelyssä) • log n = log10n (10-kantainen logaritmijärjestelmä) • ln n = logen (luonnollinen logaritmijärjestelmä, jonka kantalukuna on Neperin luku e  2.718) • logkn = (log n)k • log log n = log(log n) • Kaikille reaaliluvuille a, b, c > 0 on voimassa: • logc(ab) = logca + logcb • logc(a/b) = logca – logcb • logcan = n logca • blogba = a • logba = 1 / logab • logba = (1 / logcb)  logca /* logaritmien muunnossääntö toiseen kantalukuun */ • Viimeisestä laskusäännöstä pystyy päättelemään, että eri logaritmit eroavat toisistaan vain vakion suuruisella suhdeluvulla n:stä riippumatta. •  eri logaritmeilla on sama asymptoottinen kasvunopeus! •  kaikki logaritmit kasvavat hitaammin kuin yksikään kasvava polynomifunktio!

  48. 4 Rekursioyhtälöistä • Rekursioon ja rekursiivisiin algoritmeihin ehdittiin tutustua jo kursseilla ”Johdatus informaatioteknologiaan I / II”. rekursiivisten algoritmien ongelmanratkaisutapa syötteen koolle n perustuu tätä aidosti pienemmän (esimerkiksi n – 1:n kokoisen) syötteen ratkaisemiseen. •  ajatuksena on, että sitä mukaa kun n jatkuvasti pienenee, tehtävä ratkeaa alkuperäistä helpommin. kun aikanaan n tulee riittävän pieneksi, tehtävän tiedetään ratkeavan vakioajassaEsimerkkejä: 1) kertoman laskennassa voidaan asettaa perustapaukseksi n = 0, jolloin vastaukseksi palautetaan 1 (algoritmille voidaan asettaa useampiakin perustapauksia, mutta se ei kertoman tapauksessa ole tarpeen, mutta esimerkiksi Fibonaccin lukujen rekursiivisessa laskennassa näin on) 2) (limitys)lajitteluongelma on triviaali, jos lajiteltavia alkioita on korkeintaan yksi: pääohjelmassa testataan ainoastaan, onko syötevektorin vasen raja aidosti pienempi kuin oikea. Ellei ole, ei tehdä mitään.  siten voidaan olettaa, että korkeintaan 1 alkion lajittelu vie vakioajan. • Tällä kurssilla olemme juuri limityslajittelun kohdalla törmänneet suoritusaikalausekkeeseen, jossa yhtälön oikea puoli ei ole ratkaistussa muodossa, vaan sisältää termiä T(x), missä x < n on jokin osa alkuperäisen syötteen koosta. Limityslajittelun tarkka suoritusaikalauseke olisi muotoa: • T(n) = (1), kun n  1T(n/2) + T (n/2) (n), kun n > 1 • Kyseessä on rekursioyhtälö, joka on tarpeen ratkaista, jotta algoritmin kokonaissuoritusaika pystyttäisiin lausumaan turvautumatta jonkin kooltaan n:ää pienemmän syötteen ratkaisuaikaan.

  49. 4 Rekursioyhtälöistä • Yleensä tyydytään esittämään rekursioyhtälöstä vain rekursiivisia termejä sisältävä tapaus (edellä se, jossa n > 1), sillä tehtävän voidaan olettaa ratkeavan vakioajassa, kun n on riittävän pieni. • Myös katto- ja lattiafunktiot jätetään usein merkitsemättä, sillä niillä ei ole merkitystä asymptoottisen suoritusajan kannalta.  siten limityslajittelun suoritusaika kuvataan ratkaisemattomassa muodossa usein seuraavalla tavalla (ilman perustapausta sekä katto- ja lattiafunktioita): • T(n) = 2T(n/2) + (n) • Tällä kurssilla tarkastellaan iterointimenetelmää rekursioyhtälöiden ratkaisemiseksi. Muitakin menetelmiä on toki olemassa (esimerkiksi ratkaisun arvaaminen ja todistaminen oikeaksi). • Lähdetään aluksi tarkastelemaan rekursioyhtälöä T(n) = T(n/2) + 1. Kaavan sanomana on, että n:n kokoisen syötteen ratkeaminen vaatii työtä yhden yksikön enemmän kuin puolet lyhyempi syöte. Sovelletaan kaavaa toistuvasti sijoittamalla aina oikean puolen T(n) lausekkeen syötteen koko vasemmalle n:n paikalle. Aluksi saadaan:T(n/2) = T(n/4) + 1, seuraavalla yrityksellä T(n/4) = T(n/8) + 1, sitten T(n/8) = T(n/16) + 1 ja niin edelleen. Joka kerta T(x):n argumentti puolittuu edellisestä.  Käytetään tällä tavoin saatuja termejä hyväksi alkuperäisen syötteen työmäärän T(n) ilmaisemiseksi:

  50. 4 Rekursioyhtälöistä • T(n) = T(n/2) + 1 /* lausutaan T(n/2) summan T(n/4) + 1 avulla */ = (T(n/4) + 1) + 1 = T(n/4) + 2 /* korvataan nyt puolestaan T(n/4) termillä T(n/8) + 1 … */ = (T(n/8) + 1) + 2 = T(n/8) + 3 /* … ja jatketaan samaan tapaan … */ = (T(n/16) + 1) + 3 = T(n/16) + 4 = T(n/24) + 4 … • Nähdään, että yleisesti i. iteraatiolla saadaan muoto = T(n/2i) + i • Koska voimme olettaa, että pienillä n:n arvoilla – esimerkiksi syötteen koolla 1 – suoritusaika on vakio, pitää ratkaista, milloin T:n argumenttina oleva n/2i saavuttaa arvon 1, eli montako iteraatiokierrosta pitää tehdä. Ratkaistaan yhtälö n/2i = 1 termin i suhteen: kerrotaan ensin puolittain 2i:llä … n = 2i … ja otetaan molemmilta puolilta log2:  i = log2n •  Tarvitaan siis i = log2n iteraatiokierrosta, että saavutetaan n:n arvo 1. Sijoitetaan tämä i:n arvo ylempänä saatuun yleiseen muotoon: • T(n) = T(n/2i) + i = T(n/2log2n) + log2n /* 2log2n = n */ = T(1) + log2n = c + log2n, missä c on jokin positiivinen vakio (kuvaa tapauksen n = 1 ratkeamiseen kuluvaa aikaa) • Siten T(n) = (log2n), koska logaritmi kasvaa nopeammin kuin vakio (joka on tietysti kiinteä).

More Related