previous next Title Contents Index

4. Algoritmin suunnittelu


Mitä tässä luvussa käsitellään?

* mikä on algoritmi

* vertailu ja lajittelu

* algoritmin kompleksisuus

* alkion etsiminen joukosta Kun ohjelman suunnittelu on edennyt siihen pisteeseen, että tarvitaan yksityiskohtaisia algoritmeja, meneekin jo monella sormi suuhun.

Vaikeudet johtuvat taas liian hankalasta ajattelutavasta ja siitä, että algoritmi yritetään nähdä osana koko ohjelmaa. Tästä ajattelutavasta on luovuttava ja osattava määritellä tarvittava algoritmi omana kokonaisuutenaan, jota suunniteltaessa sitten unohdetaan kaikki muu.

4.1 Algoritmi

Algoritmi on se joukko toimenpiteitä, joilla annettu tehtävä saadaan suoritettua. Mieti esimerkiksi miten selostat kaverillesi ohjeet juna- asemalta opiskelu- boxiisi.

Voit tietysti antaa ohjeet myös muodossa "Tule osoitteeseen Ohjelmoijankuja 17 B 5". Tämäkin on varsin hyvä algoritmi. Kaverin vain oletetaan nyt osaavan enemmän. Kaverin oletetaan osaavan etsiä katuluettelosta kadun paikka ja keksivän itse menetelmän tulla asemalta sinne.

Toisaalta kaverisi saattaa hypätä taksiin ja sanoa kuskille osoitteen. Tämä on hyvä ja helppo algoritmi, mutta ehkä liian kallis opintovelkaiselle opiskelijalle. Mikäli algoritmia tarvitaan useasti, voidaan sitä myöhemmin parantaa tyyliin:

	-  kävele asemalta sinne ja sinne
	-  hyppää bussiin se ja se
	-  jne
Tarkennettu algoritmi voisi olla myös seuraavanlainen:
	Valitse seuraavista:
	1.  Kello 7- 20:
	     -  kävele kirkkopuistoon
	     -  nouse bussiin no 3 joka lähtee 15 yli ja 15 vaille
	2.  Sinulla on rahaa tai saat kimpan:
	     -  ota taksi
	3.  Ei rahaa tai haluat ulkoilla:
	     -  kävele
Edellä eri kohdat eivät ole toisiaan poissulkevia. Kello voi olla 9 ja rahaakin voi olla, mutta siitä huolimatta halutaan kävellä. Hyvässä algoritmissa ei saa olla tällaisia epätäsmällisyyksiä, vaan ohjelmoijan tulee etukäteen jo päättää mitä missäkin tapauksessa tehdään. Esimerkiksi:
	1.  Jos haluat ulkoilla, niin 
	     -  kävele.
	2.  Muuten jos kello 7- 20:
	     -  kävele kirkkopuistoon
	     -  nouse bussiin no 3 joka lähtee 15 yli ja 15 vaille
	3.  Muuten jos sinulla on rahaa tai saat kimpan:
	     -  ota taksi
	4.  Muuten
	     -  kävele
Tässäkin algoritmissa jää vielä kaverillekin tehtävää: Miten kävellään? Miten astutaan bussiin jne..

No tätä ei kaverille ehkä enää selostetakaan. Lapsille nämä asiat on aikanaan opetettu ja myöhemmin ne kuitataan yhdellä tai kahdella sanalla. Sama pätee ohjelmoinnissakin. Kerran tehtyä ei joka kerran pureksita uudelleen (vrt. aliohjelma)!

Tehtävä 4.8 Kävelyohjeet

Yritä kirjoittaa ohjeet siitä miten kävellään.
Kirjoita kaverillesi kävelyohjeet (missä käännytään, ei miten kävellään) rautatieasemalta asunnollesi.

4.2 Lajittelu

Kerhon jäsenrekisteriä suunniteltaessa tulee jossakin kohtaa vastaan tilanne, jossa nimet tai osoitteet pitää pystyä lajittelemaan jollakin tavalla.

4.2.1 Nimien ja numeroiden vertaus

Jos osaamme lajitella numeroita, niin osaammeko lajitella nimiä? Vastaus on KYLLÄ. Mikä numeroiden lajittelussa on oleellista? Oleellista on tietää onko numero A pienempi kuin numero B. Miten tämä sitten soveltuu nimille? Jos osaamme päättää onko nimi A aakkosissa ennenkuin nimi B, on ongelma ratkaistu.

Verrataanpa erilaisia nimiä:

	A: Kassinen Katto
	B: Ankka Aku
B on ensin aakkosissa. Miksi? Koska B:n ensimmäinen kirjain (A) on ennen nimen A ensimmäistä kirjainta (K).
	A: Kassinen Katto
	B: Karhukopla 701107
Nytkin B on ensin. Siis miten vertaamme kahta nimeä?
	Vertaamme nimiä merkki kerrallaan kunnes vastaan tulee eri- 
	suuret kirjaimet.  Kumpi erisuurista kirjaimista on aakko- 
	sissa ennen, määrää sen kumpi nimistä on aakkosissa ennen. 
Siinä meillä on algoritmi joka on varsin selvä. Jos algoritmi haluttaisiin vielä kirjoittaa "lausekieliseen"- muotoon, niin se olisi suurinpiirtein seuraavanlainen:
	1. siirry kummankin nimen ensimmäiseen kirjaimeen
	2. jos kummankin nimen viimeinen merkki on ohitettu, niin nimet ovat samat
	3. jos toisessa nimessä viimeinen merkki on ohitettu, niin se on ennen 
aakkosissa
4. verrataan vuorossa olevia kirjaimia kummastakin nimestä - jos samat, niin siirrytään seuraaviin kirjaimiin ja jatketaan kohdasta
2.
- jos erisuuret, niin se ensin aakkosissa, jonka kirjain on ensin
Tähän vielä pieni "viilaus enemmän strukturoidummaksi", niin meillä olisikin valmis (ali)ohjelma nimien vertaamiseksi.

4.2.2 Algoritmin sanallinen versio on kuvaavampi!

Vaikka esitimmekin algoritmin "lausekielisenä" kohdittain numeroituna, ei koskaan pidä unohtaa sitä ennen ollutta sanallista versiota, joka on selkeämpi kuvaus siitä ideasta, mitä tehdään!

Siis kirjoita aina ensin sanallinen kuvaava kuvaus algoritmista ja vasta sitten sen yksityiskohtainen "lausekielinen" versio!

4.2.3 Numeroiden järjestäminen

Näin ollen on aivan yksi lysti opettelemmeko järjestämään nimiä vai numeroita. Siksi paneudummekin seuraavassa numeroiden järjestämiseen. Kuulostaako vaikealta?

Otapa käteesi korttipakka ja ota sieltä esiin vaikkapa vain kaikki padat. Nyt sinulla on joukko "numeroita" (A=14, K=13, Q=12, J=11), yhteensä 13 kappaletta. Sekoita kortit! Yritä järjestää kortit suuruusjärjestykseen siten, ettet tarvitse pöytätilaa kuin yhden kortin verran, loput kortit pidät kädessäsi.

Millaisen algoritmin saat? Ehkäpä seuraavan (insertion sort):

	Pöydällä on lajiteltujen kasa.  Aluksi tietysti tyhjä.  Ota
	kädestäsi seuraava kortti ja laita pöydällä olevaan kasaan 
	omalle paikalleen.  Jatka kunnes kädessä ei enää kortteja. 
"Lausekielisenä":
	1. ota kädessä olevan kasan päällimmäinen kortti
	2. sijoita se pöydällä olevaan kasaan paikalleen
	3. mikäli kortteja vielä jäljellä, niin jatka kohdasta 1. 
Algoritmisi voi olla myös seuraava (selection sort):
	Etsitään aina pienin kortti ja laitetaan se pöydälle olevan 
	kasan päällimmäiseksi.  Jatketaan kunnes kädessä olevat 
	kortit on loppu. 
Eli "lausekielisenä":
	1.  etsi kädessäsi olevista korteista pienin
	2.  laita se pöydällä olevan pinon päällimmäiseksi
	3.  mikäli vielä kortteja jäljellä, niin jatka kohdasta 1. 

Tehtävä 4.9 Muita lajittelualgoritmeja

Mitä muita mahdollisia "lajittelumenetelmiä" keksit?
Siinä eräitä ratkaisuja tähän "hirveän vaikeaan" ongelmaan. Ratkaisuissa on tiettyjä huonoja puolia, mutta ne ovat todella yksinkertaisia ja jokaisen itse keksittävissä.

Tehtävä 4.10 Algoritmin kompleksisuus

Mikäli kahden kortin vertaaminen lasketaan yhdeksi "operaatioksi", niin kuinka monta "operaatiota" joudumme tekemään, jotta pakka on lajiteltu?
Edellisen tehtävän vastausta sanotaan algoritmin kompleksisuudeksi.

Tehtävä 4.11 Lajittelujärjestys

Edellinen algoritmi (selection sort) toimi siten, että kortit jäivät pöydälle suurin päällimmäiseksi. Miten algoritmia pitää muuttaa, jotta pienin saataisiin päällimmäiseksi?
Ei siis ole suurtakaan väliä pitääkö lajitella nouseva vai laskeva järjestys!

4.2.4 Kuplalajittelu

Kokeillaanpa vielä erästä algoritmia: Sotke kortit kädessäsi uudelleen.

Bubble sort:

	Vertaa aina kahta peräkkäistä korttia keskenään.  Mikäli ne
	ovat väärässä järjestyksessä, vaihda ne keskenään.  Kun koko
	pakka on käyty lävitse, aloita alusta ja jatka kunnes yhtään 
	kertaa ei tarvitse vaihtaa peräkkäisiä kortteja. 

Tehtävä 4.12 Kuplalajittelu

Tuleeko pakka järjestykseen tällä algoritmilla? Voidaanko algoritmia nopeuttaa mitenkään? Kirjoita algoritmista "lausekielinen"- versio.

4.2.5 Lajittelu avaimen mukaan

Kirjoita nyt joukko pahvilappuja, joissa kussakin on henkilön nimi, osoite ja puhelinnumero.

Sekoita laput ja kokeile toimiiko edelliset algoritmit mikäli laput järjestetään nimien mukaan. Ai tyhmä ehdotus! Tässä se onkin ohjelmoinnin vaikeus. Asiat ovat yksinkertaisia! Eiväthän ne osoitteet siellä lajittelua sotke.

Mikäli laput järjestetään nimen mukaan, sanotaan nimen olevan lajitteluavaimena. Lajitteluavaimeksi voitaisiin valita myös osoite tai puhelinnumero. Mikäli kahdella henkilöllä olisi sama nimi, voitaisiin nämä kaksi järjestää osoitteen perusteella. Tällöin lajitteluavain muodostuisi merkkijonosta johon olisi yhdistettynä nimi ja osoite.

4.2.6 Algoritmin parantaminen

Kaikki edelliset algoritmit ovat kompleksisuudeltaan normaalitapauksessa samanlaisia.

Tehtävä 4.13 Loppuminen erikoistapauksessa

Mikä edellisistä algoritmeista loppuu nopeasti, mikäli kortit jo olivat järjestyksessä?
Ohjelman toimintaan saattamisen kannalta olisi riittävää löytää jokin toimiva algoritmi. Myöhemmin, mikäli ohjelman toiminta todetaan hitaaksi ko. algoritmin kohdalta, voidaan algoritmia yrittää tehostaa. Lajittelussa tehostus saattaisi olla vaikkapa QuickSort (mukana mm. C- kielen standardikirjastossa).

Tehtävä 4.14 QuickSortin kompleksisuus

Jos algoritmin kompleksisuus on esimerkiksi 2n2+n, sanotaan että kompleksisuus on O(n2), eli usein kiinnostaa vain kompleksisuuden suurin "potenssi". QuickSortin keskimääräinen kompleksisuus on O(n log2n). On olemassa myös erikoistapauksissa toimivia lajitteluja, joissa kompleksisuus on O(n). Piirrä kuva jossa on Selection Sortin, QuickSortin ja lineaarisen lajittelun käyttämä "aika" piirrettynä lajiteltavien alkioiden (n=10,100,1000,10000,1000000) funktiona.

Tehtävä 4.15 Lisäys oikealle paikalleen vaiko lisäys loppuun ja lajittelu?

Tutki kumpiko on työmäärältään edullisempaa jos järjestettyyn taulukkoon tulee lisättäväksi suuri määrä uusia alkiota
1)
lisätä alkio aina taulukkoon oikealle paikalleen
2)
lisätä alkio aina taulukon loppuun ja kun kaikki alkiot on lisätty, niin lajitella taulukko

4.3 Algoritmin tarkentaminen

Edellisissä lajittelualgoritmeissa oli vielä muutamia aukkopaikkoja! Etsi pienin? Laita oikealle paikalleen?

4.3.1 Pienimmän etsiminen

Miten kädessä olevista korteista voidaan etsiä pienin. Yksi mahdollisuus on kuljettaa "pieninehdokasta" läpi koko pakan. Mikäli matkan varrelta löytyy parempi ehdokas, otetaan tämä tilalle. Edellä mainittu kuplalajittelu korjattuna perustuu nimenomaan tähän ideaan.

Entä jos kädessä olevien korttien järjestystä ei haluta muuttaa? Voisimme menetellä esimerkiksi seuraavasti (alkuarvaus ja arvauksen korjaaminen):

	0.  vedä kädessä olevan pakan ylin kortti hieman esille
	    ota ensimmäinen kortti tutkittavaksi
	1.  vertaa tutkittavaa korttia ja esiinvedettyä korttia
	2.  mikäli tutkittava on pienempi, vedä se esiin ja työnnä
	    edellinen takaisin
	3.  siirry tutkimaan seuraavaa korttia ja jatka kohdasta 1.
	    kunnes olet tutkinut koko pakan

4.3.2 Paikalleen sijoittaminen

Miten kortti sijoitetaan paikalleen jo lajiteltuun kasaan? Esimerkiksi seuraavasti:
	0. laita uusi kortti päällimmäiseksi lajiteltuun kasaan
	1. vertaa uutta ja seuraavaa
	2. mikäli väärässä järjestyksessä, niin vaihda ne keskenään
	   ja jatka kohdasta 1. 

4.4 Haku järjestetystä joukosta

Usein tulee vastaan myös tilanne, jossa tietyn henkilön tiedot pitäisi hakea esimerkiksi nimen mukaan. Mikäli valittu tietorakenne on järjestetty nimen mukaan, voidaan hakemisessa käyttää vaikkapa puolitushakua.

Nimen hakeminen ei taas poikenne kortin etsimisestä järjestetystä korttipakasta vai mitä?

4.4.1 Suora haku

Kun kortit ovat järjestämättä, niin miten löydät haluamasi kortin?
	Ota seuraava kortti.  Mikäli etsittävä niin lopeta, muuten ota taas seuraava. 
Algoritmi on OK 13 kortille, mutta kokeilepa Äystön etsimistä puhelinluettelosta tällä algoritmilla (muista lukea jokainen nimi ennen Äystöä)!

4.4.2 Puolitushaku

Mikäli 13 korttiasi on järjestyksessä ja sinun pitäisi mahdollisimman vähällä pläräämisellä löytää vaikkapa pata 4, niin miten voisit menetellä?
	1. laita pakka pöydälle kuvapuolet ylöspäin
	2. laita pakka puoliksi
	3. laita molemmat pakat pöydälle kuvapuolet ylöspäin
	4. kummassako kasassa etsittävä on?
	5. heitä se pakka pois jossa etsittävä ei ole
	6. jos etsittävä ei päällimmäinen, niin jatka kohdasta 1. 
Vaikuttaa tyhmältä 13 kortille, mutta kokeilepa 1000 kortilla! Tai kokeile nyt etsiä ÄYSTÖÄ puhelinluettelosta tällä algoritmilla.

Tehtävä 4.16 Puolitushaku

Kirjoita puolitushausta kunnon "lausekielinen versio" kun meillä on sivunumeroitu kirja, jonka kullakin sivulla on täsmälleen yhden henkilön tiedot. Sivunumeroita kirjassa on N- kappaletta. Aloitat sivuista S1=0 ja S2=N+1. Miten jatkat mikäli pitää etsiä nimi NIMI?

Tehtävä 4.17 Puolitushaun kompleksisuus

Mikä on puolitushaun kompleksisuus?

4.5 Yhteenveto

Tätä on ohjelmointi! Kykyä (ja rohkeutta) sanoa selvät asiat täsmällisesti. Jossain vaiheessa vaihdamme vain täsmällisyyden astetta ja "lausekielen"- sijasta siirrymme käyttämään oikeata lausekieltä, esim. C- kieltä. Nämä omatekoiset algoritmit kannattaa kuitenkin säilyttää ja kirjata näkyviin todellisen ohjelman kommentteihin. Arviot algoritmin nopeudesta kannattaa myös laittaa kommentteihin, jotta jälkeenpäin on helpompi etsiä jo tekovaiheessa hitaaksi epäiltyjä kohtia. Miksi jättää seuraavalle lukijalle sama tehtävä ihmeteltäväksi, jos olemme sen toteutuksen jo jonnekin kirjanneet.

Algoritmit kannattaa testata huolellisesti jossain tutussa ympäristössä. Hyvin moni ohjelmointiongelma vektoreiden (=taulukko, =kasa kortteja, =ruutupaperi, =sivunumeroitu kirja jne...) kanssa samaistuu johonkin jokapäiväiseen ilmiöön. Kuten etsiminen puhelinluettelosta, korttipakan järjestäminen jne. Yritä etsiä näitä yhteyksiä ja kokeile ensin ratkaista ongelma tällä tavoin. Siirrä ratkaisu sitten "lausekielelle" ja lopulta ohjelmointikielelle.

Äläkä yritä liikaa, vaan jaa aina ongelma pienempiin osiin, kunnes tulee vastaan sen kokoisia osaongelmia, jotka osataan ratkaista! Tällaista osaongelman ratkaisijaa sanotaan ohjelmointikielessä aliohjelmaksi.

Kun osaongelma on ratkaistu, unohda se miten sen ratkaisija toimii ja käsittele ratkaisijaa vain yhtenä yksinkertaisena toimenpiteenä (vrt. aikaisempi kävelyesimerkki). Tämä on myös eräs ohjelmoinnin "vaikeus". Kirjoittaja haluaa nähdä kaikkien osien toiminnan yhtäaikaisesti. Tämä on kuitenkin mahdotonta. Siis kun jokin osa tekee hommansa, niin tehköön se sen miten tahansa.

Huono on johtaja joka kyttää koko ajan alaisiaan, eikä luota siihen, että nämä tekevät heille annetun tehtävän. Tässä mielessä ohjelmointia voisi verrata yrityksen johtamiseen: Johtaja jakaa koko yrityksen pyörittämisessä tarvittavia tehtäviä alaisilleen (aliohjelmille). Nämä saattavat edelleen jakaa joitakin osatehtäviä omille alaisilleen (aliohjelma kutsuu toista aliohjelmaa) jne. Johtaja (=ohjelmoija ja pääohjelma) kokoaa alaisten tekemän työn toimivaksi kokonaisuudeksi ja firma tuottaa.

Tehtävä 4.18 Kumin paikkaus

Kirjoita algoritmi polkupyörän kumin paikkaamiseksi.

Tehtävä 4.19 Sunnuntai- ilta

Kirjoita algoritmi sunnuntai- illan viettoa varten (muista että ohjelmoinnin demot on maanantaina).

Tehtävä 4.20 Onkiminen

Kirjoita algoritmi 10 ei- alimittaisen kalan onkimiseksi mato- ongella.

Tehtävä 4.21 Järjestyksen kääntäminen päinvastaiseksi

Kirjoita algoritmi pöydälle levitetyn 13 kortin kääntämiseksi päinvastaiseen järjestykseen.


previous next Title Contents Index