Johdatus Informaatioteknologiaan II Turun yliopisto, Informaatioteknologian laitos, periodi 1 / 2011 Lasse Bergroth Kurssin sisältö 2. Algoritmien suunnittelu (jatkoa kurssilta JIT1) 2.7 Rekursio ja iteraatio 2.7.1 Rekursio 2.7.2 Iteraatio 2.7.3 Rekursio vai iteraatio? 2.8 Tieto- ja tallennusrakenteet 2.8.1 Tyyppi, abstrakti tietotyyppi ja sen implementointi 2.8.2 Tallennusrakenteet 2.8.2.1 Tietue 2.8.2.2 Taulukko 2.8.2.3 Linkitetty rakenne 2.8.3 Abstraktit tietotyypit 2.8.3.1 Lista 2.8.3.2 Listan toteutus 2.8.3.3 Puu 2.8.3.4 Binääripuut 2.8.3.5 Binääripuun toteutus 2.8.3.6 Graafi 2.8.3.7 Graafin toteutus 2.8.4 Abstraktien tietotyyppien implementoinnista 2.8.5 Oliokeskeinen ohjelmointi Kurssin sisältö 3. Algoritmiteoriaa 3.1 Tehtävän algoritminen laskettavuus 3.1.1 Laskettavuus 3.1.2 Church – Turingin teesit 3.1.3 Pysähtymisongelma ja muita algoritmisesti ratkeamattomia ongelmia 3.1.4 Yhteenveto laskettavuudesta 3.2 Kompleksisuus 3.2.1 Kompleksisuuden kertaluokat 3.2.2 Hajota ja hallitse 3.2.3 Hanoin tornien ongelma 3.2.4 Kelvottomia ongelmia 3.2.5 P ja NP 3.3 Oikeellisuus 3.3.1 Testaus 3.3.2 Oikeaksitodistaminen Kurssin sisältö 4. Tietokoneen rakenne ja toiminta 4.1 Matemaattiset perusteet 4.1.1 Lukujärjestelmät 4.1.2 Kokonaislukujen esittäminen 4.1.3 Liukulukujen esittäminen 4.1.4 Boolen algebra 4.2 Fysikaaliset perusteet 4.2.3 Loogiset piirit 4.3 Tietokoneen komponentteja 4.3.1 Yhteenlasku 4.3.2 Vähennyslasku 4.4 Mikro-ohjelmoitava tietokone 4.4.1 Mikro-ohjelmoitavan tietokoneen rakenne • • • 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. 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: MODULE maalaa(aita) IF aitaa maalaamatta THEN suorita laudan maalaus maalaa(loput aidasta) ENDIF ENDMODULE • Kahden ratkaisun selkein ero: iteratiivisessa ratkaisussa esiintyy toistolause, rekursiivisessa sen tilalla moduulin itsensä kutsu. 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: 1) Kun ongelman koko on tarpeeksi pieni, se ratkeaa triviaalisti ilman uutta rekursiivista kutsua. 2) 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. 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. 2.7.1 Rekursio Käynnistetään toinen aktivaatio moduulista KertomaRek, kun n = 2. 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 = 1. 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 = 0. 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. 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 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. 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. 2.7.2 Iteraatio • Jotta iteratiivinen ratkaisun etsiminen olisi mahdollista, pitää seuraavien kolmen reunaehdon toteutua: 1) Pitää olla käytettävissä laskennan aikana saavutettuja välituloksia. 2) Välituloksista pitää pystyä etenemään kohti ratkaisua. 3) Täytyy kyetä tunnistamaan ratkaisun löytyminen. • Esimerkki: suorituksen eteneminen kuplalajittelualgoritmissa (vrt. aliluku 2.5.4.) 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) 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 REPEAT S := S – {m} (* poistetaan alkio m joukosta S *) m := m + b UNTIL m > n UNTIL b > neliöjuuri(n) RETURN A ∪ S ENDMODULE 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). 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: 1) Se määrittää, mitä laillisia arvoja sitä edustava muuttuja (tai vakio) voi saada. 2) Lisäksi se rajaa operaatiot, joita kyseisen tyyppisiin alkioihin voidaan kohdistaa. 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ä 1) tarkastellaan ainoastaan mallin mukaisten alkioiden oleellisia piirteitä 2) malli on riippumaton ohjelmointikielestä ja 3) alkiot kuvataan niiden käyttäytymisen avulla (tallennusrakenne peitetään) 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 2) tietueen yksittäisen kentän arvon käyttäminen 3) tietueen kopioiminen 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) 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ä 01.00 – 24.00: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 6 5 3 3 1 1 2 4 6 9 1 1 1 1 2 2 2 2 1 1 1 1 1 8 3 5 7 9 1 2 1 0 8 7 6 3 1 • 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. 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] 2.8.2 Tallennusrakenteet 2.8.2.2 Taulukko • Taulukolle sallittuja operaatioita ovat: 1) tietoalkion tallennus taulukkoon indeksin osoittamaan kohtaan 2) taulukon yksittäisen tietoalkion käyttö 3) 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 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 Iisalmi 0 254 85 174 136 223 Jyväskylä 254 0 169 80 118 129 Kuopio 85 169 0 89 51 138 Pieksämäki 174 80 89 0 38 49 Suonenjoki 136 118 51 38 0 87 Varkaus 223 129 138 49 87 0 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 := 100000 (* 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. 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 := 0 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. 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. 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ä alkuja 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ä. 2.8.2 Tallennusrakenteet 2.8.2.3 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. 2.8.3.1 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. 2.8.3 Abstraktit tietotyypit 2.8.3.1 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 1) tyhjä lista tai 2) 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) 2.8.3 Abstraktit tietotyypit 2.8.3.1 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) 2.8.3 Abstraktit tietotyypit 2.8.3.2 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ä 2) 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). 2.8.3 Abstraktit tietotyypit 2.8.3.2 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: 1) Mikä alkio lisätään? 2) Minne? 3) 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). 2.8.3 Abstraktit tietotyypit 2.8.3.3 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 0. 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. 2.8.3 Abstraktit tietotyypit 2.8.3.3 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: 1) tyhjä puu, jossa ei ole lainkaan solmuja, tai 2) 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 2.8.3 Abstraktit tietotyypit 2.8.3.4 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: 1) esijärjestys: solmussa vieraillaan ennen sen poikapuiden solmuja 2) välijärjestys: solmussa vieraillaan heti, kun sen koko vasen poikapuu on tutkittu 3) 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 2.8.3 Abstraktit tietotyypit 2.8.3.4 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 1) p on tyhjä tai 2) p on ei-tyhjä, ja seuraavat ehdot ovat voimassa (oletetaan, että kaikki puuhun talletetut arvot ovat keskenään erisuuria): 2.1 p:n juuren arvo > p:n vasemman poikapuun jokaisen solmun arvo 2.2 p:n juuren arvo < p:n oikean poikapuun jokaisen solmun arvo 2.3 p:n vasen ja oikea poikapuu ovat järjestettyjä binääripuita • Esimerkkejä järjestetyistä binääripuista: Jussi 18 12 Fredi 36 24 42 30 Bertta Kati Jaana Sami Mari 2.8.3 Abstraktit tietotyypit 2.8.3.4 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 2.8.3 Abstraktit tietotyypit 2.8.3.4 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 2.8.3 Abstraktit tietotyypit 2.8.3.4 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 2) lisätään viimeksi kohdattu solmu listan l loppuun 3) 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. 2.8.3 Abstraktit tietotyypit 2.8.3.5 Binääripuun toteutus • Binääripuu voidaan toteuttaa linkitettynä rakenteena, jossa jokaiseen solmuun tallennetaan tietokenttien arvo(je)n lisäksi osoittimet vasempaan ja oikeaan poikapuuhun. Datakenttä 1 … Datakenttä n Osoitin vasempaan poikaan Osoitin oikeaan poikaan • 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! 2.8.3 Abstraktit tietotyypit 2.8.3.6 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 138 38 80 49 Jyväskylä Pieksämäki Varkaus 2.8.3 Abstraktit tietotyypit 2.8.3.6 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 Iisalmi 0 254 85 174 136 223 Jyväskylä 254 0 169 80 118 129 Kuopio 85 169 0 89 51 138 Pieksämäki 174 80 89 0 38 49 Suonenjoki 136 118 51 38 0 87 Varkaus 223 129 138 49 87 0 2.8.3 Abstraktit tietotyypit 2.8.3.6 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). 2.8.3 Abstraktit tietotyypit 2.8.3.7 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 Iisalmi 0 0 1 0 0 0 Jyväskylä 0 0 0 1 0 0 Kuopio 1 0 0 0 1 1 Pieksämäki 0 1 0 0 1 1 Suonenjoki 0 0 1 1 0 0 Varkaus 0 0 1 1 0 0 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! 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 1) ei ole vielä keksitty algoritmista ratkaisua tai 2) voidaan todistaa, ettei niille koskaan edes tule löytymään algoritmista ratkaisua! 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ä! 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 1930- 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. 2) 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”. 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ä. 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 = 0 ELSE RETURN kyllä ENDIF ENDMODULE 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). 2) 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ä”. 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? 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ö. 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 2) Eukleideen algoritmilla: syt(x, y) = syt(y, x MOD y), jos x > 0 ja y > 0 x, jos x > 0 ja y = 0 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! 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ä 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 := 1 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ä 1 + 2 + … + 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 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). 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 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 log2n n n2 n2 + 5n + 3 n3 2n 0 1 1 9 1 2 3.32 10 100 153 1 000 1024 6.64 100 10 000 10 503 1 000 000 > 1030 9.97 1 000 1 000 000 1 005 003 1 000 000 000 … 13.29 10 000 100 000 000 100 050 003 1 000 000 000 000 … 16.61 100 000 10 000 000 000 10 000 500 003 … … 19.93 1 000 000 1 000 000 000 000 1 000 005 000 003 … … • Havaintoja: 1) 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! 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 (1 000 000 käskyä sekunnissa). Syötteen koko log2n n n log2n n2 n3 2n 10 3 µs 10 µs 30 µs 100 µs 1 ms 1 ms 100 7 µs 100 µs 700 µs 10 ms 1s 4*1016 v 1 000 10 µs 1 ms 10 ms 1s 17 min Kauan! 10 000 13 µs 10 ms 130 ms 1 min 40 s 12 pv … 100 000 17 µs 100 ms 1.7 s 2.8 h 31.7 v … 1 000 000 20 µs 1s 20 s 12 pv 31 710 v … 10 000 000 23 µs 10 s 4 min 3.17 v 31 710 000 v … 100 000 000 27 µs 1 min 40 s 44 min 317 v 31 710 000 000 v … 1 000 000 000 30 µs 17 min 8.3 h 31 710 v 31 710 000 000 000 v … 3.2 Kompleksisuus 3.2.1 Kompleksisuuden kertaluokat • Algoritmin kompleksisuuden voidaan sitä kuvaavan suoritusaikalausekkeen perusteella todeta kuuluvan tiettyyn kertaluokkaan, joita ovat seuraavat: 1) vakioaikainen, eli T(n) = 1 algoritmin suoritusaika ei ole lainkaan riippuvainen syötteen koosta (esimerkiksi uuden alkion lisääminen pinoon: ei ole väliä, paljonko pinossa on alkioita ennen lisäystä) 2) logaritminen, eli T(n) = log2n suoritusaika kasvaa hyvin hitaasti syötteen koon funktiona (esimerkiksi alkion etsiminen ns. puolitushaulla järjestetystä vektorista) 3) lineaarinen, eli T(n) = n suoritusaika on suoraan verrannollinen syötteen kokoon (esimerkiksi alkion etsintä järjestämättömästä vektorista) 4) suuruusluokka T(n) = n log2n tähän ryhmään kuuluvat mm. parhaat yleiskäyttöiset lajittelumenetelmät 5) polynomiaalinen, eli T(n) = nc, missä c > 1 muun muassa yksinkertaiset lajittelualgoritmit kuuluvat tähän ryhmään 6) eksponentiaalinen, eli T(n) = cn, missä c > 1 muun muassa Hanoin tornit ja useita graafi- ja tekoälyongelmia nämä ongelmat ovat yleisessä tapauksessa kelvottomia 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 rekursiivisesti 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 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 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öä (limityksen kustannus) + 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 paikassa 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 = … = 2iT(1) + i*n i = log2n Halkaiseminen lopetetaan, kun niitä on tehty log2n kappaletta (esimerkissä 3). Koska voidaan olettaa, että 1 alkion lajittelu vie vakioajan – eli T(1) = Ο(1) – ja joka tasolla tehdään n:n alkion limitys, saadaan algoritmin kokonaistyömääräksi Ο(n log2n). Huomaa, että 2i = 2log2n = n. limityslajittelu on huomattavasti aiemmin esitettyä kuplalajittelua Ο(n2) asymptoottisesti tehokkaampi (itse asiassa yksi tehokkaimmista)! 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. • Tarinan (yhden version) mukaan takaintialaisen temppelin katolla on pystyssä kolme timanttineulaa, joista yhdessä on 64 kappaletta metallilevyjä. Muissa kahdessa timanttineulassa ei ole mitään. • Kaikki levyt ovat halkaisijaltaan eri kokoisia, ja ne sijaitsevat neulassa 1 päällekkäin ylhäältä alaspäin kasvavassa järjestyksessä koon mukaan (ylimpänä on pienin ja alimpana suurin levy). Levyt muodostavat siten ylhäältä päin kapenevan tornimaisen asetelman. • Tarkoituksena olisi, että temppelin papit siirtäisivät neulassa 1 olevat levyt samaiseen järjestykseen neulaan 2, ja siirtojen toteuttamiseksi voidaan käyttää apuna kaikkia kolmea neulaa. • Jokaisen siirron pitää täyttää seuraavat vaatimukset: 1) Ainoastaan yhtä – neulassa kulloinkin päällimmäisenä olevaa levyä – saa siirtää (vrt. pino). 2) Milloinkaan ei saa asettaa isompaa levyä pienemmän päälle yhdessäkään neuloista. • Samaisen tarinan version mukaan maailmanloppu koittaa, kun papit saavat työnsä valmiiksi. ilmeisestikään kyseessä ei ole mikään yksinkertainen tehtävä, vaikka tilanteen kuvaus ja ”pelisäännöt” ovat helposti ymmärrettäviä • Lähdetään analysoimaan tilanteen kehittymistä merkitsemällä n:llä metallilevyjen lukumäärää ja lähtemällä liikkeelle pienistä n:n arvoista. Käytettävät kolme neulaa voitaisiin nimetä niiden merkityksensä perusteella vaikkapa seuraavasti: 1 = lähtö, 2 = maali ja 3 = apu. • Levyt nimetään järjestyksessä pienimmästä suurimpaan numeroin 1, 2, … n. • Hahmotellaan tapauksia pienillä n:n arvoilla luennolla graafisesti. 3.2 Kompleksisuus 3.2.3 Hanoin tornien ongelma n = 0: Aloitetaan helposta tapauksesta ei tarvitse tehdä mitään, vastaus on valmiina monesti rekursiivisten tehtävien ymmärtämistä helpottaa lähteminen liikkeelle (hyvin) yksinkertaisesta tapauksesta, joka ratkeaa itsestään ja kelpaa ongelman perustapaukseksi tarvittavien siirtojen kokonaismäärä: 0 siirtoa n = 1: Lähtötolpassa on vain yksi levy, joka voidaan siirtää suoraan maalitolppaan. Tämän jälkeen tehtävä on loppuun käsitelty. tarvittavien siirtojen kokonaismäärä: 1 siirto n = 2: Lähtötolpassa on kaksi levyä. Ylin levy voitaisiin siirtää joko maali- tai apuneulaan. Koska kuitenkin suurempi levy tarvitsisi saada pohjimmaiseksi maalineulaan, viedään siirrossa 1 pienempi levyistä lähtöneulasta apuneulaan, siirrossa 2 suurempi levy lähtöneulasta maalineulaan ja siirrossa 3 vielä apuneulassa oleva pienempi levy maalineulaan. tarvittavien siirtojen kokonaismäärä: 3 siirtoa vähemmin siirroin ei tehtävää saada suoritettua annetuin pelisäännöin n = 3: Lähtötolpassa on kolme levyä. Nytkin ylin levy voitaisiin siirtää joko maali- tai apuneulaan. Kumpaan se siis siirretään tällä kertaa? Siirretään se enempiä ajattelematta siirrossa 1 aputolppaan kuten tapauksessa n = 2. Ellemme halua heti perään jatkaa saman levyn siirtämistä (ja toisin sanoen: katua äskeistä siirtoa), ainoa tapa työn 3.2 Kompleksisuus 3.2.3 Hanoin tornien ongelma jatkamiseksi on siirtää levy 2 lähtöneulasta maalineulaan. Siirrolla 3 voitaisiin nyt viedä pienin levy 1 päällimmäiseksi maalineulaan, jolloin lähtötolpassa yhä alkuperäisellä paikallaan oleva suurin levy 3 voitaisiin siirtää tyhjään neulaan. Harmillisesti kuitenkin tyhjänä on apu- eikä maalineula, jonne se haluttaisiin siirtää … . joutuisimme siirtämään maalitolpassa olevat 2 pienintä levyä vielä aputolppaan, jotta maalitolppa tyhjenisi ja levy 3 voitaisiin siirtää lähtötolpasta lopulliselle paikalleen. Tähän tarvittaisiin kolme lisäsiirtoa jo tehtyjen kolmen lisäksi. Emme siis toimineetkaan järkevästi aluksi, kun siirsimme levyn 1 aputolppaan. Lähdetään liikkeelle uusiksi alusta, ja tehdään järjestyksessä seuraavat kolme siirtoa: 1) levy 1 lähtötolpasta maalitolppaan, 2) levy 2 lähtötolpasta aputolppaan, 3) levy 1 maalitolpasta aputolppaan. Nyt olemme saaneet tehokkaimmalla mahdollisella tavalla aikaan tilanteen, jossa maalitolppa on tyhjä ja lähtötolpassa on vain suurin levy. Siten siirretäänpä seuraavalla siirrolla 4) levy 3 lähtötolpasta maalitolppaan. Nyt lähtötolppa on tyhjä, maalitolpassa on suurin levy, ja levyt 1 – 2 ovat päällekkäin aputolpassa. Ne saadaan parhaiten maalitolppaan seuraavin siirroin: 5) levy 1 aputolpasta lähtötolppaan (myös lähtötolppaa sai käyttää apuna levyjen siirtelyyn), 6) levy 2 aputolpasta maalitolppaan, 7) levy 1 lähtötolpasta maalitolppaan tarvittavien siirtojen kokonaismäärä: 7 siirtoa 3.2 Kompleksisuus 3.2.3 Hanoin tornien ongelma • Paljonko tarvittaisiin siirtoja, jos n = 4? Emme lähde kuitenkaan ratkaisemaan enää kyseistä ongelmaa graafisesti, vaan tarkastellaan, mitä yhtäläisyyksiä voidaan havaita edellisissä ratkaisuissa. • Voidaan pienen päättelytyön tuloksena havaita, että 1) Suurin levy pystytään siirtämään lähtötolpasta tyhjään maalitolppaan tarkalleen silloin, kun kaikki tätä pienemmät levyt ovat päällekkäin aputolpassa. Tämä on sama asia kuin kooltaan n – 1 olevan ongelman ratkaiseminen sillä poikkeuksella, että ratkaisu viedäänkin aputolppaan maalitolpan sijasta! 2) Kun suurin levy on viety paikalleen, pitää aputolpassa olevat levyt siirtää tämän päälle maalitolppaan. Ratkaistaan vielä toistamiseen n – 1 levyn ongelma mutta siten, että tornin siirto tapahtuu aputolpasta maalitolppaan käyttämällä siirroissa apuna lähtötolppaa. • Toisin sanoen, haluttaessa ratkaista Hanoin tornien ongelma 4 levylle, pitää siirtää ensiksi levyt 1 – 3 lähtötolpasta aputolppaan (yhteensä 7 siirtoa aikaisemman analyysin perusteella), sitten levy 4 lähtötolpasta maalitolppaan (1 siirto) ja lopuksi levyt 1 – 3 aputolpasta maalitolppaan (uudet 7 siirtoa) kun n = 4, tarvitaan 7 + 1 + 7 = 15 siirtoa • Yleisesti, kun n on mielivaltainen positiivinen kokonaisluku, kuvaa siirtojen kokonaismäärää rekursioyhtälö T(n) = T(n – 1) + 1 + T(n – 1) = 2T(n – 1) + 1 • Sijoittamalla toistuvasti n – 1 n:n paikalle saadaan: T(n) = 2T(n – 1) + 1 = 4T(n – 2) + 2 + 1 = 8T(n – 3) + 4 + 2 + 1 = … = 2nT(0) + 2n – 1 + 2n – 2 + … + 21 + 20 3.2 Kompleksisuus 3.2.3 Hanoin tornien ongelma • Koska tyhjän tornin siirtämiseksi ei tarvinnut tehdä mitään, kustannustermi 2nT(0) = 0. Siten geometrisen sarjan summakaavaa hyväksi käyttämällä saadaan T(n) = 2nT(0) + 2n – 1 + 2n – 2 + … + 21 + 20 = 1 + 2 + 4 + … + 2n – 2 + 2n – 1 = 2n – 1 • Koska alun perin asetetussa ongelmassa n = 64, tarvitaan siirtoja yhteensä 264 – 1 kappaletta. Kyseinen luku on suurempi kuin 1019 eli 10 triljoonaa. Mikäli papit pystyisivät tekemään taukoamatta vaikkapa käsittämättömät kymmenen siirtoa sekunnissa, tehtävän suorittamiseen kuluisi yli 58 miljoonaa vuotta! mikäli Hanoin tornien ongelman taustalla oleva tarina pitäisi paikkansa, maailmanloppu ei tuntuisi olevan vielä kovinkaan lähellä … . • Jos siirtoja simuloitaisiin hyvin nopealla nykytietokoneella, jonka laskentateho olisi 2 000 000 000 alkeisoperaatiota (tässä tapauksessa: siirtoa) sekunnissa, eli sen kellotaajuus olisi luokkaa 2 GHz, kuluisi suoritukseen siltikin yli 292 vuotta … • … ja entäpä lähes hypoteettisella, 1 THz:n prosessorilla, joka laskisi 1 000 000 000 000 alkeisoperaatiota sekunnissa? vieläkin tarvittaisiin kärsivällisyyttä yli 7 kuukautta, jotta algoritmin suoritus päättyisi! • Selvästikin eksponentiaalisia algoritmeja voidaan käytännössä pitää kelvottomana. niitä voidaan soveltaa vain hyvin pienille syötteille. • Suoritusajallinen kelvottomuus ei kuitenkaan tee algoritmista välttämättä missään määrin hankalasti kirjoitettavaa. Esimerkiksi juuri Hanoin tornien ratkaisemiseksi voidaan kirjoittaa seuraava, erittäin suoraviivainen moduuli. 3.2 Kompleksisuus 3.2.3 Hanoin tornien ongelma MODULE Hanoi(n, Lähtö, Maali, Apu) IF n > 0 THEN Hanoi(n – 1, Lähtö, Apu, Maali) (* Ratkaistaan n – 1:n kokoinen ongelma aputolppaan. *) Siirrä levy numero n tolpasta Lähtö tolppaan Maali. Hanoi(n – 1, Apu, Maali, Lähtö) (* Siirretään aputolpassa olevat levyt maalitolppaan. *) ENDIF ENDMODULE • Algoritmissa triviaalina tapauksena on tyhjä torni, eli tilanne, jossa n = 0. tällöin ei tehdä yhtään mitään, vaan edellinen rekursiivinen kutsu jatkuu ja käynnistää myöhemmän rekursiivisista kutsuista. • Kannattaa huomioida, että tolppien roolit muuttuvat kesken algoritmin suorituksen riippuen siitä, mitkä ovat kutsun todelliset parametrit. Suoritettaessa ehtolauseen ensimmäistä rekursiivista kutsua kaksi jälkimmäistä todellista parametria vaihtavat paikkaa keskenään, ja myöhemmässä kutsussa vastaavasti ensimmäinen ja viimeinen todellinen parametri vaihtuvat päittäin. 3.2 Kompleksisuus 3.2.3 Hanoin tornien ongelma • Esimerkki: Kutsu Hanoi(3, Lähtö, Maali, Apu) etenisi seuraavasti: 1. Hanoi(3, Lähtö, Maali, Apu) 2. Hanoi(2, Lähtö, Apu, Maali) 3. Hanoi(1, Lähtö, Maali, Apu) 4. Hanoi(0, Lähtö, Apu, Maali) X Siirrä levy numero 1 tolpasta Lähtö tolppaan Maali. 5. Hanoi(0, Apu, Maali, Lähtö) X Siirrä levy numero 2 tolpasta Lähtö tolppaan Apu. 6. Hanoi(1, Maali, Apu, Lähtö) 7. Hanoi(0, Maali, Lähtö, Apu) X Siirrä levy numero 1 tolpasta Maali tolppaan Apu. 8. Hanoi(0, Lähtö, Apu, Maali) X Siirrä levy numero 3 tolpasta Lähtö tolppaan Maali. 9. Hanoi(2, Apu, Maali, Lähtö) 10. Hanoi(1, Apu, Lähtö, Maali) 11. Hanoi(0, Apu, Maali, Lähtö) X Siirrä levy numero 1 tolpasta Apu tolppaan Lähtö. 12. Hanoi(0, Maali, Lähtö, Apu) X Siirrä levy numero 2 tolpasta Apu tolppaan Maali. 13. Hanoi(1, Lähtö, Maali, Apu) 14. Hanoi(0, Lähtö, Apu, Maali) X Siirrä levy numero 1 tolpasta Lähtö tolppaan Maali 15. Hanoi(0, Apu, Maali, Lähtö) X 3.2 Kompleksisuus 3.2.4 Kelvottomia ongelmia • Edellä analysoidun Hanoin tornien ongelman lisäksi on olemassa muitakin kelvottomia ongelmia. Tällaisia ovat muun muassa: * erinäiset tekoälyongelmat, kuten šakin pelaaminen * pakkausongelmat, kuten k tavarakontin mahduttaminen n ominaisuuksiltaan erilaiseen kuorma-autoon * optimointiongelmat, kuten ns. kauppamatkustajan ongelma * muutamat graafialgoritmit, kuten mm. Hamiltonin syklin (matkan pituuden suhteen rajoittamaton versio kauppamatkustajan ongelmasta) etsintä * lukujärjestysongelma näistä tarkemmin kurssilla Algoritmien suunnittelu ja analysointi • Kelvottomien tehtävien kaikki tähän mennessä tunnetut tarkat ratkaisumenetelmät ovat ajankäytöltään eksponentiaalisia. • Näiden ongelmien ratkaisemiseksi joudutaan tarkan ratkaisun hitauden vuoksi tinkimään jonkin verran algoritmin oikeellisuusvaatimuksesta. Vaihtoehtoja ovat muun muassa: 1) Likimääräisalgoritmin soveltaminen, jolloin ongelma saattaa ratketa huomattavankin nopeasti, mutta välttämättä ei saavuteta tarkkaa tai optimaalista tulosta joidenkin päällekkäisyyksien hyväksyminen lukujärjestykseen šakkipelin siirtoihin reagoimiseksi vaadittavan laskennan keventäminen jne. 3.2 Kompleksisuus 3.2.4 Kelvottomia ongelmia 2) Turvautuminen todennäköisyysalgoritmiin, jolloin ollaan valmiita hyväksymään virheellinen lopputulos hyvin pienellä todennäköisyydellä (esimerkiksi ns. Monte Carlo -algoritmit alkulukutestiä varten). 3) Käyttämällä ratkaisun löytämiseksi heuristista algoritmia, jolloin ratkaisun uskotaan löytyvän jostain tietystä osasta laajaa hakuavaruutta (esimerkiksi ns. syvyyshaku laajasta graafista). jos arvaus osuu oikeaan, löydetään ratkaisu nopeasti muussa tapauksessa voi ratkaisu jäädä kokonaan löytymättä (oikealle hakupolulle ei päästä enää koskaan huonon arvauksen tapahduttua) 3.2.5 P ja NP • Merkinnällä P tarkoitetaan ongelmia, joille on olemassa deterministinen polynomiaalinen (tässä yhteydessä: ei-eksponentiaalinen) algoritmi. • Merkintä NP tarkoittaa puolestaan ongelmia, joille on olemassa epädeterministinen polynomiaalinen algoritmi, jonka löytämä ratkaisu on testattavissa deterministisellä algoritmilla polynomiaalisessa ajassa. Tähän ryhmään kuuluvat ongelmat ovat ns. NP-vaikeita. muun muassa pakkausongelma, kauppamatkustajan ongelma ja Hamiltonin sykli kuuluvat näihin • NP-täydellisiksi kutsutaan NP-vaikeita ongelmia, jotka voidaan muuntaa miksi tahansa toiseksi NPvaikeaksi ongelmaksi jos yksikin ratkeaa polynomiaalisessa ajassa, muutkin NP-vaikeat ratkeavat! • Ei ole pystytty todistamaan, että NP-vaikeat ongelmat ratkeaisivat polynomiaalisessa ajassa deterministisellä algoritmilla, mutta niitä ei ole todistettu kelvottomiksikaan. • Tietojenkäsittelytieteen yksi suurista avoimista kysymyksistä: Onko voimassa P = NP vai P ⊂ NP 3.3 Oikeellisuus • Ohjelmissa, myös päivittäin sovelluskäytössä pidettävissä, esiintyy virheitä verrattain usein. joudutaan käyttämään paljon työaikaa ohjelmien ylläpitoon ja virheellisten ohjelmien korjailuun • Vaikka ohjelma toimisikin useimmiten ongelmitta, harvemmin esiintyvien erikoistilanteiden käsittely saattaa aiheuttaa virheitä. • Syinä virheiden ilmenemiseen ovat muun muassa: * liian monimutkaisesti toteutetut algoritmit (ei modularisoitu ainakaan riittämiin) * puutteellinen tehtävän määrittely • Virheiden paikallistamista virheellisesti toimivaksi todetussa ohjelmassa kutsutaan debuggaukseksi. • Sen sijaan ohjelma(version) tai sen osan käyttöönottoa edeltää testaus, jossa selvitetään paitsi ohjelman tai sen osan virheettömyyttä niin myös sen vastaavuutta tehtyjen vaatimusmäärittelyjen kanssa. Testausta voidaan laajentaa koskemaan myös ohjelmiston käyttäjäystävällisyyttä, tehokkuutta, käyttökuormituksen sietoa, toimivuutta eri laiteympäristöissä ja niin edelleen. testauksesta tarkemmin erikoiskurssilla Ohjelmistotestaus • Useimmiten pelkkä testaaminen ei kuitenkaan riitä vakuuttamaan, että ohjelma olisi virheetön. Kriittisimmät moduulit on paitsi testattava, niin myös todistettava oikeiksi matemaattisesti. • Testauksen ja oikeaksi todistamisen yhteisenä tavoitteena on mahdollisimman niukalti virheitä sisältävien ohjelmien tuottaminen. 3.3 Oikeellisuus 3.3.1 Testaus • Testaus on yleisimmin käytetty keino vakuuttaa käyttäjä ohjelman oikeellisuudesta. • Voidaan suorittaa joko tietokoneella tai kynällä ja paperilla. ohjelmiston suunnittelija ja ohjelmoija käyttävät avuksi molempia menetelmiä, sillä he ovat perillä sen toteutuksesta ja yhteydestä muihin ohjelmiin käyttäjä suorittaa testausta pelkästään suorittamalla koneella testattavaa ohjelmaa, sillä hänellä ei normaalisti ole syvällisempää teknistä tietämystä ohjelmasta • Tyypillisimpiä virheitä ohjelmissa ovat: 1) Käyttövirheet: ohjelmaa käytetään johonkin muuhun tarkoitukseen kuin siihen, johon se on varsinaisesti tarkoitettu syynä näihin voi tosin olla myös puutteellinen tehtävän määrittely 2) Testausvirheet: ohjelma toimii jossain (erikois)tilanteessa väärin testauksen pitää olla riittävän kattava ja huomioida mahdollisuuksien mukaan testattavan moduulin kaikki haarautumiskohdat 3) Määrittelyvirheet: ohjelmaa suunniteltaessa ei ole varauduttu kaikkiin mahdollisiin tapauksiin, joita voi suorituksen aikana esiintyä ohjelman pitää pystyä ratkaisemaan myös triviaalit ja toisaalta myös epätavallisilta vaikuttavat syötteet • Kannattaa kuitenkin aina muistaa, että testaus VOI PALJASTAA ohjelmasta virheitä, mutta virheiden esiintymättömyys testauksessa EI TAKAA, että ohjelma olisi virheetön! 3.3 Oikeellisuus 3.3.2 Oikeaksitodistaminen • Moduulin oikeaksi todistaminen perustuu moduulin alku- ja loppuehtojen esittämiseen. • Alkuehdolla tarkoitetaan, minkä edellytysten pitää olla voimassa, jotta moduulia voidaan suorittaa. Esimerkki: jos moduuli laskee syötteenä annettavan positiivisen luvun neliöjuuren likiarvon, pitää huolehtia, että moduulia ei kutsuta negatiivinen luku syötteenä. • Loppuehto puolestaan kertoo, mitä vaatimuksia funktiomoduulin palauttama arvo tai proseduurin aikaansaama toiminta välttämättä täyttävät, kun moduulin kutsu ollut laillinen Esimerkki: alkioiden lajittelua suorittavan moduulin loppuehtona on, että kutsujalle palautetaan tarkalleen kaikki syötteessä annetut alkiot mutta siten, että on voimassa ehto a1 ≤ a2 ≤ a3 … ≤ an – 1 ≤ an, eli alkiot ovat ei-vähenevässä suuruusjärjestyksessä • Varsinainen oikeaksi todistaminen pyrkii siten osoittamaan, että missä tahansa laillisessa alkutilanteessa moduuliin saavuttaessa ollaankaan, moduulissa suoritettavat komennot johtavat loppuehdon täyttävään tilaan. • Oikeaksi todistaminen voi perustua joko induktioon, invariantteihin tai peräkkäisiin väitteisiin. • Yksinkertainen esimerkki: MODULE itseisarvo(a) RETURNS a:n itseisarvo {Alkuehto: a on mikä tahansa reaaliluku, merkitään a:n alkuarvoa a#:lla} IF a < 0 (* Syötteenä negatiivinen luku *) THEN a := -a ENDIF RETURN a {Loppuehto: a ≥ 0 JA (a = #a TAI a = -#a)} ENDMODULE 3.3 Oikeellisuus 3.3.2 Oikeaksitodistaminen • Oikeaksi todistamisessa käytetään usein apuna predikaattilogiikkaa. • Toistorakenteiden todistamisessa turvaudutaan ns. silmukkainvariantteihin. • Silmukkainvariantti on looginen väittämä, joka on voimassa aina, kun uusi kierros silmukassa on käynnistymässä. • Silmukkainvariantti voi hetkellisesti rikkoutua silmukan suorituksen keskellä, mutta sen on oltava voimassa jälleen uuden kierroksen alussa. • Kun silmukkainvariantti ei ole enää voimassa, pitää silmukan loppuehdon toteutua, jotta silmukan toiminta olisi oikeellinen. • Esimerkki: Alkion hakeminen listasta selauksella aloitusehtoisella toistolla i := 1 löytyi := epätosi WHILE i <= n AND NOT löytyi DO {Etsittyä alkiota ei ole löydetty, ja listasta on vielä tutkimatta paikat [i..n]} IF lista[i] = haettava alkio THEN löytyi := tosi ELSE i := i + 1 ENDIF ENDWHILE {Loppuehto: löytyi = tosi TAI i = n + 1} • Algoritmien oikeaksi todistamista tarkastellaan enemmälti kurssilla Ohjelmoinnin metodiikka. 4 Tietokoneen rakenne ja toiminta • Tämä varsin hyödyllisen ja mielenkiintoisen luvun käsittely joudutaan rajoittamaan vain muutamien keskeisimpien asioiden selvittämiseen. • Käsittely rajoittuu lähinnä lukujärjestelmiin, loogisiin piireihin ja mikro-ohjelmoitavan tietokoneen kursoriseen esittelyyn. • Muut osat jäävät lukijan oman mielenkiinnon varaan. 4.1 Matemaattiset perusteet • Tietokoneen toimintalogiikka perustuu pitkälti kahden olotilan periaatteelle. jokin ominaisuus joko on voimassa (1) tai ei (0). • Siten tietokoneissa käytetään yleisesti kaksi- eli binäärijärjestelmää, jossa numeroista ainoastaan 0 ja 1 ovat käytössä. • Tietokoneella esitettävän informaation pienin yksikkö on bitti, johon pystyy tallentamaan yhden 2-järjestelmän numeron yksi bitti riittää vaikkapa totuusarvon tallentamiseen koodattuna 0 = epätosi, 1 = tosi • Ohjelmien sisältö pitää jollain tavalla koodata binääriaakkostolle, jotta ohjelma voidaan suorittaa konekielisenä. Koodaus voi tapahtua joko tiedoittain, jolloin tiedon esitysmuodolla on tulkinta (numeerinen tieto) tai merkeittäin, kun tällaista tulkintaa ei ole (aakkosnumeerinen tieto) 4.1 Matemaattiset perusteet • Aakkosnumeerinen tieto koodataan merkeittäin käyttämällä jotain kooditaulua. • Yleisimmin käytettyjä kooditauluja ovat 7- ja 8-bittiset ASCII-koodit, 8-bittinen EBCDICkoodi sekä näitä huomattavasti laajempi Unicode. 7 bitin ASCII on riittämätön esimerkiksi skandinaavisten erikoismerkkien (mm. å, ä, ö, æ ja ø) esittämiseen, mutta ne mahtuvat 8 bitin ASCII-koodiin. Unicodea käyttämällä voidaan esittää lähes kaikkien maailman kirjoitettavien sisältämät symbolit. 4.1.1 Lukujärjestelmät • Luku on numeroista koostuva matemaattinen objekti, jolla on arvo. • Numero on puolestaan merkki, jota käytetään lukujen esittämiseen. • Lukujärjestelmässä, jonka kantalukuna on k, on käytössä numerot [0..k – 1]. Kymmenjärjestelmässä esiintyy numeroita [0..9] 2-järjestelmässä käytetään vain numeroita 0 ja 1 8-järjestelmässä (oktaalijärjestelmässä) käytettään numeroita [0..7] • Mikään ei estä määrittelemästä ja käyttämästä lukujärjestelmää, jonka kantaluku > 10. pitää vain sopia käytettävistä numerosymboleista esimerkiksi heksadesimaalijärjestelmässä k = 16, numerot [0..9, A, B, C, D, E, F] 4.1 Matemaattiset perusteet 4.1.1 Lukujärjestelmät • Luvut esitetään käytetystä lukujärjestelmästä riippumatta muodossa an-1kn-1 + an-2kn-2 + … + a1k + a0 + a-1k-1 + a-2k-2 + … a-m+1k-m+1 + a-mk-m • Ellei ole selvyyttä siitä, mikä lukujärjestelmä on käytössä, voidaan tämä merkitä näkyviin luvun oikeaan alakulmaan (esimerkiksi 2213 3-järjestelmän luku 221 = 2510) • Muunnos k-järjestelmästä 10-järjestelmään tapahtuu suoraviivaisesti korottamalla kantaluku k yhtä alempaan potenssiin kuin sen sijainti numeron lopusta lukien ja kertomalla se vastaavalla paikassa sijaitsevalla numerolla [0..k–1] jokaista luvun numeroa kohti. kokonaisluvun – tai liukuluvun kokonaisosan – viimeinen numero kuvaa kantaluvun nollannen potenssin kerrointa, toiseksi viimeinen termin k1 kerrointa jne., ja yleisesti luvun i. numero lopusta lukien edustaa termin ki-1 kerrointa. liukuluvussa pisteen jälkeinen i. numero tarkoittaa vastaavasti termin k-i kerrointa • Esimerkkejä k-järjestelmän lukujen muuntamisesta 10-järjestelmään: 110102 = 1*24 + 1*23 + 0*22 + 1*21 + 0*20 = 16 + 8 + 0 + 2 + 0 = 2610 201203 = 2*34 + 0*33 + 1*32 + 2*31 + 0*30 = 162 + 0 + 9 + 6 + 0 = 17710 E1A16 = 14*162 + 1*161 + 10*160 = 3584 + 16 + 10 = 361010 301.214 = 3*42 + 0*41 + 1*40 + 2*4-1 + 1*4-2 = 48 + 0 + 1 + 0.5 + 0.0625 = 49.562510 4.1 Matemaattiset perusteet 4.1.1 Lukujärjestelmät • 10-järjestelmän kokonaisluvun – tai liukuluvun kokonaisosan – x muuntaminen k-kantaiseen järjestelmään tapahtuu toistamalla seuraavassa mainittuja operaatioita niin kauan kuin x ≠ 0: 1. jaetaan x halutulla uudella kantaluvulla k 2. jakolaskun jakojäännös viedään muodostettavan k-järjestelmän luvun alkuun (ts. ensimmäisestä jakojäännöksestä tulee luvun k-järjestelmän mukaisen esityksen viimeinen numero) 3. tehdään jakolaskun osamäärästä uusi x Esimerkki: Muunnetaan 10-järjestelmän luku 3610 oktaalijärjestelmään (x = 3610, k = 8): Alkutilanne: x = 3610 ≠ 0 1. jakolasku: 3610 DIV 8 = 451, 3610 MOD 8 = 2 8-järjestelmän luvuksi alustavasti 2 Uusi x = 451 ≠ 0 2. jakolasku: 451 DIV 8 = 56, 451 MOD 8 = 3 8-järjestelmän luvun alkuun 3 32 Uusi x = 56 ≠ 0 3. jakolasku: 56 DIV 8 = 7, 56 MOD 8 = 0 8-järjestelmän luvun alkuun 0 032 Uusi x = 7 ≠ 0 4. jakolasku: 7 DIV 8 = 0, 7 MOD 8 = 7 8-järjestelmän luvun alkuun 7 7032 Uusi x = 0 = 0 muunnos 8-järjestelmään on valmis 4.1 Matemaattiset perusteet 4.1.1 Lukujärjestelmät • Mikäli halutaan muuntaa k-järjestelmän kokonaisluku (tai liukuluvun kokonaisosa) h-järjestelmään, missä sekä k, h ≠ 10, tapahtuu muunnos helpoiten 10-järjestelmän kautta. Esimerkki: Muunnetaan 11-järjestelmän luku 374 4-järjestelmään 1) Tehdään ensiksi muunnos 10-järjestelmään … : 37411 = 3*112 + 7*111 + 4*110 = 363 + 77 + 4 = 44410 2) … ja sitten 10-järjestelmästä 4-järjestelmään: 444 DIV 4 = 111, 444 MOD 4 = 0 111 DIV 4 = 27, 111 MOD 4 = 3 27 DIV 4 = 6, 27 MOD 4 = 3 6 DIV 4 = 1, 6 MOD 4 = 2 1 DIV 4 = 0, 1 MOD 4 = 1 Osamäärä 0, vastaus valmis. 4-järjestelmän luvuksi alustavasti 0 4-järjestelmän luvun alkuun 3 30 4-järjestelmän luvun alkuun 3 330 4-järjestelmän luvun alkuun 2 2330 4-järjestelmän luvun alkuun 1 12330 4.1 Matemaattiset perusteet 4.1.1 Lukujärjestelmät • 10-järjestelmän liukuluvun desimaaliosan x muuntaminen k-järjestelmään onnistuu puolestaan seuraavaa menettelyä käyttäen. Sitä jatketaan niin kauan, kunnes desimaaliosa menee nollaksi tai riittävä tarkkuus on saavutettu (oletetaan, että alkutilanteessa desimaaliosa ≠ 0, muutenhan kyseessä on kokonaisluvuksi kelpaava reaaliluku). 1. kerrotaan desimaaliosa x luvulla k 2. saadun tulon kokonaisosa lisätään nykyisen luvun loppuun (ensimmäisellä kierroksella heti kokonaisosaa seuraavan erotinmerkin eli pisteen jälkeen) 3. jos tulo on vähintään ykkösen suuruinen, vähennetään siitä kokonaisosa 4. jäljelle jääneestä desimaaliosasta tehdään uusi x Esimerkki: Muunnetaan 10-järjestelmän luku 123.703125 4-järjestelmään 1) Ensinnä kokonaisosa: 123 DIV 4 = 30, 123 MOD 4 = 3 4-järjestelmän luvuksi alustavasti 3 30 DIV 4 = 7, 30 MOD 4 = 2 4-järjestelmän luvun alkuun 2 23 7 DIV 4 = 1, 7 MOD 4 = 3 4-järjestelmän luvun alkuun 3 323 1 DIV 4 = 0, 1 MOD 4 = 1 4-järjestelmän luvun alkuun 1 1323 osamäärä 0, luvun kokonaisosan muunnos 4-järjestelmään valmis 4.1 Matemaattiset perusteet 4.1.1 Lukujärjestelmät 2) Sitten desimaaliosa: Alkutilanne: x = 0.703125 ≠ 0 1. kertolasku: 4x = 2.8125 lisätään kokonaisosa 2 luvun 1323. perään 1323.2 Vähennetään tulosta kokonaisosa: 2.8125 – 2 = 0.8125 Uusi x = 0.8125 ≠ 0 2. kertolasku: 4x = 3.25 lisätään kokonaisosa 3 luvun 1323.2 perään 1323.23 Vähennetään tulosta kokonaisosa: 3.25 – 3 = 0.25 Uusi x = 0.25 ≠ 0 3. kertolasku: 4x = 1.0 lisätään kokonaisosa 1 luvun 1323.23 perään 1323.231 Vähennetään tulosta kokonaisosa: 1.0 – 1 = 0.0 Uusi x = 0.0 = 0 Vastaus valmis, eli 123.70312510 = 1323.2314 • Kannattaa huomioida, että järjestelmän A mukainen tarkka liukuluku voi olla päättymätön järjestelmässä B ja päinvastoin. Tällöin sama jakso alkaa toistua. Kyseisessä tapauksessa pistettä seuraava osa katkaistaan, kun riittävä tarkkuus on saavutettu. Esimerkki: 150.510 = 12120.111111…3 desimaaliosa 0.5 alkaa toistua, kun sitä kerrotaan ensin kolmella ja vähennetään tulosta kokonaisosa eli ykkönen 21.13 = 7.333333…10 3-järjestelmän liukuluvut, jotka eivät ole tasalukuja, ovat kaikki päättymättömiä 10-järjestelmässä! 4.1 Matemaattiset perusteet 4.1.1 Lukujärjestelmät • Lisäksi kannattaa huomioida, että muunnos järjestelmien k ja kj välillä onnistuu helposti j numeron lohkoissa kerrallaan. Kokonaisosan viimeinen lohko päättyy juuri ennen desimaalierotinta, ja desimaaliosan ensimmäinen lohko alkaa heti pisteen jälkeen. Mahdolliset kokonaisosan etunollat jätetään asettamatta. • Esimerkki: 10110102 = 1|011|010 = 1328 Muunnos 2-järjestelmästä 8-järjestelmään (23 = 8) E1A16 = 1110|0001|10102 Muunnos 16-järjestelmästä 2-järjestelmään (16 = 24) 201203 = 2|01|20 = 2169 Muunnos 3-järjestelmästä 9-järjestelmään (32 = 9) 1033.2134 = 1|00|11|11.10|01|11 = 1001111.100111 (= 79.60937510) Muunnos 4-järjestelmästä 2-järjestelmään (4 = 22), kokonaisosan etunolla 14 012 jää asettamatta 4.1.2 Kokonaislukujen esittäminen • Edellisessä aliluvussa tarkasteltiin 2-järjestelmään kokonaisluvuista pelkästään ns. puhdasta binääriesitystä. • Tämä ei kuitenkaan riitä, sillä myös negatiiviset binääriluvut on pystyttävä esittämään jollain tavalla. myös luvun etumerkki on koodattava 2-järjestelmään käyttämällä symboleita 0 ja 1. • Tärkeimpiä vaihtoehtoja etumerkillisten kokonaislukujen esittämiseksi 2-järjestelmässä on neljä: 1) Etumerkki + itseisarvo -esitys 2) 1:n komplementti 3) 2:n komplementti ja 4) Excess-2n–1 -koodaus 4.1 Matemaattiset perusteet 4.1.2 Kokonaislukujen esittäminen • Etumerkki + itseisarvo -esitysmuoto on vaihtoehdoista yksinkertaisin. • Olettaen, että kokonaislukujen esittämiseen varataan n bittiä, käytetään niistä tässä esitysmuodossa ensimmäinen etumerkin ilmaisemiseen ja loput n – 1 bittiä luvun puhdasta binääriesitystä varten. aliluvun kaikissa seuraavissa esimerkeissä oletetaan, että n = 4. • Positiivisen luvun esitysmuoto on sama kuin sen puhdas binääriesitys, kun taas negatiivisen luvun tunnistaa luvun alkamisesta ykkösellä. • Tässä esitysmuodossa on kuitenkin laskennan kannalta kaksi pulmaa: 1) luvulle 0 on kaksi eri esitystä: 0000 (+0) ja 1000 (-0) 2) lukusuorat kulkevat nollasta katsottuna eri suuntiin (arvot lähtevät kasvamaan, liikuttiinpa nollasta poispäin kumpaan suuntaan tahansa) tästä syystä peruslaskutoimitusten suoritus ei onnistu: * kahden negatiivisen luvun summasta tulee positiivinen: -a – b a + b * jos toinen yhteenlaskettava (a) on negatiivinen ja toinen (b) on positiivinen, tulee tulokseksi a – b eikä a + b kuten pitäisi esimerkki: summan -2 + 3 laskenta allekkain: -2 = 1010 +3 = 0011 1101 = -510 4.1 Matemaattiset perusteet 4.1.2 Kokonaislukujen esittäminen • Yhden komplementti -esitysmuodossa positiivisten lukujen esitysmuoto on sama kuin niiden puhdas binääriesitys, kun taas negatiivinen luku saadaan komplementoimalla vastaavan positiivisen luvun bitit (nollat ykkösiksi ja ykköset nolliksi) • Nytkin negatiivisen luvun tunnistaa alussa olevasta ykkösestä, mutta lukujen kasvusuunta on 1:n komplementtia käytettäessä sama nollan molemmilla puolilla, eli negatiiviset luvut pienenevät lähestyttäessä nollaa, toisin kuin etumerkki + itseisarvo -esitystä käytettäessä. • Tässäkin esitysmuodossa on edelleen kaksi esitystä nollalle: 0000 (+0) ja 1111 (-0), mistä syystä yhteenlaskun tulos heittää vielä ykkösen verran. esimerkki: summan -2 + 3 laskenta allekkain: -2 = 1101 +3 = 0011 0000 = +0 = 010 (oikea tulos olisi tietystikin +1) • Tehdään vielä yksi korjaus: lisätään komplementoituun negatiiviseen lukuun ykkönen. Tällöin päästään ns. kahden komplementti -esitysmuotoon, jossa lukua -0 ei enää esiinny. samalla negatiivisen puolen lukualue laajenee yhdellä, eli n bitillä pystytään esittämään kokonaisluvut väliltä [-2n-1..2n-1-1]. • Koska nyt nollan esitys on yksikäsitteinen, ja lukusuorien kasvusuunnat ovat samat, pystytään peruslaskutoimitukset suorittamaan lopultakin kunnialla … 4.1 Matemaattiset perusteet 4.1.2 Kokonaislukujen esittäminen esimerkki: summan -2 + 3 laskenta allekkain: -2 = 1110 +3 = 0011 0001 = +1 = 110 (yhteenlaskun tulos on nyt oikein) • … vai iloitsimmeko sittenkin liian aikaisin? Lasketaanpa vaikka summa -4 + -7: -4 = 1100 -7 = 1001 0101 = +5 = 510 ≠ -1110 !? Eihän tässä näin pitänyt käydä! • Yhteenlasku -4 + -7 ei tuottanut oikeaa tulosta, mutta tälle on looginen selitys. Koska 4 bitillä voidaan esittää vain alueelle [-8..+7] kuuluvia kokonaislukuja, vastaus -11 menee ulos tältä alueelta. Lukualueen loppuessa kesken tapahtuu ns. ylivuoto. tarvittaisiin viides bitti, jotta tulos menisi oikein. Testataanpa mielenkiinnosta: -4 = 11100 -7 = 11001 10101 = -1110 Nyt toimii! • Kannattaa huomioida, että 4 bitillä saavutettu tulos ei kuitenkaan ole täysin sattumanvarainen, vaan tulos +5 on 2n:n eli tässä tapauksessa 16:n etäisyydellä oikeasta eli -11:stä (+5 – 16 = -11). ylivuototilanteessa saadaan tulos, joka on kongruentti oikean tuloksen kanssa modulo 2n, eli laillisen lukualueen loppuessa alhaalta (ylhäältä) kesken lähdetään ”uudelle kierrokselle” alueen vastakkaisesta reunasta! 4.1 Matemaattiset perusteet 4.1.2 Kokonaislukujen esittäminen • Ylivuoto paljastuu siten, että kahden positiivisen luvun summasta tulee negatiivinen tai kahden negatiivisen luvun summasta positiivinen. jos yhteenlaskettavat ovat erimerkkiset keskenään, ylivuotoa ei voi syntyä. •Kahden komplementtiesityksessä olevan negatiivisen luvun muunto vastaluvukseen tapahtuu samoin kuin positiivisen luvun vastaluvun muodostaminen, eli komplementoimalla bitit ja lisäämällä ykkönen. esimerkiksi 10101 (= -11) 01010 + 1 01011 = +11 • Myös kahden komplementtiesityksessä positiivisen luvun ja nollan esitysmuoto on sama kuin luvun puhdas binääriesitys. Negatiivisen luvun esitys puolestaan kertoo, miten kaukana luku on pienimmästä esitettävissä olevasta kokonaisluvusta, kun etumerkki jätetään huomiotta. esimerkiksi edellä nähty 1|0101 on 1012:n etäisyydellä luvusta -24 eli -16 siten luku on -11 luvun -2n–1 esitysmuoto on siten etumerkkibitti 1, jota seuraa n – 1 kappaletta nollia jos n = 4, luvun -8 esitys on 1000 • Excess-2n–1 -koodauksessa kokonaisluvut esitetään siten, että luvun arvo kertoo sen etäisyyden pienimmästä esitettävissä olevasta kokonaisluvusta -2n–1 , jonka esitysmuoto on n kappaletta nollia. negatiivisen luvun etumerkkiä edustaa alussa nolla ja ei-negatiivisen ykkönen. • Excess-koodauksen etuna on, että se säilyttää lukujen suuruusjärjestyksen puhtaina binääriesityksinä, toisin kuin kahden komplementti. • Aritmetiikka on excess-koodauksella kuitenkin hankalammin toteutettavissa. • Excess-koodausta käytetään mm. liukulukujen eksponentin arvon esittämiseen. • Esimerkki: Kokonaislukujen esittäminen, kun n = 4. Lukualue on siis 0...15 tai -8...+7. Luku Etumerkki + itseisarvo 1:n komplementti 2:n komplementti Excess-8 +7 0 111 0 111 0 111 1111 +6 0 110 0 110 0 110 1110 +5 0 101 0 101 0 101 1101 +4 0 100 0 100 0 100 1100 +3 0 011 0 011 0 011 1011 +2 0 010 0 010 0 010 1010 +1 0 001 0 001 0 001 1001 +0 0 000 0 000 0 000 1000 -0 1 000 1 111 ei ole ei ole -1 1 001 1 110 1 111 0111 -2 1 010 1 101 1 110 0110 -3 1 011 1 100 1 101 0101 -4 1 100 1 011 1 100 0100 -5 1 101 1 010 1 011 0011 -6 1 110 1 001 1 010 0010 -7 1 111 1 000 1 001 0001 -8 ei esitettävissä ei esitettävissä 1 000 0000 4.1 Matemaattiset perusteet 4.1.3 Liukulukujen esittäminen • Tietokoneella ei pystytä esittämään mielivaltaisia reaalilukuja, sillä niiden esittämiseen varattavien bittien määrää joudutaan väkisin rajoittamaan. • Tärkeimmät kaksi esitystapaa liukuluvuille ovat: 1) Kiinteän pisteen esitysmuoto, jossa kokonaisosaa seuraavan pisteen jälkeen on aina käytettävissä ennalta määritelty määrä bittejä ylimenevän osan esittämiseen 2) Liukulukuesitys, jossa binääripiste liu’utetaan sopivaan kohtaan siten, että luvut voidaan aina esittää samalla määrällä merkitseviä bittejä • Merkitsevät bitit esitetään ns. mantissana (m), joka yhdenmukaistetaan siirtämällä piste ensimmäisen ykkösen vasemmalle puolelle (mantissassa ei siten esiinny alkunollia) . Mantissalla ei ole etumerkkiä. mantissan arvoalueeksi tulee täten [0.5, 1). • Pisteen paikka ilmoitetaan eksponentin avulla. Se kertoo, paljonko pistettä kuuluu siirtää (skaalata) joko vasemmalle (eksponentti on positiivinen) tai oikealle (eksponentti on negatiivinen). • Alkuperäinen, esitettävä luku on muotoa m * 2k. • Esimerkki: 10.112 (= 2.7510) = 0.1011 * 22, joten mantissa on 1011 ja eksponentti 210 0.00112 (= 0.187510) = 0.11 * 2-2, joten mantissa on 11 ja eksponentti -210 • IEEE:n standardoimat liukulukujen esitystavat: 1) Lyhyet liukuluvut: 4 tavua = 32 bittiä 1 merkkibitti, eksponentti 8 bittiä (koodaus: excess-27), mantissan itseisarvo 23 bittiä esimerkki: -19.7187510 = 10011.101112 = 11000010110011101110000000000000 2) Pitkät eli ns. kaksoistarkkuuden liukuluvut: 8 tavua = 64 bittiä 1 merkkibitti, eksponentti 11 bittiä (koodaus: excess-210), mantissan itseisarvo 52 bittiä 4.1 Matemaattiset perusteet 4.1.4 Boolen algebra • Boolen algebraksi kutsutaan matemaattista struktuuria, joka koostuu luvuista 0 ja 1, yhteen- ja kertolaskuoperaatioista (+, *) sekä negaatiosta (⁻). tarvitaan suoritettaessa loogisia operaatioita arvo 0 tulkitaan totuusarvoksi epätosi ja 1 totuusarvoksi tosi. • Boolen algebraan kuuluvia laskulakeja tarvitaan mm. loogisten piirien toteuttamiseksi • Seuraavassa ovat esiteltyinä Boolen algebran laskulait: 0 + 0 = 0, 0 + 1 = 1, 1 + 1 = 1; 0 * 0 = 0, 0 * 1 = 0, 1 * 1 = 1, ⁻0 = 1, ⁻1 = 0 Liitäntä- eli assosiatiivilait: x + (y + z) = (x + y) + z, x(yz) = (xy)z kuten tavallisessa algebrassa Vaihdanta- eli kommutatiivilait: x + y = y + x xy = yx kuten tavallisessa algebrassa Osittelu- eli distributiivilait: x(y + z) = xy + xz x + yz = (x + y)(x + z) voimassa molemmin päin, toisin kuin tavallisessa algebrassa Nolla-, ykkös- ja vasta-alkiot: x + 0 = x, x + 1 = 1; x * 0 = 0, x * 1 = x; x + ⁻x = 1, x⁻x = 0 Absorptio- eli sisällyttämislait: x + xy = x, x(x + y) = x eivät voimassa tavallisessa algebrassa de Morganin lait: x + y = ⁻x⁻y xy = ⁻x + ⁻y eivät voimassa tavallisessa algebrassa 4.2 Fysikaaliset perusteet 4.2.3 Loogiset piirit • Monisteen aliluvut 4.2.1 (Puolijohteet) ja 4.2.2 (Puolijohdekomponentit) sivuutetaan luennoilla. • Näissä esitellään, mitä ominaisuuksia tietokoneen rakenneosina käytettävillä komponenteilla on ja mistä aineista ne koostuvat. • Loogisten piirien avulla pystytään konstruoimaan kytkentöjä, joiden tuottama tulos riippuu halutulla tavalla piirin saamista syötteistä. tällä tavoin muodostetaan esimerkiksi tietokoneen aritmeettis-loogisessa yksikössä sijaitsevat yhteenlaskupiirit. • Loogiset piirit kootaan ns. perusporteista tai -veräjistä, jotka saavat syötteekseen yhden tai useampia sisääntulosignaaleja ja tuottavat yhden tulosteen. • Perusveräjien totuusarvotaulut ja niiden symbolit esitetään seuraavassa: 1) EI-portti (NOT) • Ainoastaan yksi syöte: x • Tulos on syötteen negaatio eli päinvastainen kuin syötteen totuusarvo. • Kyseisen ominaisuutensa takia EI-portista käytetään myös nimityksiä signaalinkääntäjä ja invertteri. x NOT (⁻x) x tulos EI-portin symboli 0 1 1 0 4.2 Fysikaaliset perusteet 4.2.3 Loogiset piirit 2) JA-portti (AND) • Kaksi syötettä: x ja y • Tulos on tosi (1) ainoastaan silloin, kun molemmat syötteet ovat tilassa tosi. Muutoin tulos on 0. x y AND (xy) 0 0 0 0 1 0 1 0 0 1 1 1 x y tulos JA-portin symboli 3) TAI-portti (OR) • Kaksi syötettä: x ja y • Tulos on tosi, kun edes jompikumpi syötteistä x ja y on tosi. Jos molemmat ovat epätosia, tulos on 0. x y OR (x+y) 0 0 0 0 1 1 1 0 1 1 1 1 x y tulos TAI-portin symboli 4.2 Fysikaaliset perusteet 4.2.3 Loogiset piirit 4) EI-JA -portti (NAND) • Kaksi syötettä: x ja y • Tulos on tosi (1), kunhan molemmat syötteet eivät ole tilassa tosi. Jos kumpikin on tosi, tulos on 0. toimii päinvastoin kuin JA-portti! x y NAND (xy) 0 0 1 0 1 1 1 0 1 1 1 0 x y tulos EI-JA -portin symboli 5) EI-TAI -portti (NOR) • Kaksi syötettä: x ja y • Tulos on tosi, kun kumpikaan syötteistä x ja y ei ole tosi. Jos edes jompikumpi on tosi, tulos on 0. toimii päinvastoin kuin TAI-portti! x y NOR (x+y) 0 0 1 0 1 0 1 0 0 1 1 0 x y tulos EI-TAI -portin symboli 4.2 Fysikaaliset perusteet 4.2.3 Loogiset piirit 6) Poissulkeva TAI-portti (XOR) • Kaksi syötettä: x ja y • Tulos on tosi (1), kun tarkalleen yksi syöte on tilassa tosi. Jos ei kumpikaan ole tai molemmat, tulos on 0. x y XOR (x⊕ ⊕y) kutsutaan myös parittomuusveräjäksi, sillä tulos on 1, kun pariton määrä syötteitä on arvoltaan tosi 0 0 0 0 1 1 1 0 1 1 1 0 x y tulos Poissulkevan TAI-portin symboli 7) Ekvivalenssiportti (EQV) • Kaksi syötettä: x ja y • Tulos on tosi, kun syötteistä x ja y ovat tosia molemmat tai ei kumpikaan. Muutoin tulos on 0. x y EQV (x⊕ ⊕y) toimii päinvastoin kuin poissulkeva TAI-portti! kutsutaan myös parillisuusveräjäksi, sillä tulos on 1, 0 0 1 kun parillinen määrä syötteitä on arvoltaan tosi 0 1 0 1 0 0 1 1 1 x y tulos Ekvivalenssiportin symboli 4.2 Fysikaaliset perusteet 4.2.3 Loogiset piirit • Loogisia piirejä piirrettäessä kannattaa huomioida, että syötesignaalia voidaan monistaa usealle eri portille. Samoin yksittäisen portin tuottama tulossignaali voidaan tarvittaessa haaroittaa ja viedä toisten porttien syötteeksi. • Esimerkki: Halutaan toteuttaa kolmeen syöttösignaaliin perustuva looginen piiri, jolla on seuraava totuusarvotaulu. Syötteinä ovat signaalit x, y ja z, ja tulossignaalina on g. x y z g 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 1 • Periaatteessa pitäisi rakentaa piiri, joka toteuttaa ehdon ⁻xyz + x⁻y⁻z + x⁻yz + xy⁻z + xyz • Boolen algebraa hyväksi käyttämällä saadaan lauseke kuitenkin sievenemään muotoon x + yz. Taulukosta voidaan myös helposti nähdä, että sievennys pitää paikkansa (tarkastellaan luennolla). 4.2 Fysikaaliset perusteet 4.2.3 Loogiset piirit • Tulokseksi saadaan lopulta seuraavanlainen piiri, jossa kytketään peräkkäin yksi JA- ja yksi TAI-portti. x tulos x + yz y tulos yz z • Loogisista perusveräjistä (EI-porttia lukuun ottamatta) voidaan rakentaa myös useampia kuin kaksi syöttösignaalia hyväksyviä. 4.3 Tietokoneen komponentteja • Tietokone on periaatteessa looginen piiri, jossa on hyvin suuri määrä portteja. • Se on samalla modulaarinen kokonaisuus, joka koostuu monista komponenteista. • Seuraavassa on esiteltyinä komponenteista tärkeimmät: 1) Aritmetiikkayksikkö huolehtii aritmetiikan suorittamisesta monisteen yksinkertaisessa tietokoneessa kahden komplementtiesityksessä olevat luvut lasketaan yhteen ja vähennetään laitteistollisesti 4.3 Tietokoneen komponentteja kerto- ja jakolaskut suoritetaan hyvin matalan tason ohjelmilla korkeamman tason laskenta suoritetaan konekielellä tai korkeamman tason ohjelmilla 2) Muisti tietojen automaattisen käsittelyn kannalta välttämätön komponentti, jotta laskettuja tuloksia pystytään varastoimaan ja jotteivät ne häviäisi käytettävistä, kun niitä yhä tarvitaan yksinkertaisin muistipiiri on yhden bitin muistava kiikku (lue monisteesta aliluku 4.3.3) kokonaisen muistisanan eli useamman bitin muistavaa konstruktiota kutsutaan rekisteriksi (lue monisteesta aliluku 4.3.4) 3) Väylät tarvitaan tiedon siirtämiseen paikasta toiseen, eli muistin, prosessorin rekisterien ja aritmeettisloogisen yksikön välillä sekä oheislaitteiden kanssa kommunikointiin erilliset väylät datan, osoitetiedon ja ohjaus- eli kontrollitiedon välittämiseen 4) Loogiset vertailut mahdollistavat algoritmeissa käytettävien valinta- ja toistorakenteiden toteuttamisen hyvin matalalla tasolla voidaan testata ainoastaan, onko jonkin rekisterin kahden komplementtiesityksessä oleva sisältö nolla tai negatiivinen 5) Kello huolehtii koneessa suoritettavien toimintojen ajoittamisesta ja tahdistamisesta kellotaajuus eli kellosyklin ajallinen kesto kuvaa koneen suorituskykyä 4.3 Tietokoneen komponentteja 6) Oheislaitteet näihin kuuluvat mm. syöttö- ja tulostuslaitteet (näyttö, näppäimistö, hiiri, anturit yms.) sekä ulkoiset muistit tärkeitä, jotta kone pystyisi kommunikoimaan ulkomaailman kanssa 4.3.1 Yhteenlasku • Kuten jo edellä aliluvussa 4.1.2 ehdittiin todeta, kahden komplementtiesityksessä olevien lukujen yhteenlasku tapahtuu samankaltaisella algoritmilla kuin allekkain lasku kynällä ja paperilla. • Kahden bitin (x, y) yhteenlaskuun pystyvä piiri on nimeltään puolisummain. Se palauttaa tuloksinaan sekä sarakesumman (s) sekä muistinumeron (m). • Seuraavassa nähdään puolisummaimen totuusarvotaulu: x y m s 0 0 0 0 0 1 0 1 1 0 0 1 1 1 1 0 • Taulukosta havaitaan, että sarakesumma on 1 silloin, kun tarkalleen yksi syötteistä on ykkönen. siten sarakesumma noudattaa poissulkevan TAI-portin (XOR) totuusarvoja • Vastaavasti muistinumero esiintyy vain silloin, kun molemmat syötebitit ovat ykkösiä. muistinumeron toteuttaminen onnistuu JA-portin avulla 4.3 Tietokoneen komponentteja 4.3.1 Yhteenlasku Puolisummain x y x y s m ½ m s • Puolisummain pystyy selvästikin käsittelemään ainoastaan kaksi yksittäistä syötteen bittiä. • Jotta pystyttäisiin laskemaan useammasta bitistä koostuvia lukuja yhteen, pitää puolisummaimista konstruoida kokosummain, joka huolehtii paitsi yhteenlaskettavien pareittaisten bittien summaamisesta, niin myös muistinumeron kulkeutumisesta • Lisäksi tarvitaan erillinen tulostuslinja ylivuotobittiä varten. • Kannattaa huomioida, että yhteenlaskun lopputulos voi olla oikein, vaikka ylivuotobitti olisikin ykkönen. Näin käy esimerkiksi aina silloin, kun molemmat yhteenlaskettavat ovat negatiivisia. Tulos on virheellinen vain silloin, kun kaksi samanmerkkistä yhteenlaskettavaa tuottaa erimerkkisen summan, eli kahden positiiviluvun summa on negatiivinen tai kahden negatiivisen luvun summa on positiivinen. 4.3 Tietokoneen komponentteja 4.3.1 Yhteenlasku 4-bittinen kokosummain x2 y2 x1 y1 x3 y3 m z4 m (ylivuotobitti) m ½ ½ s z3 m m ½ ½ m ½ m ½ ½ s z2 x0 y0 s z1 s z0 4.3 Tietokoneen komponentteja 4.3.2 Vähennyslasku • Vähennyslasku voidaan muuntaa yhteenlaskuksi siten, että vähennyslaskun vähentäjä muutetaan vastaluvukseen. • Vastaluvuksi muunto tehdään syöttämällä vähentäjän kaikki bitit EI-portin läpi, ja lisäämällä alimpien bittien summaan ykkönen: näin saadaan aikaan vähentäjän kahden komplementtiesitys. Esimerkki: 3 – 7 = 3 + (-7): 0011 +3 , 0111 +7 1001 (-7) 0011 1001 1100 = -4 • Toinen vaihtoehto on esitellä vähennyslaskualgoritmin säilyttävä ns. kokovähennin konstruoimalla se puolivähentimistä. Puolivähentimessä on sisääntulot vähennettävälle ja vähentäjlle ja tuloslinjat sekä sarakkeen erotukselle (e) että lainausbitille (l). Seuraavassa puolivähentimen totuusarvotaulukko: x y l e 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 • Nähdään, että sarake-erotusta vastaa looginen operaatio x ⊕ y ja lainausta operaatio ⁻xy • Harjoitustehtävä: yritä rakentaa 4-bittinen kokovähennin puolivähentimien avulla! 4.4 Mikro-ohjelmoitava tietokone • Kurssin päätteeksi esitetään lyhyt katsaus pelkistetyn mikro-ohjelmoitavan tietokoneen toimintaan. • Samainen monisteen luku antaa melko selkeän kuvan siitä, miten primitiivisin operaatioin ohjelmoitujen algoritmien askeleet suoritetaan tietokoneella. käsiteltävä asia paikoitellen vaikeaa, mutta mielenkiintoista ja hyödyllistä kannattaa lukea läpi omalla ajalla! 4.4.1 Mikro-ohjelmoitavan tietokoneen rakenne • Esiteltävän tietokoneen sanakoko on 16 bittiä • Kone muodostuu rekistereistä, muisteista, yhteenlaskulaitteesta, väylistä sekä viisivaiheisesta kellosta, joka tahdistaa koneen toimintoja. Seuraavassa näiden lyhyt esittely: 1) Mikrokäskyrekisteri MIR (22-bittinen) sisältää kulloinkin suoritettavana olevan mikrokäskyn bittien (1-22) arvot ohjaavat, millä tavoin koneen muodostava monimutkainen looginen piiri toimii käskyä suoritettaessa 2) Mikro-ohjelmamuisti MPM (22-bittinen, osoiteavaruus 0 – 255) sisältää kaikkien niiden alkeisoperaatioiden toiminnot, jotka kone pystyy suorittamaan ainoastaan lukemista varten: muistin sisältö poltetaan lopulliseen muotoonsa konetta rakennettaessa 4.4 Mikro-ohjelmoitava tietokone 4.4.1 Mikro-ohjelmoitavan tietokoneen rakenne 3) Mikro-ohjelmalaskuri MPC (8-bittinen) ilmoittaa suoritettavana olevan käskyn osoitteen mikro-ohjelmamuistissa huolehtii suoritettavana olevan ohjelman kontrollista 4) Muistin datarekisteri MDR (16-bittinen) käytetään muistista haettavien ja sinne tallennettavien tietojen rekisterinä kaikki muistioperaatiot tapahtuvat MDR:n kautta 5) Päämuisti MM (16-bittinen, osoiteavaruus 0 – 4095) konekielisten ohjelmien ja niiden käsittelemän tiedon säilytyspaikka 6) Muistin osoiterekisteri MAR (12-bittinen) sisältää päämuistin osoitteen, johon seuraava tallennus- tai hakuoperaatio kohdistuu 7) Rekisterit A, B, C ja D (16-bittisiä) paraikaa prosessointia varten tarvittavan datan säilyttämiseen rekisterillä A (ns. akku) erikoisasema: sen arvoa voidaan testata, onko se nolla tai negatiivinen 4.4 Mikro-ohjelmoitava tietokone 4.4.1 Mikro-ohjelmoitavan tietokoneen rakenne 8) Aritmeettinen yksikkö huolehtii yhteenlaskusta (16-bittinen kokosummain) vähennyslaskut suoritetaan muuntamalla vähentäjä vastaluvukseen suorittaa yhteenlaskun tuloksen siirtämisen vasemmalle (vastaa kertomista kahdella) 9) Väylät kone sisältää tiedon siirtoa varten yhteensä 4 väylää * kolme 16-bittistä data-/osoiteväylää * yhden 22-bittisen ohjausväylän 10) Kello viisivaiheinen tahdistin sille, mitä koneessa minäkin ajanhetkenä tehdään * kello 1 määrätään, mitä lasketaan * kello 2 selvitetään, minne lasketut tulokset viedään * kello 3 suoritetaan joko tallennus muistiin tai haku muistista * kello 4 määräytyy seuraavaksi suoritettava käsky * kello 5 noudetaan kyseinen käsky mikro-ohjelmamuistista mikrokäskyn ohjausbittien merkitys löytyy luentomonisteesta • Viimeisellä kalvolla on näytetty edellä kuvatun mikro-ohjelmoitavan koneen rakennekaavio
© Copyright 2024