JohdatusInformaatiot..

Transcription

JohdatusInformaatiot..
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