Esittely latautuu. Ole hyvä ja odota

Esittely latautuu. Ole hyvä ja odota

Johdatus Informaatioteknologiaan II Turun yliopisto, Informaatioteknologian laitos, periodi 1 / 2011 Lasse Bergroth.

Samankaltaiset esitykset


Esitys aiheesta: "Johdatus Informaatioteknologiaan II Turun yliopisto, Informaatioteknologian laitos, periodi 1 / 2011 Lasse Bergroth."— Esityksen transkriptio:

1 Johdatus Informaatioteknologiaan II Turun yliopisto, Informaatioteknologian laitos, periodi 1 / 2011 Lasse Bergroth

2 Kurssin sisältö 2. Algoritmien suunnittelu (jatkoa kurssilta JIT1) 2.7 Rekursio ja iteraatio Rekursio Iteraatio Rekursio vai iteraatio? 2.8 Tieto- ja tallennusrakenteet 3. Algoritmiteoriaa 3.1 Tehtävän algoritminen laskettavuus 3.2 Kompleksisuus 3.3 Oikeellisuus 4. Tietokoneen rakenne ja toiminta 4.1 Matemaattiset perusteet 4.2 Fysikaaliset perusteet 4.3 Tietokoneen komponentteja 4.4 Mikro-ohjelmoitava tietokone 4.5 Konekieli 4.6 Kommunikointi

3 Kurssin sisältö 5. Systeemiohjelmisto
5.1 Ohjelmointikielten kääntäminen 5.2 Syntaksin määrittely 5.3 Kääntäjän toiminta 5.4 Käyttöjärjestelmät 5.5 Tiedostojärjestelmä 6. Epädeterminismi, rinnakkaisuus ja vapaajärjesteisyys 6.1 Epädeterministiset ongelmat 6.2 Rinnakkaisalgoritmit 6.3 Vapaajärjesteinen ohjelmointi Lukujen numerointi vastaa luentomonisteen mukaista numerointia. Kurssin sisältöä kuvaava luettelo tarkentuu kurssin edetessä. Tästä syystä tarkempi alilukujen numerointi lisätään vasta pätkittäin, kun on tarkemmin selvillä, miten eri alilukuja painotetaan luennoissa. Luennoilla ei ehditä käsitellä koko luentomonisteen vastaavia lukuja, mutta siitä huolimatta kannattaa moniste käydä läpi huolellisesti.

4 2.7 Rekursio ja iteraatio Rekursio ja iteraatio ovat usein kaksi erilaista, vaihtoehtoista lähestymistapaa samaan asiaan: kuin ”saman kolikon kaksi puolta”. Esimerkki saman ongelman ratkaisemisesta kummallakin tavalla: aidan maalaaminen Esitetään ongelmalle ensiksi iteratiivinen … MODULE maalaa(aita) WHILE aitaa maalaamatta DO suorita laudan maalaus siirry seuraavaan lautaan ENDWHILE ENDMODULE … ja sitten rekursiivinen ratkaisu: IF aitaa maalaamatta THEN maalaa(loput aidasta) ENDIF Kahden ratkaisun selkein ero: iteratiivisessa ratkaisussa esiintyy toistolause, rekursiivisessa sen tilalla moduulin itsensä kutsu.

5 2.7.1 Rekursio Rekursio on reduktiivisen ongelmanratkaisun erikoistapaus, mutta kuitenkin suora seuraus modulaarisuudesta  moduuli voi kutsua laskennassa mitä tahansa toista moduulia – myös itseään Siinä tietyn, kooltaan n olevan ongelman ratkaisu perustuu pienemmän vastaavanlaisen ongelman ratkaisuun (esimerkiksi n – 1:n kokoiseen). Jotta ongelman rekursiivinen ratkaiseminen olisi mahdollista, pitää seuraavien kahden vaatimuksen toteutua: ) Kun ongelman koko on tarpeeksi pieni, se ratkeaa triviaalisti ilman uutta rekursiivista kutsua ) Jokainen uusi rekursiivinen kutsu lähestyy jotain tällaista triviaalia tapausta, josta käytetään myös nimitystä perustapaus tai rekursion kanta. Ellei jälkimmäinen ehdoista toteudu, rekursio jää joko polkemaan paikallaan (laskenta ei edisty) ja on päättymätön tai jopa villiintyy (loittonee yhä kauemmas triviaaleista tapauksista). Silloin puolestaan, kun rekursion jokin perustapauksista saavutetaan, ei rekursiota enää jatketa pidemmälle, vaan se alkaa palautua ja sitä varten muodostettu rekursiopino alkaa purkautua. Mitä myöhemmin muodostettu kutsu, sitä aikaisemmin sen suoritus päättyy.

6 2.7.1 Rekursio Esimerkki rekursiosta: Kertoman laskeminen palautuskaavan avulla (edellä se laskettiin iteratiivisesti): MODULE KertomaRek(n) RETURNS n! IF n = 0 THEN RETURN 1 ELSE RETURN n * KertomaRek(n – 1) ENDIF ENDMODULE Tarkastellaan, miten kertoman laskenta etenee rekursiivisesti, jos algoritmissa kohdataan seuraava asetuslause: x := KertomaRek(3)  Yllä olevaa funktiota kutsutaan x:n arvon määräämiseksi siis todellisen parametrin arvolla 3, joka kopioidaan moduulissa muodollisen parametrin n paikalle. Tällöin moduulista muodostuu ns. ensimmäinen aktivaatio  Koska n = 3, suoritetaan ELSE-haara, jossa kohdataan palautuslause, jossa funktion paluuarvoksi asetetaan 3 * KertomaRek(2), jota ei ole mistään suoraan saatavilla.  Nykyinen aktivaatio asetetaan odottamaan, kunnes KertomaRek(2) on ensinnä laskettu, minkä jälkeen se voidaan sijoittaa funktion kutsun paikalle.

7 2.7.1 Rekursio  Käynnistetään toinen aktivaatio moduulista KertomaRek, kun n =  Koska n = 2, suoritetaan nytkin ELSE-haara, jossa kohdataan palautuslause, jossa funktion paluuarvoksi asetetaan 2 * KertomaRek(1), jota pitää laskea, ennen kuin toisen aktivaation suoritusta voidaan jatkaa.  Käynnistetään kolmas aktivaatio moduulista KertomaRek, kun n =  Koska n = 1, valitaan edelleen ELSE-haara, jossa kohdataan palautuslause, jossa funktion paluuarvoksi asetetaan 1 * KertomaRek(0), jota pitää laskea, ennen kuin kolmannen aktivaation suoritusta voidaan jatkaa  Käynnistetään neljäs aktivaatio moduulista KertomaRek, kun n =  Koska lopultakin n = 0, valitaan nyt THEN-haara, jossa kohdataan palautuslause, jossa funktion paluuarvoksi asetetaan 1. Enää ei rekursiota jatketa syvemmälle, vaan on saavutettu rekursion kanta, josta se alkaa palautua  Tulos KertomaRek(0) = 1 on nyt kutsujan eli moduulin kolmannen aktivaation käytettävissä. Neljäs aktivaatio päättyy tähän.  Nyt saadaan kolmannen aktivaation paluuarvoksi 1 * 1 = 1, joka on nyt toisen aktivaation käytettävissä. Kolmas aktivaatio päättyy tähän  Nyt saadaan toisen aktivaation paluuarvoksi 2 * 1 = 2, joka on nyt ensimmäisen aktivaation käytettävissä. Toinen aktivaatio päättyy tähän  Ensimmäinen kutsun tulokseksi saadaan 3 * 2 = 6, joten algoritmin suoritus päättyy tähän.

8 2.7.1 Rekursio Kannattaa huomioida, että saman moduulin eri aktivaatioissa esiintyvillä muuttujilla ei ole mitään tekemistä keskenään. Esimerkiksi edellä muodostettiin muuttujasta n neljä eri esiintymää, ja kukin niistä oli käytettävissä vain omassa aktivaatiossaan (arvona esiintyi joko 3, 2, 1 tai 0). Jos n:n arvoa olisi suorituksen aikana päivitetty, päivitys ei olisi näkynyt mihinkään aikaisemmista aktivaatioista. Moduulia kutsutaan häntärekursiiviseksi, jos rekursiivinen kutsu (eli kutsu itseensä) on sen viimeinen suoritettava lause. Edellä esitetyt esimerkit (aidan maalaaminen, kertoman laskeminen) olivat kumpikin häntärekursiivisia. Toinen esimerkki rekursiivisesta algoritmista: kahden positiivisen kokonaisluvun suurimman yhteisen tekijän eli syt(x, y):n määrääminen. MODULE syt(x, y) RETURNS x:n ja y:n suurin yhteinen tekijä IF x = y THEN RETURN x ENDIF IF x > y THEN RETURN syt(x – y, y) ENDIF IF x < y THEN RETURN syt(x, y – x) ENDIF ENDMODULE

9 2.7.1 Rekursio  moduulin triviaalina tapauksena esiintyy x = y: kun tämä tilanne saavutetaan, vastaus eli x:n arvo nykyisessä aktivaatiossa välitetään kutsupinon huipulle asti  muutoin tarkalleen jompikumpi parametreista x tai y pienenee aidosti uutta rekursiivista kutsua käynnistettäessä  rekursio päättyy viimeistään silloin, kun kumpikin parametreista saavuttaa arvon 1, joka on väistämättä kummankin luvun yhteinen tekijä Tarkastellaan vielä ongelmaa merkkijonon kääntämisestä peilikuvakseen rekursiivisesti Alustava ratkaisuhahmotelma voisi olla seuraavanlainen: Ota erilleen sanan ensimmäinen kirjain. Käännä sanan loppuosa takaperin. Lisaa ensimmäinen kirjain käännetyn osan loppuun.  Algoritmi kaipaa kuitenkin täsmennystä, miten pitkään toimintoja suoritetaan.  Tarvitaan triviaali tapaus, joksi kelpaisi yhdestä merkistä koostuva sana. Sen kääntämiseksi ei tarvitse tehdä mitään.  Tätäkin paremmin kelpaa perustapaukseksi kuitenkin vielä tyhjä sana (sana, jossa ei ole yhtään merkkiä): senkään kääntämiseksi ei tarvitse tehdä mitään, ja lisäksi vältytään asettamasta reunaehtoa, että käännettävässä sanassa pitää esiintyä aina vähintään yksi merkki.

10 2.7.1 Rekursio MODULE takaperin (sana) IF sana ei ole tyhjä THEN eka := sanan ensimmainen kirjain takaperin(sanan loppuosa) Tulosta(eka) ENDIF ENDMODULE Tarkastellaan luennolla esimerkkinä sanan ”auto” kääntämistä takaperin yllä esitettyä moduulia käyttämällä. Kannattaa huomioida, että edellä esitetty algoritmimoduuli ei ole häntärekursiivinen, sillä parametrina annettavan sanan ensimmäinen merkki tulostetaan vasta rekursiivisen kutsun jo päätyttyä. Joissakin ei-modulaarisissa koneenläheisissä kielissä rekursio ei ole käytettävissä. 2.7.2 Iteraatio Iteratiivinen ongelman ratkaiseminen perustuu ajatukseen edetä kohti ratkaisua laskemalla aina vain tarkentuvia välituloksia. Ratkaisun löytämisen idea: suoritetaan toistoa ja tarpeeksi pitkään. Esimerkki: iteratiivisessa kertoman laskennassa aikaisempaa tulosta kerrottiin aina kierroslaskurin numerolla  tulos tarkentuu kierroksittain.

11 2.7.2 Iteraatio Jotta iteratiivinen ratkaisun etsiminen olisi mahdollista, pitää seuraavien kolmen reunaehdon toteutua: ) Pitää olla käytettävissä laskennan aikana saavutettuja välituloksia ) Välituloksista pitää pystyä etenemään kohti ratkaisua. 3) Täytyy kyetä tunnistamaan ratkaisun löytyminen. Esimerkki: suorituksen eteneminen kuplalajittelualgoritmissa (vrt. aliluku )  1) Välituloksiksi kelpaavat syötteen kulloisetkin järjestykset  2) jokainen toistokierros saa (ainakin teoriassa) aikaan edistystä lajittelussa: yhä useampi lajitelluista alkioista on saatu oikeille paikoilleen laskennan edetessä ulomman silmukan i. kierros vie i:nneksi suurimman alkion lopulliselle paikalleen  3) syöte on lajiteltu, kun enää yhtään alkioparia ei jouduttu vaihtamaan silmukan uloimman kierroksen aikana. Iteraatio soveltuu erittäin luontevasti ongelmiin, joissa riittää likiarvon määrääminen ratkaisulle sekä numeeriseen analyysiin  kts. monisteen esimerkit neliöjuuren likiarvon määräämiseksi sekä numeerisen integroinnin suorittamiseksi (esimerkit sivuutetaan luennolla)

12 2.7.2 Iteraatio Tarkastellaan lopuksi Eratostheneen seulan toteuttamista esimerkkinä iteratiivisesta algoritmimoduulista. Siinä määrätään kaikki alkuluvut väliltä [2, n]. Tämä algoritmi on selkeästi tehokkaampi kuin aikaisemmin esitettyyn alkulukutestiin perustuva alkulukujen määrääminen. Algoritmin toimintalogiikka: alun perin joukko S sisältää koko lähtöjoukon [2..n]. Ulommassa silmukassa otetaan kunkin kierroksen alussa käsiteltäväksi S:n pienin alkio b, joka on välttämättä samalla myös alkuluku, ja se viedään alkulukujen joukkoon A. Sisemmässä silmukassa poistetaan b:n kaikki monikerrat, jotka ovat  n. Kun S:n pienin luku ylittää n:n neliöjuuren, ovat loput S:ään jääneistä luvuista nyt alkulukuja, joten ne liitetään A:han ja ulomman silmukan suoritus päättyy. MODULE seula(n) RETURNS alkulukujen joukko S := {2, 3, 4, 5, 6, ..., n} A := Ø (* tyhjä joukko *) REPEAT Etsi joukon S pienin luku b A := A  {b} (* lisätään alkio b joukkoon A *) m := b S := S – {m} (* poistetaan alkio m joukosta S *) m := m + b UNTIL m > n UNTIL b > neliöjuuri(n) RETURN A  S ENDMODULE

13 2.7.3 Rekursio vai iteraatio?
Usein rekursio ja iteraatio ovat toisilleen vaihtoehtoisia ongelman ratkaisutapoja. Iteraatiota pidetään kuitenkin yleensä tehokkaampana, sillä tietokoneen toiminta on luonteeltaan iteratiivista, joten samaa menettelyä noudattavan algoritmin kääntäminen onnistuu usein tehokkaammin. Tiettyihin ongelmiin kuitenkin näistä jompikumpi soveltuu luontevammin kuin toinen  erityisesti numeerisessa analyysissä ja likiarvojen laskennassa iteraatio on selvästi ilmeisempi valinta  sen sijaan tehtävissä, jotka voidaan palauttaa saman tyyppisen mutta pienemmän ongelman ratkaisuun, rekursiivinen ratkaisemistapa tuntuu osuvammalta (esimerkiksi funktionaalinen ja logiikkaohjelmointi)  rekursio on reduktioperiaatteen erikoistapaus. On kuitenkin olemassa myös ns. luonnostaan rekursiivisia tehtäviä, joihin löytyy hyvin helposti oikeelliseksi havaittava rekursiivinen ratkaisu, mutta sitä vastoin mitään kunnollista iteratiivista ratkaisua niille ei ole olemassa. Tähän ryhmään kuuluu tunnetuimpana ns. Hanoin tornien ongelma, johon palataan myöhemmin  näillekin ongelmille saadaan aikaan iteratiivinen algoritmi, mutta se on kovin väkinäisen tuntuinen vastaavan tehtävän ratkaisevaan rekursiiviseen algoritmiin verrattuna (muunnos iteratiiviseksi ohjelmaksi tehdään kuitenkin aina käännettäessä ohjelmaa konekielelle, jossa rekursio ei ole sallittua).

14 2.8 Tieto- ja tallennusrakenteet
2.8.1 Tyyppi, abstrakti tietotyyppi ja sen implementointi Kurssilla ’Johdatus informaatioteknologiaan I’ keskityttiin pitkälti algoritmien ohjausrakenteiden tarkasteluun, ongelman asteittaiseen tarkentamiseen ja modularisointiin. Sen sijaan datan käsittelyyn kiinnitettiin paljon niukemmin huomiota. Muutamia poikkeuksia lukuun ottamatta (esimerkiksi nimilista) kurssin esimerkeissä esitetty tieto oli tyypiltään yksinkertaista, kuten kokonais- ja reaalilukuja, totuusarvoja tai merkkijonoja. Usein kuitenkin ongelman ratkaisemisessa tarvittavat tiedot ovat loogisesti toisiinsa yhteenkuuluvia, joten ne olisi tarpeen pitää yhdessä myös ongelmaa ratkaisevaa algoritmia suoritettaessa  tarvitaan avuksi tiedon kuvaavaan esittämiseen soveltuvia tietorakenteita Algoritmin on tarkoitus kuvata tietojenkäsittelyprosessia, jossa yksi tietorakenne (syöttötiedot) muunnetaan toiseksi tietorakenteeksi (tulostiedot). Jokaista ei-yksinkertaista tyyppiä olevaa tietorakennetta kohti pitää määritellä uusi tyyppi, joka konstruoidaan käyttämällä hyväksi olemassa olevia rakenteita. Tyypin voidaan mieltää muodostuvan kahdesta osasta: ) Se määrittää, mitä laillisia arvoja sitä edustava muuttuja (tai vakio) voi saada ) Lisäksi se rajaa operaatiot, joita kyseisen tyyppisiin alkioihin voidaan kohdistaa.

15 2.8.1 Tyyppi, abstrakti tietotyyppi ja sen implementointi
Esimerkki: kokonaislukutyyppi (Pascal: integer, Java, C: int) tulee täysin määriteltyä, kun kerrotaan, mikä on suurin ja pienin esitettävissä oleva kokonaisluku ja mitkä operaatiot niille ovat laillisia (+, -, *, / ja jakojäännös) ja miten ne toimivat Perustyypeillä operaatiot ovat sisäänrakennettuja ja niiden toiminta on oletettua. Omien tietorakenteiden konstruoimiseksi ovat pseudokielessämme käytettävissä taulukot , tietueet ja linkitetyt rakenteet. Oliokeskeisessä ohjelmoinnissa myös luokan käsite on määriteltynä ja tarjolla omien tietorakenteiden konstruointia varten. Tietorakenteista puhuttaessa on syytä erottaa tietorakenteen abstrakti malli ja sen mahdolliset tallennusrakenteet. Tallennusrakenne kertoo, miten tietorakenne on toteutettu (millaisista osista se on konstruoitu) ja miten se tallennetaan tietokoneen muistiin. Tietorakenteen abstrakti malli sen sijaan kuvaa ainoastaan, millaisia operaatioita tietorakenteeseen voi kohdistaa ja miten kyseiset operaatiot toimivat. Tällaista mallia kutsutaan abstraktiksi tietotyypiksi (ADT). Abstraktiudella tarkoitetaan tässä yhteydessä sitä, että ) tarkastellaan ainoastaan mallin mukaisten alkioiden oleellisia piirteitä ) malli on riippumaton ohjelmointikielestä ja ) alkiot kuvataan niiden käyttäytymisen avulla (tallennusrakenne peitetään)

16 2.8.2 Tallennusrakenteet 2.8.2.1 Tietue
Tietueen avulla voidaan koota toisiinsa yhteen kuuluvat, mutta mahdollisesti eri tyyppiä edustavat tiedot, yhdeksi tietorakenteeksi. Tietueen eri rakenneosia kutsutaan kentiksi, jotka voivat edelleen muodostua mistä tahansa määritellyistä rakenteista (tietue voi sisältää vaikkapa toisen tietueen). On tarjolla tyyppikonstruktorina useimmissa ohjelmointikielissä. Soveltuu hyvin esimerkiksi henkilö-, tuote- tai tilaustietojen tallentamiseen. Esimerkki: Henkilötietueen ja –muuttujan esitteleminen Pascal-ohjelmointikielellä type (* esitellään tyyppi nimeltä henkilotiedot *) henkilotiedot = RECORD nimi: String; (* tyyppi String on merkkijonotyyppi *) osoite: String; (* … muita tietoja tarpeen mukaan *) END; var (* perustetaan tyyppiä henkilotiedot oleva muuttuja *) yksihenkilo: henkilotiedot; Tietueen kenttiin viitataan usein pistenotaatiolla (esimerkki: x := yksihenkilo.nimi) Tietueille lailliset operaatioita ovat: 1) arvon sijoittaminen tietueen kenttään ) tietueen yksittäisen kentän arvon käyttäminen 3) tietueen kopioiminen

17 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko
Taulukko on staattinen, kiinteän kokoinen tallennusrakenne, jonka koko määräytyy useissa ohjelmointikielissä (esimerkiksi Pascal- ja C-kielissä) jo käännösaikana. Muutamissa kielissä (esimerkiksi Javassa) taulukon koon määrääminen onnistuu vielä ajon aikanakin, mutta sitä ei saa mennä muuttamaan sen jälkeen, kun se on kertaalleen määrätty. Toisin kuin tietue, taulukko sisältää yleensä useita eri henkilöitä tai asioita kuvaavia tietoja. Sen sijaan kaikki taulukkoon tallennetut tiedot ovat keskenään tyypiltään identtisiä (esimerkiksi kokonaislukuja), kun taas tietueen eri kentät voivat erota toisistaan tietotyypiltään. Taulukolla voi 1- tai useampiulotteinen: 1-ulotteista taulukkoa kutsutaan yleensä vektoriksi ja 2-ulotteista matriisiksi. Taulukolle varataan muistista fyysisesti yhtenäinen alue, eli sen alkiot sijaitsevat muistissa peräkkäin.  taulukko on hyvä ja tehokas tallennusrakenne, jos siihen tallennettavien alkioiden lukumäärä on kiinteä tai korkeintaan niukasti vaihteleva (muussa tapauksessa joudutaan aina varautumaan alkioiden teoreettiseen enimmäismäärään sille kokoa määrättäessä, jolloin muistitilan käytöstä tulee tehotonta)

18 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko
Taulukon yksittäiseen muistilokeroon eli soluun viitataan indeksoinnilla. Indeksi esitetään yleensä ei-negatiivisena kokonaislukuna hakasulkeiden ([]) sisällä, mutta myös muut numeroituvat tyypit (ei siis reaaliluvut ja merkkijonot) voivat tulla kyseeseen indekseinä. Indeksi koostuu yhtä monesta arvosta kuin taulukossa on ulottuvuuksia eli dimensioita. Esimerkki vektorista: lämpötilan mittaustulokset vuorokauden jokaiselta tasatunnilta väliltä – 24.00: Oletetaan, että muuttuja x on määritelty 1-ulotteiseksi vektoriksi, johon edellä näkyvät lämpötilatiedot on tallennettu. Nyt esimerkiksi lämpötila kello 11 saataisiin tulostettua komennolla Tulosta(x[11]) Vastaavasti, lämpötilaksi kello 6 aamulla saataisiin asetettua +3 käskyllä x[6] := 3 Jos taulukko on 2-ulotteinen, sovitaan normaalisti, että indekseistä ensimmäinen viittaa riviin ja jälkimmäinen sarakkeeseen. 3-ulotteisia ja erityisesti sitä useampiulotteisia taulukoita tarvitaan selvästi vektoreita ja matriiseja harvemmin. 6 5 3 1 2 4 9 13 17 19 21 22 20 18 16 11 8

19 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko
Taulukon esittelemisen syntaksi vaihtelee suuresti eri ohjelmointikielten välillä Esimerkiksi 10-paikkainen vektori x esiteltäisiin Pascalissa seuraavasti … type vektori = ARRAY [1..10] OF integer; (* esitellään rakenteinen tyyppi 10-paikkaista kokonaislukuvektoria varten *) var x: vektori; (* määritellään x edellä esitellyn tyyppiseksi muuttujaksi *) … ja C:ssä seuraavasti: int *x = allocate(10 * sizeof(int)); (* x määritellään osoittimeksi 10-alkioiseen taulukkoon, jonka indeksointi alkaa nollasta (!) *) Esimerkki matriisista: Indeksoinnin esittäminen matriisissa M, jossa on 4 riviä ja 6 saraketta Se voisi esittää vaikkapa neljän eri henkilön pistemääriä kuudesta kokeesta. M[1,1] M[1,2] M[1,3] M[1,4] M[1,5] M[1,6] M[2,1] M[2,2] M[2,3] M[2,4] M[2,5] M[2,6] M[3,1] M[3,2] M[3,3] M[3,4] M[3,5] M[3,6] M[4,1] M[4,2] M[4,3] M[4,4] M[4,5] M[4,6]

20 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko
Taulukolle sallittuja operaatioita ovat: 1) tietoalkion tallennus taulukkoon indeksin osoittamaan kohtaan ) taulukon yksittäisen tietoalkion käyttö ) usein myös taulukon kopiointi Taulukon kaikki solut ovat alun perin alustamattomia, ennen kuin niille asetetaan arvo ensimmäistä kertaa. Seuraavaksi esitellään algoritmi, joka laskee pituudeltaan n olevaan ja välille 1..n indeksoituun kokonaislukuvektoriin T tallennettujen arvojen keskiarvon: MODULE keskiarvo(T) RETURNS T:n alkioiden keskiarvo s := 0; FOR i := 1, 2, … n DO s := s + T[i] (* lisätään summaan vuoron perään kukin vektorin luvuista *) RETURN s / n (* jaetaan muodostunut summa alkioiden kokonaismäärällä *) ENDMODULE

21 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko
Esimerkki 2-ulotteisesta taulukosta Etä, joka on indeksoitu lueteltua tyyppiä käyttäen Iisalmi Jyväskylä Kuopio Pieksämäki Suonenjoki Varkaus 254 85 174 136 223 169 80 118 129 89 51 138 38 49 87

22 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko
Esimerkki: Moduuli, joka käyttää edellä esitettyä taulukkoa Etä ja etsii sieltä parametrina annettua kaupunkia a lähimmän toisen kaupungin nimen  koska moduulin pitää palauttaa vastaus, tehdään siitä funktio MODULE LähinKaupunki(Etä, a) RETURNS kaupunkia a lähinnä olevan kaupungin lähinmatka := (* alkuarvo, joka varmasti tulee pienenemään *) FOR j := Iisalmi, …, Varkaus DO IF a <> j THEN IF Etä[a, j] < lähinmatka THEN lähinmatka := Etä[a, j] lähin := j ENDIF ENDIF ENDFOR RETURN lähin ENDMODULE Esimerkiksi kutsun ollessa LähinKaupunki(Etä, Suonenjoki) saataisiin paluuarvoksi Pieksämäki.

23 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko
Esimerkki: 2-ulotteisen lukutaulukon (matriisin) kaikkien alkioiden summan laskeminen MODULE Taulusumma(Taulu, m, n) RETURNS matriisissa Taulu olevien lukujen summa summa := FOR i := 1, 2, …, m DO FOR k := 1, 2, …, n DO summa := summa + Taulu[i, k] ENDFOR ENDFOR RETURN summa ENDMODULE Edellä esitetty moduuli lisää summaan aluksi ylimmän rivin alkiot vasemmalta oikealle, ja vastaavat toimenpiteet suoritetaan tämän jälkeen kaikille seuraaville riveille. Huom! Monisteessa on erilainen indeksointi: siellä m kuvaa sarakkeiden ja n rivien määrää (olettaen tietystikin, että ensimmäinen parametri tulkitaan riviksi ja jälkimmäinen sarakkeeksi: mikään ei estä ajattelemasta asiaa päinvastoin!) Taulukko soveltuu tallennusrakenteeksi yleensä huonosti silloin, jos siihen pitää usein lisätä alkioita alkuun tai keskelle poistamatta mitään alkiota  saatetaan joutua siirtelemään useita alkioita yhdellä eteenpäin Vastaavasti poistettaessa alkio joudutaan taulukkoa tiivistämään alkioiden siirroilla vasemmalle.

24 2.8.2 Tallennusrakenteet 2.8.2.3 Linkitetty rakenne
Linkitetyssä rakenteessa jokainen tietoalkio sisältää varsinaisen datan lisäksi vähintään yhden osoittimen rakenteen seuraavaan alkioon.  muodostuu tietoalkioiden ketjutettu rakenne Rakenne on luonteeltaan rekursiivinen: yhteen suuntaan linkitetyn rakenteen jokaista paitsi viimeistä alkiota seuraa vastaavanlainen mutta pienempi rakenne. Viimeisen alkion seuraaja yhteen suuntaan linkitetyssä rakenteessa on tyhjä. Graafisesti esitettynä yhteen suuntaan linkitetty rakenne näyttää seuraavanlaiselta: Toisin kuin edellä esitelty taulukko, linkitetty rakenne on dynaaminen, mikä tarkoittaa, että sen koko voi vaihdella tarpeen mukaan suorituksen aikana  taloudellinen valinta muistinkäytön kannalta silloin, kun tallennusrakenteeseen säilöttävän datan määrä vaihtelee suuresti (ei tarvitse varautua ennalta pahimpaan). Uuden alkion lisääminen ja olemassa olevan poistaminen varsin helppoja operaatioita Hankaluutena on kuitenkin rakenteen alkioihin käsiksi pääseminen: sitä voidaan selata vain yhteen suuntaan, joten edelliseen alkioon siirtyminen edellyttää koko rakenteen selaamista uudelleen alusta alkaen haluttuun kohtaan asti.

25 2.8.2 Tallennusrakenteet 2.8.2.3 Linkitetty rakenne
Mikäli joudutaan usein liikkumaan linkitetyssä rakenteessa eri suuntiin, kannattaa se toteuttaa kaksisuuntaisella linkityksellä  alkiosta päästään suoraan sekä edeltäjään että seuraajaan  haittapuolena kuitenkin päivitysten hankaloituminen, koska ylläpidettävien linkkien määrä kaksinkertaistuu Kaksisuuntaisestakaan linkitetystä rakenteesta ei ole apua, jos halutaan usein siirtyä alku ja loppupään ylitse. Ratkaisuksi tulee rengasrakenne: merkitään viimeisen solmun seuraajaksi rakenteen alkusolmu, ja vastaavasti ensimmäisen solmun edeltäjäksi rakenteen viimeinen solmu. Rengasrakenteen linkitys voi olla joko 1- tai 2-suuntainen Rengaslistojen pulma: listan alkamisen ja loppumisen tunnistaminen vaikeutuu, sillä on usein tarpeen tietää, milloin kaikki alkiot on jo ehditty käsitellä.

26 2.8.3 Abstraktit tietotyypit
2.8.2 Tallennusrakenteet Linkitetty rakenne Lisäksi voidaan linkitetyssä rakenteessa sallia solmuille useampia seuraajia kuin enintään yksi. Tällöin alun perin listamaisesta rakenteesta muodostuu puu.  puita käytetään usein muun muassa hakua tehostavana tietorakenteena. 2.8.3 Abstraktit tietotyypit Seuraavassa tarkastellaan abstrakteja tietotyyppejä, joille on tietyt ominaisuudet ja joihin voidaan kohdistaa rakenteelle määriteltyjä operaatioita. Oleellista on, että abstraktin tietotyypin kannalta sen toteuttamiseksi käytetyllä tallennusrakenteella ei ole merkitystä  tärkeää on ainoastaan, miten abstraktia rakennetta käsitellään ja mitä eri operaatiot saavat aikaan. Lista Lista on dynaaminen rakenne, joka koostuu peräkkäin asetetuista alkioista. Ensimmäistä alkiota lukuun ottamatta kaikilla listan alkioilla on yksikäsitteinen edeltäjä ja viimeistä alkiota lukuun ottamatta niillä on yksikäsitteinen seuraaja  listarakennetta kutsutaan esitystapansa tähden usein peräkkäisrakenteeksi.

27 2.8.3 Abstraktit tietotyypit
Lista Listalle pitää yleisesti ottaen (ei-rajoitetulle listalle) pystyä suorittamaan seuraavia operaatioita: * uuden alkion lisääminen mihin tahansa paikkaan listassa * olemassa olevan alkion poistaminen listan vapaavalintaisesta paikasta * listan tyhjyyden tarkastus * listan ensimmäisen alkion ja loppuosan (ilman ensimmäistä alkiota olevan osan) palauttaminen kutsujalle Lista voidaan määritellä rekursiivisesti seuraavalla tavalla: Lista on joko ) tyhjä lista tai ) alkio, jota seuraa lista Esimerkkejä listarakenteista: * sana on kirjaimista muodostuva lista. * lause on sanojen muodostama lista * luvun esitys (esimerkiksi 14293) on numeroiden muodostama lista * juna on tietue, jonka kaksi komponenttia ovat veturi ja vaunujen muodostama lista * puhelinluettelo on henkilötietueiden muodostama lista (kukin tietue sisältää yhtä henkilöä tai yritystä koskevat tiedot asianmukaisiin kenttiin tallennettuna)

28 2.8.3 Abstraktit tietotyypit
Lista Lista soveltuu käytettäväksi silloin, kun tietoalkioita käsitellään peräkkäin seuraavaan tapaan: Ota käsiteltäväksi listan ensimmäinen alkio WHILE ei olla listan lopussa DO käsittele listan alkio siirry seuraavaan alkioon ENDWHILE Mikäli listan operaatioita rajoitetaan siten, että lisäys- ja poisto-operaatiot pitää suorittaa aina tiettyyn kohtaan listassa (alkuun tai loppuun), saadaan aikaan seuraavat tietorakenteet: Jono on lista, jossa alkion lisäys tapahtuu aina rakenteen loppuun ja poistaminen (edellyttäen, ettei lista ole tyhjä) listan alusta. Vain jonon ensimmäiseen alkioon pääsee käsiksi. Lisäksi pitää pystyä testaamaan, onko jono tyhjä  Alkiot poistetaan jonosta samassa järjestyksessä kuin ne sinne viedään (vrt. asiakasjonon ”normaali” eteneminen kaupan kassalla). Pino on lista, jolla on muuten samat ominaisuudet kuin jonolla, mutta siinä uuden alkion lisäys ja alkion poistaminen kohdistuvat molemmat rakenteen huipulle (yleensä loppuun)  Mitä myöhemmin alkio on lisätty pinoon, sitä varhaisemmin se siitä poistetaan (vrt. kolikot bussikuskin lippaassa)

29 2.8.3 Abstraktit tietotyypit
Listan toteutus Lista voidaan toteuttaa (sekä yleisenä että rajoitettuna tietorakenteena) käyttämällä tallennusrakenteena joko taulukkoa (vektoria) tai dynaamista linkitettyä rakennetta. Jos käytetään vektoritoteutusta …  edut: mihin tahansa listan alkioon pääsee helposti käsiksi (joko lukemaan tai päivittämään) viittaamalla indeksoinnilla alkion sijaintipaikkaan  haitat: 1) alkion poistaminen työlästä, sillä poistettavaa alkiota seuraavat alkiot joudutaan siirtämään vasemmalle, jotta tallennusalue säilyy tiiviinä ) joudutaan aina varautumaan tilanteeseen, jossa lista on maksimaalisen pituinen Jos käytetään linkitettyä rakennetta …  edut: listan koko voi vaihdella runsaastikin ilman, että sen ylläpitämiseksi joudutaan tekemään paljoa työtä (muutokset ovat hyvin paikallisia ja rajoittuneita)  haitat: alkion hakuoperaatiot vievät mahdollisesti kauan aikaa Toisin sanoen, vektoritoteutus on hyvä valinta silloin, kun listan koko pysyy ainakin likimain kiinteänä ja siihen kohdistuu usein luku- ja päivitysoperaatioita muttei sen sijaan lisäys- ja/tai poisto-operaatioita, jotka vaativat alkioiden siirtoja oikealle (lisäys) tai vasemmalle (poisto). Linkitetty rakenne soveltuu puolestaan käytettäväksi silloin, kun lista on luonteeltaan aidosti dynaaminen eli siihen lisätään ja sieltä poistetaan alkioita usein. Seuraavaksi esitetään esimerkki, miten uuden alkion lisääminen tapahtuisi tallennusrakenteen tasolla paikkaan i vektorimuotoiseen listaan L, jonne mahtuu n alkiota ja jossa on paraikaa k alkiota (k < n).

30 2.8.3 Abstraktit tietotyypit
Listan toteutus k := k + 1; IF i = k (* Lisäys tapahtuu listan loppuun *) THEN L[i] := a ELSE (* Lisäys listan alkuun tai keskelle: kohdan i jälkeisiä alkioita joudutaan siirtämään eteenpäin – oikealle – jotta lisäys onnistuisi *) FOR x := k, k – 1, …, i + 1 DO L[x] := L[x–1] ENDFOR L[i] := a ENDIF Edellisessä algoritmissa vektorissa olevia alkioita joudutaan siirtämään paikasta i lukien yhdellä oikealle  muutoin jokin aikaisemmista arvoista menetetään! Käyttäjä tietäisi abstraktista tietotyypistä vain, miten lisäysoperaatiota kutsutaan. Siten alkion lisäämisen suorittavan moduulin käyttöliittymässä pitäisi olla seuraavat parametrit: ) Mikä alkio lisätään? ) Minne? ) Kuinka monenneksi? Moduulin rungossa pitäisi lisäksi vielä aluksi selvittää, paljonko alkioita on ennestään eli muuttujan k alkuarvo. Edellinen algoritmi ei muuten toimi (ensimmäinen asetuslause muutoin kelvoton).

31 2.8.3 Abstraktit tietotyypit
Puu Puu on lineaarisen listan yleistys. Samoin kuin listoilla, myös puun jokaisella alkiolla ensimmäistä lukuun ottamatta on yksikäsitteinen edeltäjä. Tästä syystä puu on listan tavoin hierarkkinen tietorakenne. Sen sijaan puun alkioilla voi olla useita seuraajia, mikä ei ole sallittua listoilla. Seuraajien määrä voi vaihdella eri alkioiden kohdalla. Tiedot tallennetaan rakenteen haarautumiskohtiin eli solmuihin. Solmuja yhdistävät viivat eli kaaret edustavat loogisia suhteita niiden päätepisteissä olevien solmujen välillä  solmun edeltäjää kutsutaan isäksi (tai vanhemmaksi) ja seuraajia pojiksi (tai lapsiksi). Puun haaroja, jotka yhdistävät solmun ja sen välittömän ja välilliset seuraajat, kutsutaan poluksi.  polun pituus solmusta itseensä on 0. Hierarkiassa ylimpänä olevaa solmua kutsutaan juureksi. Sillä ei ole lainkaan edeltäjää. Vastaavasti solmuja, joilla ei ole yhtään seuraajaa, kutsutaan lehtisolmuiksi tai lehdiksi. Muut kuin lehdet ovat puun sisäsolmuja. Polun pituus solmusta A solmuun B tarkoittaa, miten monen solmun päässä samalla polulla sijaitsevat kyseiset kaksi ovat toisistaan. Solmun aste on solmun seuraajien lukumäärä  lehtisolmun aste on 0. Koko puun korkeus on polun pituus juurisolmusta sen kaukaisimpaan lehteen. Tällöin pelkän juurisolmun sisältävän puun korkeudeksi tulee  Huom! Toisinaan puun korkeus määritellään kuitenkin sen tasojen lukumääräksi. Tällöin tyhjän puun korkeus on 0, ja pelkän juuren sisältävän puun korkeus on 1.

32 2.8.3 Abstraktit tietotyypit
Puu Puu on rekursiivinen tietorakenne, joka samaistetaan usein juurensa kanssa:  jokainen puun solmu on itse yhden pienemmän puun eli alipuun juurisolmu. Solmun seuraajan edustamaa puuta kutsutaan poikapuuksi. Puuta kutsutaan k-haaraiseksi (tai k-ariseksi), jos sen jokaisella solmulla on tarkalleen k poikapuuta, joista osa voi olla tyhjiä. Määritelmä: k-arinen puu on joko: ) tyhjä puu, jossa ei ole lainkaan solmuja, tai ) se koostuu juurisolmusta, jota seuraa k poikapuuta, jotka ovat k-haaraisia puita  monet puita käsittelevistä algoritmeista ovat rekursiivisia Esimerkki puusta: P Q R S  valkoiset solmut ovat sisäsolmuja, tummennetut lehtiä, puun korkeus P  S on 4, puun ariteetti eli haarojen määrä on 4, tyhjiä poikapuita ei ole merkitty, Q on juuren P ja R on Q:n poikapuu

33 2.8.3 Abstraktit tietotyypit
Binääripuut Lista mielletään 1-haaraiseksi eli unaariseksi puuksi. Vastaavasti 2-haaraista puuta nimitetään binääripuuksi. Binääripuussa kullakin solmulla on 0 – 2 seuraajaa. Seuraajasolmuista käytetään nimityksiä vasen ja oikea poika, niistä alkavista alipuista nimityksiä vasen- ja oikea poika- tai alipuu. Binääripuilla on tärkeä asema hakurakenteena. Kaikki siihen kuuluvat solmut voidaan listata käymällä ne lävitse jossain systemaattisessa järjestyksessä. Koska vakiintuneen käytännön mukaisesti jokaisen solmun vasen alipuu tutkitaan ennen oikeaan siirtymistä, on tarjolla seuraavat usein käytetyt menettelyt solmujen läpi käymiseksi: ) esijärjestys: solmussa vieraillaan ennen sen poikapuiden solmuja ) välijärjestys: solmussa vieraillaan heti, kun sen koko vasen poikapuu on tutkittu ) jälkijärjestys: solmussa vieraillaan vasta, kun sen molemmat poikapuut on tutkittu Algoritmi: puun solmujen listaaminen välijärjestyksessä (p on osoitin puun solmutietueeseen) MODULE välijärjestys(p) IF p <> tyhjä puu THEN välijärjestys(p.vasen) Tulosta(p.arvo) välijärjestys(p.oikea) ENDIF ENDMODULE

34 2.8.3 Abstraktit tietotyypit
Binääripuut Yleisimmin käytetään juuri välijärjestystä, sillä sitä soveltamalla voidaan listata puun solmuihin tallennetut arvot eli avaimet suuruusjärjestyksessä pienimmästä suurimpaan. Määritelmä: Puu p on järjestetty binääripuu, jos ) p on tyhjä tai ) p on ei-tyhjä, ja seuraavat ehdot ovat voimassa (oletetaan, että kaikki puuhun talletetut arvot ovat keskenään erisuuria): p:n juuren arvo > p:n vasemman poikapuun jokaisen solmun arvo p:n juuren arvo < p:n oikean poikapuun jokaisen solmun arvo p:n vasen ja oikea poikapuu ovat järjestettyjä binääripuita Esimerkkejä järjestetyistä binääripuista: Jussi 18 Fredi Kati 12 36 24 42 Bertta Jaana Sami 30 Mari

35 2.8.3 Abstraktit tietotyypit
Binääripuut Järjestettyä binääripuuta kutsutaan usein myös binääriseksi hakupuuksi, sillä siitä pystytään nopeasti etsimään haettavaa arvoa. Jos binäärinen hakupuu luetaan välijärjestyksessä, saadaan tulokseksi lineaarinen järjestetty lista. Esimerkki: Järjestämättömän listan lajitteleminen binääripuuta hyväksi käyttäen järjestämätön lista  järjestetty binääripuu  järjestetty lista MODULE lajittele(lista l) (* Parametri l viittaa listan ensimmäiseen alkioon. *) p := ListaPuuksi(l, p) (* Konstruoidaan listan l alkioista järjestetty binääripuu p. *) PuuListaksi(p, l) (* Muodostaa binäärisen hakupuun p alkioista järjestetyn listan l *) ENDMODULE Listan l alkioiden lisääminen puuhun onnistuu seuraavaa algoritmia käyttämällä: MODULE ListaPuuksi(lista l, järjestetty binääripuu p) p := tyhjä puu WHILE l <> tyhjä lista DO LisääPuuhun(l.arvo, p) (* Lisää listan l yksittäisen alkion puuhun p.*) l := l.seuraava (* Otetaan listan seuraava alkio käsittelyyn. *) ENDWHILE ENDMODULE

36 2.8.3 Abstraktit tietotyypit
Binääripuut Alkion lisääminen järjestettyyn binääripuuhun määräytyy välijärjestyksen mukaan. Ensiksi tarkastellaan puun p juurisolmua. Alkiolle etsitään paikkaa vertaamalla lisättävää arvoa matkan varrelle osuvien solmujen arvoihin  jos kohdatun solmun arvo on suurempi kuin listasta luettu arvo, edetään nykyisestä solmusta p:n vasempaan alipuuhun edellyttäen, ettei se ole tyhjä  jos kohdatun solmun arvo on pienempi, siirrytään vastaavasti oikeaan ei-tyhjään alipuuhun  kun lopulta tulee vastaan puun p tyhjä haara, lisättävä alkio viedään sinne Esitetään rekursiivinen algoritmi, joka vie alkion a oikealle paikalleen järjestettyyn binääripuuhun p MODULE LisääPuuhun(alkio a, järjestetty binääripuu p) IF p = tyhjä THEN p.arvo := a p. vasen := tyhjä p.oikea := tyhjä ELSE IF a < p.arvo THEN LisääPuuhun(a, p.vasen) ELSE LisääPuuhun(a, p.oikea) ENDIF ENDIF ENDMODULE

37 2.8.3 Abstraktit tietotyypit
Binääripuut Esitetään vielä toinen rekursiivinen algoritmi, joka muuntaa lopulta järjestetyn binääripuun järjestetyksi listaksi MODULE PuuListaksi(järjestetty binääripuu p, järjestetty lista l) IF p <> tyhjä THEN PuuListaksi(p.vasen, l) Lisää juuren arvo listan l loppuun PuuListaksi(p.oikea, l) ENDIF ENDMODULE Algoritmin toiminta-ajatus: edetään puussa aina rekursiivisesti niin pitkälle vasemmalle kuin mahdollista, jolloin löytyy tarkasteltavan alipuun pienin alkio. Tämän jälkeen tulostetaan sen juuri ja siirrytään tarkastelemaan oikeaa alipuuta rekursiivisesti.  Suoritettavien askelten prioriteetti: 1) edetään vasemmalle niin kauan kuin pystytään ) lisätään viimeksi kohdattu solmu listan l loppuun ) edetään oikealle viimeksi kohdatusta solmusta  Lopputulos: nousevaan suuruusjärjestykseen lajiteltu lista l. Algoritmi noudattaa puun solmujen listaamista välijärjestyksessä: osoittimen p osoittaman solmun koko vasen alipuu tulostetaan rekursiivisesti ennen sen osoittamaa solmua, joka tulostetaan seuraavaksi, ja vasta lopuksi tulostetaan rekursiivisesti koko oikea alipuu.

38 2.8.3 Abstraktit tietotyypit
Binääripuun toteutus Binääripuu voidaan toteuttaa linkitettynä rakenteena, jossa jokaiseen solmuun tallennetaan tietokenttien arvo(je)n lisäksi osoittimet vasempaan ja oikeaan poikapuuhun. Binääriseen hakupuuhun tallennetut arvot voidaan esittää myös ns. sulkumerkki- eli termiesityksellä.  ei vaadi graafista esitystä  tekniikka esitelty luentomonisteessa, mutta sivuutetaan luennolla Toinen ei-graafinen esitysmuoto järjestetylle binääripuulle: vektorimuotoinen lista V, jossa solmun i vasen poika löytyy indeksipaikasta 2i ja oikea poika paikasta 2i + 1. Puuttuvan pojan symboli on ’_’  käytetään usein tietokoneohjelmissa Järjestetyn binääripuun redusoitu versio: (minimi- tai maksimi)keko Maksimikeossa on aina voimassa ominaisuus V[i] > V[2i] ja V[i] > V[2i+1] (minimikeossa päinvastoin), eli isäsolmussa sijaitseva arvo on aina suurempi (pienempi) kuin sen poikiin tallennetut arvot.  maksimi (minimi) löytyy aina puun juuresta erittäin nopeasti, ja keon ylläpito on yksinkertaista  kekoa tarvitaan mm. prioriteettijonoissa ja kekolajittelussa ( Tietorakenteet ja algoritmit I)  sen sijaan rakenne EI OLE enää järjestetty binääripuu! Datakenttä 1 Datakenttä n Osoitin vasempaan poikaan Osoitin oikeaan poikaan

39 2.8.3 Abstraktit tietotyypit
Graafi Graafi on puurakenteen yleistys: siinä hierarkiasta on annettu periksi siten, että solmun edeltäjän ei enää tarvitse olla yksikäsitteinen (itse asiassa koko edeltäjä – seuraaja -asetelma puuttuu kokonaan suuntaamattomasta graafista).  solmusta voi alkaa ja siihen voi päättyä mielivaltaisen monta kaarta, jotka yhdistävät sen graafin muihin solmuihin  solmun asteella tarkoitetaan siitä lähtevien kaarten lukumäärää  solmun sisäaste on puolestaan solmuun saapuvien kaarten määrä Graafi voi olla joko suunnattu tai suuntaamaton.  suunnatussa graafissa voidaan solmusta edetä toiseen vain sellaista kaarta pitkin, joka on merkitty solmusta lähtevällä nuolella (esimerkiksi yksisuuntaista katua kuvaava merkintä)  graafi voi lisäksi olla joko painottamaton tai painotettu Graafia voidaan käyttää hyvin esimerkiksi liikenneyhteyksien kuvaamiseen. Seuraavassa esimerkki painotetusta suuntaamattomasta graafista: Iisalmi 85 Kuopio 51 Suonenjoki Jyväskylä Pieksämäki Varkaus

40 2.8.3 Abstraktit tietotyypit
Graafi Edellä esitetty graafi sisältää täsmälleen saman informaation kuin alempana esitetty välimatkataulukko.  Graafiesitys näyttää havainnolliselta, mutta välimatkat pitää laskea yhteen, jos kahden kaupungin välillä ei ole suoraa yhteyttä. Taulukosta haluttu tulos saadaan suoraan. Iisalmi Jyväskylä Kuopio Pieksämäki Suonenjoki Varkaus 254 85 174 136 223 169 80 118 129 89 51 138 38 49 87

41 2.8.3 Abstraktit tietotyypit
Graafi Mikäli jokin yhteyksistä lyhenee, riittää graafissa pelkkä yhden kaaren painon päivittäminen  jos vaikkapa Kuopion ja Suonenjoen välistä rautatietä oikaistaisiin nykyisestään kilometrin verran, tarvitsisi graafista muuttaa vain kyseisten kaupunkien välistä suoraa yhteyttä kuvaavan kaaren paino 51:stä 50:een  sen sijaan taulukossa joudutaan päivittämään kaikki ne etäisyydet, joissa osuus Kuopio – Suonenjoki kuuluu osaksi matkaa (vaikkapa Iisalmi – Pieksämäki) Taulukko on graafiesitystä tehokkaampi suoran lasketun informaation etsimiseen. Suunnattu graafi on syklitön, mikäli yhdestäkään graafin pisteestä ei pystytä palaamaan takaisin samaan pisteeseen mitään etenemistapaa käyttämällä. Graafit soveltuvat hyvin suurten verkostojen esittämiseen. Esimerkiksi kelpaisivat vaikkapa sukulaisuussuhteet (esimerkki monisteessa) ja lentoreittien verkosto. Sukugraafi on syklitön silloin, jos sinne ei tallenneta symmetrisiä (esimerkiksi puoliso) tai välillisiä (esimerkiksi serkku) sukulaisuussuhteita. Myöskään kaikkia kaaria ei tarvitse välttämättä ottaa käyttöön kaiken tarpeellisen informaation esittämiseksi. Esimerkiksi henkilön lapset voidaan kerätä listaksi vanhimmasta nuorimpaan siten, että vanhempi yhdistetään kaarella ainoastaan vanhimman lapsensa kanssa. Katso monisteen esimerkkiä siitä, miten saman informaation (puoliso, lapsi/vanhempi sekä sisarus) sisältävä graafi yksinkertaistuu, jos nämä kolme relaatiota esitetään redusoidussa muodossa (mies/vaimo, esikoinen/äiti ja pikkusisarus).

42 2.8.3 Abstraktit tietotyypit
Graafin toteutus Graafi voidaan esittää parhaiten joko linkitettyjä rakenteita tai taulukkoja käyttäen Linkitetyssä rakenteessa tarvitaan yhdestä solmusta osoittimet niihin solmuihin, joihin se on yhdistetty kaaren avulla (vrt. edellä kuvattu sukugraafi). Erityisesti painotettujen graafien esittämiseen sopii taulukkomuotoinen bittikartta eli vierekkäisyysmatriisi (solussa oleva arvo 0 kuvaa, ettei kahden pisteen välillä esiinny kaarta)  nollasta poikkeava arvo kuvaa solmuja yhdistävän kaaren painoa. Jos graafi on painottamaton, arvo 1 kuvaa kaaren olemassaoloa indeksiparina esitettyjen kohteiden välillä. Seuraavassa vielä esimerkki tästä: Iisalmi Jyväskylä Kuopio Pieksämäki Suonenjoki Varkaus 1

43 2.8.4 Abstraktien tietotyyppien implementoinnista
Luvussa esitellään, miten abstraktin tietotyypin Pino ja sille määriteltyjen operaatioiden esittely tapahtuu pseudokielellä. Esittely tapahtuu sekä kapseloimattomana että kapseloituna versiona. Kapseloimattoman abstraktin tietotyypin tallennusrakenne näkyy käyttäjälle, ja hän pääsee siihen halutessaan käsiksi, vaikkei tämä olekaan suotavaa  ei ole tarkoituksenmukaista, että ohjelmoija menee tekemään muutoksia pinon sisältöön muulla tavoin kuin erikseen määriteltyjä pino-operaatioita käyttämällä  jos muuttuja p on tyyppiä Pino, voitaisiin periaatteessa tehdä vaikkapa asetus p[3] := 5 riippumatta siitä, paljonko pinossa on alkioita lausetta suoritettaessa, mikä on selvästikin ristiriitaista pinolle sovellettavia sallittuja operaatioita ajatellen  pinoon lisäyksen ja sieltä poiston pitää aina kohdistua pinon huipulle – muuten ei enää ole kyseessä pino, jos näitä rajoituksia rikotaan! Kapseloidussa pinon toteutuksessa pinon sisältöä pääsee muuttamaan yksinomaan sitä varten määriteltyjä operaatioiden kautta (määritelmän PUBLIC-osuus). Suorat viittaukset tallennusrakenteisiin, kuten edellinen asetuslause, on estetty (PRIVATE). Tutustukaa itse monisteen sivun 68 esimerkkeihin! 2.8.5 Oliokeskeinen ohjelmointi Sisältää muun muassa tietoa pinon toteuttamisesta oliokeskeisellä Eiffel-ohjelmointikielellä. Sivuutetaan luennolla ajan puutteen vuoksi, mutta … … tutustukaa kaikesta huolimatta omalla ajallanne tähän alilukuun!

44 3.1 Tehtävän algoritminen ratkeavuus
3 Algoritmiteoriaa Tähän mennessä on tällä kurssilla – sekä myös kurssilla JIT1 – käsitelty lähinnä algoritmien muodostamista ja niiden esittämistä. Sen sijaan ei juurikaan ole kiinnitetty huomiota muodostettujen algoritmien käytännön tehokkuuteen. Tässä luvussa on tarkoitus antaa lyhyt johdatus algoritmien analyysiin sekä perustella, miksi läheskään kaikkia tehtäviä ei pystytä ratkaisemaan algoritmisesti. Lisäksi tullaan toteamaan, että myös jotkin algoritmisesti ratkeavat tehtävät ovat yleisessä tapauksessa kelvottomia – ne vaativat liian paljon resursseja, jotta ratkaisu olisi kohtuullisin reunaehdoin laskettavissa. 3.1 Tehtävän algoritminen ratkeavuus 3.1.1 Laskettavuus Tehtävän laskettavuuden määritelmä: tehtävälle löytyy algoritminen ratkaisu Kaikki tietokoneella ratkaistavissa olevat ongelmat ovat laskettavia. Tietokoneella ratkeavien ongelmien lisäksi on kuitenkin myös ongelmia, joille joko ) ei ole vielä keksitty algoritmista ratkaisua tai ) voidaan todistaa, ettei niille koskaan edes tule löytymään algoritmista ratkaisua!

45 3.1 Tehtävän algoritminen ratkeavuus
3.1.1 Laskettavuus Pitkään uskottiin, että kaikki olemassa olevat (ja myös tulevat!) ongelmat pystytään ratkaisemaan algoritmisesti  ”ellei algoritminen ratkaisu ole tiedossa, se tarkoittaa, ettei sitä joko ole vielä keksitty, tai sitten ongelmaa ei vielä ymmärretty riittävässä määrin” David Hilbertin vuonna 1928 esittämä ratkaisemisongelma (Entscheidungsproblem):  ”Oletetaan, että esitetään mielivaltainen kokonaislukuja koskeva väite. Voidaanko algoritmisesti selvittää, pitääkö väite paikkansa vai ei?” Kurt Gödel pystyi vuonna 1931 epätäydellisyyslauseellaan (Unvollständigkeitstheorem) todistamaan, ettei Hilbertin ongelma ole laskettavissa  seuraus: on olemassa tehtäviä, joita tietokoneella ei voida ratkaista! Myöhemmin, Gödelin todistuksen jälkeen, 1930-luvulla esitettiin muitakin algoritmisesti ratkeamattomia ongelmia  esittäjinä muun muassa matemaatikot Alonso Church, Stephen Kleene, Emil Post ja Alan Turing Toisin sanoen, monet algoritmien teoriaa koskevat tulokset todistettiin jo 1930-luvulla eli yli 10 vuotta ennen ensimmäisten tietokoneiden kehittämistä!

46 3.1 Tehtävän algoritminen ratkeavuus
3.1.2 Church-Turingin teesit Toistaiseksi ei ole päästy sopimukseen yleisesti käytettävästä algoritmin määritelmästä. Muutamia tunnettuja määritelmiä esitettiin ja 1950-luvuilla  Rekursiivisten funktioiden teorian mukaiset säännöt (Kleene 1935)  Lambda-kalkyyli (Church 1936) – funktionaalisen LISP-ohjelmointikielen teoreettinen perusta  Turingin koneen käskyjoukko (Turing 1937)  Markovin algoritmit (Markov 1951) Edellä esitetyt neljä algoritmin määritelmää on todistettu ekvivalenteiksi  jos ongelma voidaan ratkaista yhdellä näistä tavoista määritellyllä algoritmilla, se voidaan ratkaista myös kaikkia muilla kolmella tavalla määritellyllä algoritmilla Algoritmin määritelmään liittyvät ns. Churchin ja Turingin teesit ovat seuraavat: 1) Kaikki tunnetut järkevät algoritmin määritelmät ovat keskenään ekvivalentteja ) Mikä tahansa järkevä algoritmin määritelmä ikinä keksitäänkään, se tulee olemaan ekvivalentti tunnettujen algoritmien määritelmien kanssa Teeseistä ensimmäinen on osoitettu oikeaksi, ja toisen uskotaan pitävän paikkansa  jälkimmäistä ei voida todistaa, koska tulevia algoritmin määritelmiä ei vielä tunneta! Voidaan lisätä uusi määritelmä: ”Algoritmeja ovat tällä kurssilla algoritmien esitykseen käytettävällä pseudokielellä kirjoitettavissa olevat sallittujen toimenpiteiden sarjat”.

47 3.1 Tehtävän algoritminen ratkeavuus
3.1.3 Pysähtymisongelma ja muita algoritmisesti ratkeamattomia ongelmia Kirjoitetusta algoritmista ei läheskään aina pystytä päättelemään, päättykö sen suoritus ennemmin tai myöhemmin, vai jatkuuko sen suoritus loputtomiin.  olisi varsin hyödyllistä tietää ennalta, pysähtyykö tarkasteltava algoritmi, kun sille annetaan jokin tietty syöte  pitäisi saada vastaus ns. pysähtymisongelman tiettyyn esiintymään Pysähtymisongelman kuvaus: On olemassa mielivaltainen ohjelma P ja sen syötetiedot D Kysymys: Pysähtyykö P syötteellä D (merkitään P(D))? Pysähtymisongelma on esimerkki tehtävistä, jotka eivät ole laskettavissa. Todistetaan seuraavassa kyseisen ongelman ratkeamattomuus algoritmisesti ristiriidan avulla, joka saadaan tekemällä vastaoletus, jonka mukaan sittenkin olisi olemassa algoritmi, joka pystyisi vastaamaan, pysähtyykö P(D)  Kirjoitetaan vastaoletuksen mukainen funktionaalinen moduuli Pysähtymistesti MODULE Pysähtymistesti(P, D) RETURNS kyllä / ei (* Moduuli palauttaa arvon kyllä, jos P(D) pysähtyy ja muutoin arvon ei. *) ENDMODULE Määritelmänsä mukaisesti edellä kuvattu moduuli antaa aina vastauksen, eli se pysähtyy itse minkä tahansa syötteenä annetuilla ohjelman ja sen syötetietojen yhdistelmillä.

48 3.1 Tehtävän algoritminen ratkeavuus
3.1.3 Pysähtymisongelma ja muita algoritmisesti ratkeamattomia ongelmia Kirjoitetaan nyt uusi, äskeistä rajoitetumpi moduuli PysähtyyItsellään, joka testaa ohjelman P pysähtymistä, kun sille annetaan syötteeksi P:n ohjelmakoodi  ohjelman antamisessa syötteeksi ohjelmalle ei ole mitenkään ainutkertaista, vaan näin toimitaan usein esimerkiksi ohjelmointikielten kääntäjiä tai editoreja käytettäessä. MODULE PysähtyyItsellään(P) RETURNS kyllä / ei RETURN Pysähtymistesti(P, P) ENDMODULE Ainoana toimintonaan moduuli PysähtyyItsellään käynnistää moduulin Pysähtymistesti suorituksen siten, että P esiintyy kumpanakin todellisena parametrina. Konstruoidaan nyt edellä esiteltyjä moduuleja hyväksi käyttävä funktionaalinen moduuli Huuhaa, joka saa syötteekseen ohjelman P, jonka pysähtymistä itsellään halutaan testattavan. MODULE Huuhaa(P) RETURNS pysähtyy / ei pysähdy IF PysähtyyItsellään(P) = kyllä THEN REPEAT UNTIL 1 = ELSE RETURN kyllä ENDIF

49 3.1 Tehtävän algoritminen ratkeavuus
3.1.3 Pysähtymisongelma ja muita algoritmisesti ratkeamattomia ongelmia Tarkastellaan nyt seuraavaksi, mitä tapahtuu, kun edellä esitettyä moduulia kutsutaan itsellään, eli annetaan kutsu Huuhaa(Huuhaa). 1) Mikäli moduulissa testattavan kutsun PysähtyyItsellään(Huuhaa) palauttama arvo on tosi, tarkoittaa se nyt kahden ensiksi määritellyn moduulin perusteella selvästikin, että aluksi käynnistetyn kutsun Huuhaa(Huuhaa) kuuluu pysähtyä … … MUTTA: kutsun PysähtyyItsellään(Huuhaa) paluuarvon ollessa tosi moduulin Huuhaa IF-lauseen ehto toteutuu, minkä jälkeen THEN-haarassa ajaudutaan ikuiseen silmukkaan (lopetusehto 1 = 0 ei toteudu koskaan)  Tällöin Huuhaa(Huuhaa) ei todellisuudessa pysähdykään, vaikka Pysähtymistesti näin hetkeä aikaisemmin väitti!  Johtopäätös 1: Pysähtymistesti ei voi palauttaa arvoa tosi, jos sitä kutsutaan parametreilla Huuhaa(Huuhaa) ) Siispä pysähtymistestin tuloksen on oltava ”ei pysähdy” kutsulle Huuhaa(Huuhaa) … MUTTA: vastaus ”ei” kutsuun PysähtyyItsellään(Huuhaa) tarkoittaisi sitä, että moduulin Huuhaa suoritus ei pysähdy, kun sitä kutsutaan itsellään  jos moduulin PysähtyyItsellään(Huuhaa) paluuarvo on ”ei pysähdy”, myöskään IF-lauseen ehto moduulissa Huuhaa ei toteudu, joten siirrytään ELSE-haaraan, jossa paluuarvoksi asetetaan ”kyllä”.

50 3.1 Tehtävän algoritminen ratkeavuus
3.1.3 Pysähtymisongelma ja muita algoritmisesti ratkeamattomia ongelmia  Tällöin Huuhaa(Huuhaa) todellakin pysähtyy, vaikka Pysähtymistesti hetkeä aikaisemmin väitti, ettei Huuhaa(Huuhaa) pysähdy!  Johtopäätös 2: Pysähtymistesti ei voi palauttaa myöskään arvoa ”ei pysähdy”, jos sitä kutsutaan parametreilla Huuhaa(Huuhaa)!  Ajaudutaan ristiriitaan, mikä kumoaa vastaoletuksen oikeellisuuden. Toisin sanoen aluksi määriteltyä moduulia Pysähtymistesti ei voikaan olla olemassa! Pysähtymisongelman algoritmisen ratkeamattomuuden todistus vastaoletuksen avulla johtaa samanlaisiin ristiriitoihin kuin ns. valehtelijan paradoksi: ”Valehtelen”. Toinen esimerkki (ilmeisestikin )pysähtymättömästä algoritmista (algoritmi esitetty monisteessa): Fermat’n suuren lauseen paikkansapitävyyden kumoaminen kokeellisesti  jos kyseinen lause pitää paikkansa, sen paikkansapitävyyttä testaava algoritmi ei milloinkaan pysähdy (tarkempi käsittely sivuutetaan luennolla) Muita ei laskettavissa olevia ongelmia: * Totaalisuusongelma: pysähtyykö yksittäinen algoritmi P kaikilla syötteillään? * Ekvivalenssiongelma: suorittavatko algoritmit A ja B täysin saman tehtävän? * Ricen teoreema: annettuina tehtävä T ja algoritmi A – tekeekö algoritmi A tehtävän T?

51 3.1 Tehtävän algoritminen ratkeavuus
3.1.4 Yhteenveto laskettavuudesta Jos on olemassa algoritmi, joka ratkaisee annetun tehtävän kaikilla mahdollisilla kelvollisilla syötteillä, sanotaan, että tehtävä on laskettavissa. Jos puolestaan sanotaan, että tehtävä ei ole laskettavissa, on pystyttävä todistamaan, ettei voi olla olemassa algoritmia, joka ratkaisisi tehtävän kaikilla syötteillä. Tällöin sanotaan myös, että tehtävä on algoritmisesti ratkeamaton. Lisäksi on tehtäviä, joiden laskettavuus ei ole tiedossa.  ainakaan toistaiseksi ei ole pystytty kirjoittamaan tehtävän ratkaisevaa algoritmia …  … muttei myöskään ole todistettu tehtävän olevan algoritmisesti ratkeamaton. 3.2 Kompleksisuus Laskettavissa olevista tehtävistä on tarpeen tietää, minkä verran niiden ratkaiseminen vaatii eri tyyppisiä resursseja: aikaa, muistitilaa ja laitteistoa (esimerkiksi prosessoreita, jos algoritmi suorittaa rinnakkaista laskentaa). Algoritmien resurssientarvetta selvittävä tieteenhaara on nimeltään kompleksisuusteoria. Tällä kurssilla kompleksisuutta käsitellään ainoastaan lyhyesti. Lisäksi huomio kiinnitetään yksinomaan algoritmin aikakompleksisuuteen eli -vaativuuteen.  on kuitenkin olemassa tehtäviä, joissa muistinkulutus on kriittisempää kuin ajankäyttö.

52 3.2 Kompleksisuus Algoritmin kompleksisuudella mitataan sen tehokkuutta.  sen avulla voidaan verrata keskenään eri algoritmeja, jotka suorittavat saman tehtävän Esimerkki: Kahden positiivisen kokonaisluvun suurimman yhteisen tekijän määrääminen joko toistuvilla vähennyslaskuoperaatioilla (esitetty kurssilla JIT1 luvussa 2.6.4) tai seuraavalla jakojäännösmenetelmään perustuvalla Eukleideen algoritmilla Oletetaan, että halutaan laskea syt(75, 6) 1) Toistuvilla vähennyslaskuoperaatioilla (vähennetään suuremmasta luvusta pienempi, kunnes luvut tulevat yhtä suuriksi): syt(75, 6) = syt(69, 6) = syt(63, 6) = syt(57, 6) = syt(51, 6) = syt(45, 6) = syt(39, 6) = syt(33, 6) = syt(27, 6) = syt(21, 6) = syt(15, 6) = syt(9, 6) = syt(3, 6) = syt(3, 3) = 3  13 vähennyslaskuoperaatiota ) Eukleideen algoritmilla: syt(x, y) = syt(y, x MOD y), jos x > 0 ja y > x, jos x > 0 ja y =  pienempi luku vähennetään isommasta heti niin monta kertaa, kuin se sisältyy siihen syt(75, 6) = syt(6, 3) = syt(3, 0) = 3  vain 3 jakojäännösoperaatiota!

53 3.2 Kompleksisuus  esimerkkitapauksessa Eukleideen algoritmissa tehdään laskuoperaatioita vain vajaa neljäsosa siitä, mitä tehtäisiin toistetulla vähennyslaskumenetelmällä  Eukleideen algoritmi vaikuttaa siis näistä kahdesta menettelystä tehokkaammalta  Mitä kauempana x:n ja y:n alkuarvot ovat toisistaan, sitä selvemmäksi tulee tehokkuusero Eukleideen algoritmin hyväksi! Aikakompleksisuutta mitattaessa ei ole mielekästä käyttää mittayksikkönä absoluuttista kellotettua aikaa, sillä toteutunut suoritusaika riippuu paljolti koneesta, jossa algoritmia suoritetaan.  tästä syystä pyritäänkin arvioimaan suoritettavien alkeisoperaatioiden kokonaismäärää Usein eri peruslaskutoimitusten yksikkökustannukset arvioidaan keskenään samoiksi, mutta todellisuudessa yhteen- ja vähennyslaskun suorittaminen ovat ”halvempia” operaatioita kuin kerto- ja jakolaskuoperaatiot  ne vievät vähemmän suoritinaikaa yhtä operaatiota kohti Määritelmä: Algoritmin kompleksisuudella tarkoitetaan sen resurssintarvetta pahimmassa tapauksessa – silloin, kun sille annettu syöte on mahdollisimman epäedullinen, eli joudutaan tekemään maksimaalinen määrä työtä rehellinen mittari: enempää aikaa ei ainakaan jouduta käyttämään! Määritelmä: Tehtävän kompleksisuudella tarkoitetaan sen ratkaisemiseksi kehitetyt parhaan (tehokkaimman) algoritmin kompleksisuutta  sama tehtävä voidaan ratkaista mahdollisesti tehokkuudeltaan eri kertaluokkaa olevilla algoritmeilla (esimerkiksi n alkion lajittelu suuruusjärjestykseen)  tutustutaan seuraavaksi kahteen tapaan määrätä n. asteen polynomin arvo pisteessä x, ja analysoidaan kummankin algoritmin tehokkuutta kerto- ja yhteenlaskujen määrällä

54 3.2 Kompleksisuus Esimerkki: Polynomin anxn + an-1xn-1 + an-2xn-2 + … + a2x2 + a1x + a0 arvon laskeminen, kun kertoimet ai ja laskentapiste x  R. Kertoimet annetaan syötteessä vektorissa A, joka on indeksoitu välille [0..n] MODULE Polynomi1(A, x) RETURNS polynomin arvo pisteessä x summa := a0 (* summan alkuarvoksi polynomin vakiotermi *) FOR i := 1, 2, …, n DO (* lasketaan aluksi lausekkeen xi arvo ja tallennetaan se muuttujaan xpot *) xpot := REPEAT i TIMES xpot := xpot * x ENDREPEAT summa := summa + A[i]* xpot (* kerrotaan ai:llä edellä laskettu termi xi *) ENDFOR RETURN summa ENDMODULE  Algoritmin analyysi: * Sisemmässä silmukassa suoritetaan jokaista ulomman silmukan laskurin arvoa i kohti i kappaletta kertolaskuja. Lisäksi sisemmän silmukan päätyttyä tehdään ulommassa silmukassa vielä joka kierroksella 1 kertolasku  kertolaskuja yhteensä … + i + n = n(n + 1)/2 + n = n(n + 3)/2 kappaletta * Yhteenlaskuja suoritetaan yksi kutakin i:n arvoa kohti ulommassa silmukassa  yhteenlaskuja yhteensä n kappaletta

55 3.2 Kompleksisuus Kun merkitään polynomin korkeimman termin astelukua n:llä, kertolaskujen kokonaismäärää merkinnällä T1(n) ja yhteenlaskujen kokonaismäärää merkinnällä T2(n)  Algoritmille Polynomi1 saadaan T1(n) = n(n + 3)/2 = (n2 + 3n)/2 ja T2(n) = n. Edellä esitetty algoritmi on hyvin primitiivinen  se laskee x:n potenssit aina uudelleen alusta alkaen! MODULE Polynomi2(A, x) RETURNS polynomin arvo pisteessä x summa := a0 (* summan alkuarvoksi polynomin vakiotermi *) xpot := 1 (* voidaan asettaa x:n potenssin alkuarvoksi x0 = 1 *) FOR i := 1, 2, …, n DO (* kunkin kierroksen alkaessa muuttujassa xpot on tallella arvo xi-1 *) xpot := xpot * x (* kerrotaan xpot kerran itsellään, niin saadaan x:n i. potenssi *) summa := summa + ai * xpot (* kerrotaan vielä ai:llä edellä laskettu termi xi *) ENDFOR RETURN summa ENDMODULE  Algoritmin analyysi: * Tarvitaan vain yksi silmukka, jossa kutakin kierrosta kohti tehdään kaksi kertolaskua ja yksi yhteenlasku  kertolaskuja yhteensä 2n kappaletta  yhteenlaskuja yhteensä n kappaletta  Algoritmille Polynomi2 saadaan T1(n) = 2n ja T2(n) = n Mitä voimme päätellä näistä tuloksista?: Mitä korkeampaa astetta polynomi on, sitä selkeämmin jälkimmäisen moduulin paremmuus tulee esiin (se ei hukkaa jo kertaalleen laskettuja käyttökelpoisia välituloksia).

56 3.2.1 Kompleksisuuden kertaluokat
3.2 Kompleksisuus 3.2.1 Kompleksisuuden kertaluokat Useissa algoritmeissa alkeisoperaatioiden tarkan lukumäärän laskeminen tulee työlääksi eikä vastaa tarkoitustaan, kun syötteen kokoa ilmaiseva parametri n alkaa kasvaa yli tiettyjen rajojen Tällaisessa ns. asymptoottisessa kompleksisuustarkastelussa on tärkeintä arvioida suoritettavien alkeisoperaatioiden suuruusluokka tarkan arvon asemesta  suuruusluokan arviointi antaa yleensä riittävän selkeän käsityksen siitä, miten algoritmin voidaan odottaa käyttäytyvän syötteen koon kasvaessa  mielenkiintoista on tietää, miten voimakkaasti algoritmin suoritusaika riippuu syötteen koosta Edellisistä kahdesta moduulista, joissa laskettiin astetta n olevan polynomin arvoa pisteessä x, Polynomi1:n aikakompleksisuudeksi saataisiin n2 ja Polynomi2:lle n, kun sitä arvioidaan kertolaskuoperaatioiden määrällä. Aikakompleksisuudesta käytetään usein ns. iso ordo () -merkintää. Iso ordo -lauseke rajoittaa ylhäältä päin algoritmin suoritusaikaa  Tätä enempää ei aikaa käytetä pahimmassakaan tapauksessa n:n funktiona, kun n on riittävän iso  Polynomi1:n aikakompleksisuus on siten (n2) ja Polynomi2:n (n) Asymptoottisessa analyysissä muut kuin kaikkein merkitsevin suoritusaikalausekkeen termi jätetään usein mainitsematta  syynä tähän on se, että kun n kasvaa riittävän suureksi, alempaa astetta olevien termien merkitys suhteessa korkeinta astetta olevaan alkaa voimakkaasti vähentyä  seuraava taulukko valaissee asiaa tarkemmin

57 3.2.1 Kompleksisuuden kertaluokat
3.2 Kompleksisuus 3.2.1 Kompleksisuuden kertaluokat Taulukko ilmaisee, miten aikavaativuuslausekkeiden log2n, n2, n2 + 5n + 3, n3 ja 2n arvot muuttuvat n:n funktiona Havaintoja: ) Jos algoritmin suoritusaika on logaritminen, on täysin yhdentekevää, miten iso syöte sille annetaan  esimerkiksi puolitushaku miljoonan kokoisesta vektorista vaatii enintään 20 vertailua. 2) Lausekkeiden n2 ja n2 + 5n + 3 arvot ovat varsin samaa suuruusluokkaa, kun n kasvaa  termi n2 peittoaa ja jättää vähitellen varjoonsa termien 5n + 3 merkityksen. 3) Kuutiolliset algoritmit ovat kelvollisia vielä muutaman tuhannen kokoisille syötteille. 4) Eksponentiaalisille algoritmeille jo noin 40 alkaa syötteen kokona olla siedettävyyden äärirajoilla! log2n n n2 n2 + 5n + 3 n3 2n 1 9 2 3.32 10 100 153 1 000 1024 6.64 10 000 10 503 > 1030 9.97 13.29 16.61 19.93

58 3.2.1 Kompleksisuuden kertaluokat
3.2 Kompleksisuus 3.2.1 Kompleksisuuden kertaluokat Tarkastellaan seuraavaksi, minkä verran eri kokoisten syötteiden ratkeamiseen kuluu aikaa eri aikakompleksisuusluokkia edustavilta algoritmeilta, kun prosessorin nopeus on 1 MHz ( käskyä sekunnissa). Syötteen koko log2n n n log2n n2 n3 2n 10 3s 10 s 30 s 100 s 1 ms 100 7 s 700 s 10 ms 1 s 4*1022 v 1 000 17 min Kauan! 10 000 13 s 130 ms 1 min 40 s 12 pv 17 s 100 ms 1.7 s 2.8 h 31.7 v 20s 20 s v 23 s 10 s 4 min 3.17 v v 27 s 44 min 317 v v 8.3 h v

59 3.2 Kompleksisuus 3.2.2 Hajota ja hallitse
Hajota ja hallitse (lat. divide et impera) on tekniikka, jota sovelletaan muun muassa yhteen tunnettuun lajittelumenetelmään: limityslajitteluun. Menetelmän ajatuksena on halkaista alkuperäinen syöte kahtia niin kauan kuin halkaistavaa riittää Tämän jälkeen järjestetään aluksi kaikki yhden kokoiset ositteet pareittain järjestykseen suorittamalla niiden limitys. Mikäli alkuperäisen syötteen koko n on kakkosen potenssi, on tällaisia pareja yhteensä n/2 kappaletta.  saadaan tulokseksi n/2 kahden pituista järjestettyä listaa. Seuraavaksi limitetään pareittain kyseiset n/2 listaa, ja saadaan yhteensä n/4 järjestettyä listaa, joiden kunkin pituus on 4. Tällä tavoin jatketaan, kunnes lopulta on jäljellä enää kaksi n/2:n mittaista järjestettyä listaa, jotka limitetään yhdeksi n:n mittaiseksi järjestetyksi listaksi  lajittelu on tämän jälkeen valmis. Tarkastellaan lajittelun etenemistä aiemmin käsitellylle nimilistalle, johon otetaan mukaan vielä kahdeksas nimi, vaikkapa Urpo, jotta listan pituus olisi kakkosen potenssi (23 = 8). Tällöin esimerkin ymmärtäminen yksinkertaistuu. Alkutilanne: L = Jussi, Kati, Fredi, Bertta, Sami, Jaana, Mari, Urpo  Halkaistaan lista keskeltä: muodostuu kaksi alkuperäisen listan L alilistaa Tilanne 1. halkaisun jälkeen: L1 = Jussi, Kati, Fredi, Bertta L2 = Sami, Jaana, Mari, Urpo

60 3.2 Kompleksisuus 3.2.2 Hajota ja hallitse
 Halkaistaan molemmat alilistat L1 ja L2 keskeltä: muodostuu neljä alkuperäisen listan L alilistaa Tilanne 2. halkaisun jälkeen: L11 = Jussi, Kati L12 = Fredi, Bertta L21 = Sami, Jaana L22 = Mari, Urpo  Halkaistaan kaikki alilistat L11, L12, L21 ja L22 keskeltä: saadaan kahdeksan listan L alilistaa Tilanne 3. halkaisun jälkeen: L111 = Jussi, L112 = Kati, L121 = Fredi, L122 = Bertta, L211 = Sami, L212 = Jaana, L221 = Mari, L222 = Urpo  Nyt kaikki alilistat ovat vain yhden mittaisia: enää ei jatketa halkaisuja, vaan lajitellaan viime halkaisussa muodostuneet alilistat pareittain aakkosjärjestykseen limittämällä (otetaan nimi aina sen alilistan alusta, jossa sijaitsee aakkosissa aikaisemmin oleva nimi): L111 ja L112  uusi L11 = Jussi, Kati L121 ja L122  uusi L12: Bertta, Fredi (tehtiin vaihto) L121 ja L122  uusi L21 = Jaana, Sami (tehtiin vaihto) L221 ja L222  uusi L22: Mari, Urpo  seuraavaksi limitetään pareittain jo järjestyksessä olevat L11 ja L12 sekä L21 ja L22: L11 ja L12  uusi L1 = Bertta, Fredi, Jussi, Kati L21 ja L22  uusi L2 = Jaana, Mari, Sami, Urpo  lopuksi limitetään alilistat L1 ja L2: saadaan lopputulokseksi lajiteltu nimilista L: L = Bertta, Fredi, Jaana, Jussi, Kati, Mari, Sami, Urpo

61 3.2 Kompleksisuus 3.2.2 Hajota ja hallitse
 Algoritmin analyysi: Halkaisuja joudutaan tekemään niin kauan, kunnes jokainen ositteista on pienentynyt yhden mittaiseksi. Jokaista halkaisutasoa kohti lisäksi sille muodostuneet alilistat limitetään. Siten ajantarvetta kuvaa yhtälö: T(n) = T(n/2) (1. aliosite) + T(n/2) (2. aliosite) + n (näiden limitys) = 2T(n/2) + n Kyseessä on rekursioyhtälö, jonka mukaan n:n kokoisen syötteen ratkeamiseen menee aikaa n yksikköä + kaksinkertaisesti se työmäärä, joka tarvitaan puolta lyhyemmän syötteen ratkaisemiseen eli 2T(n/2) Vastaavasti T(n/2) voidaan lausua edellisen kaavan perusteella (termi n korvataan joka paikasta termillä n/2) T(n/2) = 2T(n/4) + n/2, jolloin T(n) = 2[2T(n/4) + n/2] + n = 4T(n/4) + 2n Halkaiseminen lopetetaan, kun niitä on tehty log2n kappaletta (esimerkissämme 3). Koska joka tasolla tehdään n:n alkion limitys, saadaan algoritmin kokonaistyömääräksi n log2n  limityslajittelu on huomattavasti aiemmin esitettyä kuplalajittelua (n2) tehokkaampi!

62 3.2.3 Hanoin tornien ongelma
3.2 Kompleksisuus 3.2.3 Hanoin tornien ongelma Hanoin tornit on esimerkki ongelmasta, jolle löytyy helposti oikeelliseksi ymmärrettävä algoritmi, mutta jonka ratkaiseminen on erittäin hidas prosessi, kun syötteen koon n annetaan kasvaa. Tarkastellaan asiaa luennolla esimerkin valossa.


Lataa ppt "Johdatus Informaatioteknologiaan II Turun yliopisto, Informaatioteknologian laitos, periodi 1 / 2011 Lasse Bergroth."

Samankaltaiset esitykset


Iklan oleh Google