Tavanomaisimpien tietorakenteiden ja algoritmien testauksesta

Slides:



Advertisements
Samankaltaiset esitykset
18. Abstraktit tietotyypit
Advertisements

Osaamisen ja sivistyksen parhaaksi Tilatiedot ja niiden muutokset
@ Leena Lahtinen Helia Ohjelman perusrakenteet 1. PERÄKKÄISRAKENNE 2. VALINTARAKENNE 3. TOISTORAKENNE.
JavaScript (c) Irja & Reino Aarinen, 2007
Ohjelman perusrakenteet
Binääripuut Kaksihaaraista puuta sanotaan binääripuuksi:
Tietorakenteet ja algoritmit
2.8.3 Abstraktit tietotyypit
Rakenteinen ohjelmointi
Taulukot: Array Taulukko Javassa pitää aina perustaa (new)
Java-ohjelmointi Opas ammattimaiseen osaamiseen Luku 4 Toistolauseet
@ Leena Lahtinen OHJELMAN OSITTAMINEN LUOKKA ATTRIBUUTIT METODIT.
TIETOKONEOHJELMAN RAKENNE OHJELMALLA ON KAKSI OSAA  MÄÄRITYSOSA TIETOJEN KUVAUKSIA VARTEN  SUORITUSOSA TIETOJEN KÄSITTELYÄ VARTEN.
Ehto- ja toistolauseet
Poikkeustenkäsittely  Mitä poikkeustenkäsittely tarkoittaa?  Poikkeuksen käsitteleminen  Poikkeusluokkien hierarkia  Poikkeuksen heittäminen 1.
Toiston tekeminen Javalla  Mikä toistorakenne on?  while toistorakenne  do-while toistorakenne  for toistorakenne 1.
Ohjelmoinnin tekniikkaa Sisällys for -lause lyhemmin. Vaihtoehtoisia merkintöjä aritmeettisille lauseille. Useiden muuttujien esittely.
Binäärinen hakupuu Jokaisessa solmussa on yksikäsitteinen avain
Ohjelman perusrakenteet
Tiedonhakumenetelmät Helena Ahonen-Myka Kevät 2004, osa 9 Merkkijonohahmon etsintä selaamalla.
13. Hyvä ohjelmointitapa (osa 1)
1 Kertaus koetta varten oleellisista asioista Jukka Juslin.
ict1td002 - Copyright Raine Kauppinen 1 Alkuarvot ja tyyppimuunnokset (1/5)  Aiemmin olemme jo antaneet muuttujille alkuarvoja, esimerkiksi: int.
Visual Basic -ohjelmointi
2.7 Rekursio ja iteraatio Algoritminen ongelmanratkaisu ei ole
Metodit – Arvotyyppi Ellei metodi palauta arvoa, sen arvotyyppi on void Tällöin ”return;”-lauseke ei ole metodissa pakollinen, vaikka sen käyttö on sallittua.
11. Javan toistorakenteet
S ysteemianalyysin Laboratorio Teknillinen korkeakoulu Esitelmä 11 - Teemu Mutanen Optimointiopin seminaari - Syksy 2005 / 1 Lisätiedon arvo.
4. Attribuutit 4.1. Sisällys Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2.
C 1. Testaus on ”sarja toimintoja” Itse asiassa, testaus on vuorovaikutusta, jota rytmittää ohjelmiston arviointi. Vaikka on hyödyllistä tunnistaa sarja.
@ Leena Lahtinen OHJELMAN OSITTAMINEN LUOKKA ATTRIBUUTIT METODIT.
Hyvä ohjelmointitapa (osa 2) Yleistä Lisää hyviä ohjelmointikäytäntöjä: − Jaa pitkä koodi osiin. − Käytä attribuutteja säästeliäästi.
@ Leena Lahtinen Toistorakenne Ohjelmassa toistetaan tiettyjä toimenpiteitä monta kertaa peräkkäin Toisto noudattaa sille kuuluvia tarkkoja standardoituja.
5. Lineaarinen optimointi
@ Leena Lahtinen TIETOKONEOHJELMAN RAKENNE OHJELMALLA ON KAKSI OSAA:  MÄÄRITYSOSA TIETOJEN KUVAUKSIA VARTEN  SUORITUSOSA TIETOJEN KÄSITTELYÄ.
Rinnakkaisuus Järjestelmässä, jossa voi olla useita prosesseja rinnakkain suorituksessa voi tulla tilanteita, joissa prosessien suoritusta täytyy kontrolloida.
Lasilaatikkotestauksen toinen lähestymistapa. ● Siinäkin käytetään vuoverkkoa. ● Tarkastelun kohteena ovat moduulin muuttujat (myös sen käyttämät globaalit.
Olioiden taulukointi Perustaulukon käyttö Luokan ilmentymät voidaan tallettaa taulukkoon samoin kuin muuttujat Esimerkki talletetaan taulukkoon opintojaksojen.
11. Relaatiotietokannan suunnittelualgoritmit ja lisäriippuvuudet Tällä kurssilla käsitellään kirjan luvusta 11 ainoastaan algoritmi 11.1 häviöttömän liitoksen.
Antti-Jussi Lakanen Nuorten peliohjelmointi 2009 / Jyväskylän yliopisto.
Kontrollirakenteet laajemmin
Keskusmuistitietokantahakemistot Vilho Raatikka Solid Information Technology Tietokannat NYT! Helsinki,
Valintarakenne valintarakenne alkaa aina kysymyksellä eli ehdolla ehto tarkoittaa, että muuttujan sisältöä verrataan toisen muuttujan sisältöön tai vakioon.
Listat eli luettelot listaelementit ovat lohkoelementtejä:  lista ja listan alkiot alkavat uudelta riviltä  listan jälkeen tuleva elementti alkaa uudelta.
21. Rekursio.
Ohjausrakenteet Määräävät ohjelmakoodin suoritusjärjestyksen Ehtolause if – else on muotoa if (lauseke) lause1 else lause2 Jos lauseke on tosi, niin suoritetaan.
Prioriteettijonot ja kekolajittelu (heapsort)
Kuplalajittelu (bubble sort)
Puun määritelmä Puu on yhden tai useamman kytketyn solmun muodostama hierarkinen joukko Lehtisolmuista juurisolmuun on yksikäsitteinen polku Käytetään.
Jakolajittelu (Radix sort) Vertailu tehdään avaimen osien perusteella Avaimia käsitellään R-kantaisina arvoina esim. postin lajittelukone prosessoi lähetykset.
Lajittelun sovelluksia Yksilöllisyyden testaaminen Kopioiden poistaminen Mediaani/n:ksi suurimman valinta frekvenssien laskenta/yleisin alkio, l.moodi.
Hajoita ja hallitse (divide and conquer) Pikalajittele n-alkioinen taulukko 1. Divide: Jaetaan taulukko kahdeksi alitaulukoksi tukialkion (pivot) x suhteen:
Hajakoodaus Talletetaan alkiot avain-indeksoituun taulukkoon Hajakoodausfunktio Menetelmä avain-indeksin laskemiseen avaimesta Törmäyksen selvitysstrategia.
Linkitetty lista Joukko alkioita jotka on kytketty toisiinsa linkeillä Listan alkio sisältää talletettavan datan ja linkin seuraavan alkioon Alkion lisääminen.
Symbolitaulut Joukko hakuavaimen omaavia tietueita LISÄÄ uusi tietue ETSI tietue hakuavaimen perusteella Sovelluksia: Spell checker etsii sanoja sanakirjasta.
Toistorakenne Toistorakennetta käytetään ohjelmissa sellaisissa tilanteissa, joissa jotain tiettyä ohjelmassa tapahtuvaa toimenpidekokonaisuutta halutaan.
8. Rekursio.
8. Näppäimistöltä lukeminen
7. Hyvä ohjelmointitapa..
Toisto Toistolausekkeet for, while(ehto){…} ja do {…} while(ehto)
8. Näppäimistöltä lukeminen
16. Ohjelmoinnin tekniikkaa
14. Hyvä ohjelmointitapa.
11. Javan valintarakenteet
4. Attribuutit.
8. Rekursio.
Kontrollirakenteet laajemmin
13. Loogiset operaatiot.
Ohjelman perusrakenteet
16. Ohjelmoinnin tekniikkaa
Esityksen transkriptio:

Tavanomaisimpien tietorakenteiden ja algoritmien testauksesta Lasse Bergroth

1. Haku järjestämättömästä listasta tai vektorista Etsitty alkio voi sijaita missä kohdin listaa / vektoria tahansa! Pahimmassa tapauksessa koko lista / vektori tutkittava läpi. Toteutus yhteen suuntaan linkitetyn listan avulla Varmistuttava, että on käytettävissä osoitin listan alkuun. Muutoin listan alkupään alkiot jäävät tarkastelematta. Jos haku epäonnistuu, koko lista on käyty läpi. Pitää varmistua, ettei yritetä testata enää avainarvoa, jos listaosoitin saa arvon NIL. Muutoin tuloksena yleensä ajonaikainen virhe. Tieto listaan tallennettujen alkioiden määrästä ei ole oleellinen. Toteutus kahteen suuntaan linkitetyn listan avulla Listan alkuosoitin ei ole tarpeen, mutta on varmistuttava, ettei etsintöjä lopeteta liian aikaisin. Kahteen suuntaan linkitetyssä listassa haku epäonnistuu, kun lähtösolmusta on edetty molempiin suuntiin niin pitkälle kuin päästään (toisessa suunnassa edeltäjä- ja toisessa seuraajaosoitin NIL). NIL-osoittimiin liittyvät rajatestit nytkin tarpeen.

1. Haku järjestämättömästä listasta tai vektorista Toteutus rengaslistan avulla Listan alkuosoitin ei ole tarpeen, mutta on varmistuttava, ettei etsintöjä lopeteta liian aikaisin. NIL-testejä ei tarvita, sillä ei ole pelkoa joutumisesta ulos tietorakenteesta 4) Toteutus staattisen vektorin avulla Vektoriin tallennettujen alkioiden lukumäärä tarpeen tietää. Muutoin voidaan ajautua ajonaikaiseen virheeseen, jos vektori loppuu kesken. voidaan saada myös virheellinen myönteinen vastaus (tieto haun onnistumisesta), mikäli etsitty arvo löytyy vektorin osasta, joka ei ole paraikaa käytössä (mutta johon on aikaisemmin ehditty tallettaa tietoa)!

2. Haku järjestetystä vektorista Etsinnän hakualue voimakkaasti rajoitettu: puolitus- eli binäärihaulla tieto alkion löytymisestä (tai vaihtoehtoisesti haune epäonnistumisesta) saadaan työmäärällä O(log2n). Hakualue kutistuu puoleen aikaisemmasta jokaisella yrityksellä. Edellytykset puolitushaun toimimiselle: Etsinnän kohteena oleva vektori on oltava lajiteltu. Muutoin menetelmä ei ole käyttökelpoinen (joudutaan turvautumaan lineaarihakuun)! On varmistuttava, ettei puolitushakua (eikä mitään muutakaan alkioiden järjestämistä edellyttävää hakumenetelmää) kutsuta, ellei vektori ole lajiteltu! Tarvitaan tieto vektoriin tallennettujen alkioiden lukumäärästä. Tiedettävä, mitkä indeksirajat vektorista ovat käytössä.

2. Haku järjestetystä vektorista Esimerkki 1: Puolitushaku (kts. https://trakla.cs.hut.fi/ebook_fi/ebook-Puolitushaku.html) public class BinarySearch { /** * Binäärihaku, haetaan arvoa k taulukosta table */ public static int binarySearch( int table[], int k) { /* table: säilö; k: kiintoarvo */ int low = 0; /* low: kulkija, alaraja */ int high = a.length - 1; /* high: kulkija, yläraja */ int mid; /* mid, kulkija, keskikohta */ while( low <= high ) { mid = (low + high) / 2; if( table[mid] < k) low = mid + 1; else if(table[mid] > k) high = mid - 1; else return mid; } return -1; // Epäonnistunut haku }

2. Haku järjestetystä vektorista Puolitushakualgoritmi ohjelmoidaan usein virheellisesti. Pitää kiinnittää huomiota erityisesti seuraaviin seikkoihin: Hakualueen rajojen oikea asettaminen? Tutkitaanko vektorista varmasti kaikki paikat, joista haettavaa avainta kannattaa etsiä? Poistetaanko kertaalleen jo tutkittu vektorin indeksi jatkotutkimusten ulkopuolelle, vai voiko haku jäädä junnaamaan paikoilleen? Varmistutaanko, ettei vektorista yritetä tutkia positiota, jota ei ole olemassa tai joka on alustamaton? Poistutaanko ohjelmasta heti, kun vastaus tiedetään? Joko etsitty alkio on jo löydetty tai Ei ole enää järkevää tapaa haun jatkamiseksi (epäonnistunut haku)

3. Pinojen ja jonojen käsittelystä Pinojen ja jonojen käsittelyn kannalta kriittiset kohdat olettaen, että ne on toteutettu vektorin avulla: Pinot: Pinon täyttyminen – miten hallitaan? Alkion lukemis- / poistoyrityksen estäminen pinon tultua tyhjäksi. Jonot: Jonon alku- ja loppuindeksien ylläpito, kun jonoon lisätään tai sieltä poistetaan alkio. On mahdollista, että jonon alkuosa sijaitsee fyysisesti vektorin loppupäässä ja jono loppuosa vastaavasti vektorin alussa! Jonon täyttymisen tunnistaminen. Alkion lukemis- / poistoyrityksen estäminen jonon tultua tyhjäksi.

4. Binäärinen hakupuu Binäärisen hakupuun määritelmä: Puun kaikilla solmuilla juurta lukuun ottamatta on yksikäsitteinen isäsolmu Puun jokaiseen solmuun on tallennettuna avainarvo sekä mahdollisesti jotain ylimääräistä dataa Lisäksi solmulla voi olla vasen ja/tai oikea seuraajasolmu Mielivaltaisen solmun Z kohdalta alkavan vasemman alipuun kaikkien solmujen avainarvot ovat aidosti pienempiä kuin Z:n avainarvo Samaisen solmun kohdalta alkavan oikean alipuun kaikkien solmujen avainarvot ovat vähintään yhtä suuria kuin Z:n avainarvo Avainarvon etsiminen tai uuden avaimen lisääminen puuhun on verrattain yksinkertaista, sillä Hakupolku on aina yksikäsitteinen Samoin uuden solmun lisäämispolku: lisääminen tapahtuu aina lehtitasolle ja vaatii vain kolmen linkin asettamisen (isälinkki + vasen ja oikea seuraajalinkki [nämä ovat aluksi kumpikin NIL-osoittimia]) Solmua poistettaessa on kuitenkin 3 eri tapausta, jotka vaativat huolellista linkkien päivittämistä. ---> Helposti jokin tilanne jää vahingossa huomiotta!

4. Binäärinen hakupuu Esimerkki 2: Avaimen poisto binäärisestä hakupuusta (kts. https://trakla.cs.hut.fi/ebook_fi/ebook-Alkion_poistaminen.html) /** Remove element k from subtree t */ public TreeNode remove( int k, TreeNode t ) { /* k: kiintoarvo, poistettava avain */ /* t: kulkija, tutkittava solmu */ if( k < t.getKey()) // Hakuvaihe t.left = remove( k, t.left ); else if( k > t.getKey() ) t.right = remove( k, t.right ); else if( t.left != null && t.right != null ) { // Tapaus: kaksilapsinen solmu TreeNode min = t.right; /* min: kulkija, oikean alipuun pienin */ while ( min.left != null ) min = min.left; t.setKey(min.getKey()); t.right = remove( min.getKey(), t.right ); } else // Tapaus: 0-1 lapsinen solmu t = ( t.left != null ) ? t.left : t.right; return t;

5. Rekursiivisista algoritmeista yleensä Jotta rekursiivinen algoritmi voisi olla toimiva, kahden perusedellytyksen pitää välttämättä toteutua Algoritmissa pitää esiintyä ainakin yksi ns. helppo tai triviaali tapaus, joka ratkeaa sellaisenaan ilman uutta rekursiivista kutsua. Jokainen kerta, kun algoritmia kutsutaan rekursiivisesti – eli siitä muodostuu uusi aktivaatio – lähestytään jotakin yksinkertaisesti ratkeavaa tapausta. Mahdollisia virhelähteitä: Triviaalin tapauksen joko puuttuu kokonaan, tai ainakaan sen toteutumista ei testata ennen kutakin uutta rekursiivista kutsua. Rekursioaskel toteutettu virheellisesti, jolloin laskennassa ei tapahdu edistymistä. Kummassakin tapauksessa seurauksina joko laskennan päättymättömyys tai ajonaikainen virhe muistiresurssien loppuminen kesken.

6. Lajittelumenetelmistä yleensä Syötteeksi annetaan äärellinen joukko lajiteltavia avaimia yleensä joko vektori- tai listamuodossa. Jos syöte on vektorimuotoinen, myös avainten lukumäärä on tiedossa. Lajittelun loppuehtovaatimus: kutsujalle palautetaan tarkalleen samat avaimet, jotka saatiin syötteenä, mutta nyt ne on lajiteltuna joko ei-vähenevään (nousevaan, ellei duplikaatteja) tai ei-kasvavaan (laskevaan, ellei duplikaatteja) suuruusjärjestykseen Yksikkötestausvaiheessa kannattaa asettaa rajoitus, joka takaa, että lopputulos on oikeellinen, käytettiinpä mitä lajittelualgoritmia hyvänsä! Jos lajittelun lopputulos on virheellinen, se on merkki siitä, ettei kaikkia syötteen alkiopareja ole verrattu toisiinsa onnistuneesti – jotain on jäänyt tekemättä tai on suoritettu väärät toimenpiteet. Toisinaan päädytään käyttämään lajittelualgoritmia, joka ei ole käyttökelpoinen sovellettavaan tilanteeseen. Itse ohjelman testaaminen riippuu paljolti sille tyypillisistä ominaisuuksista (onko menetelmä rekursiivinen vai iteratiivinen, käsitteleekö se vektoreita vai listoja).

6.1. Pikalajittelu Perustuu syötteen osittamiseen ns. pivot-alkioiden avulla niin pitkään, kunnes vektorissa ei enää ole jäljellä yhtään vähintään kahden alkion muodostamaa ositetta. Ositusten päätyttyä lopputulos on jo valmis (osittaminen työlästä, mutta osatulosten yhdistäminen äärimmäisen yksinkertaista)! Toimii keskimäärin ajassa O(nlog2n), mutta huonolla tuurilla suoritusajaksi voi tulla neliöllinen, jos esimerkiksi pivot-alkion (alkion, jonka suhteen ositus tapahtuu) valinta epäonnistuu toistuvasti siten, että kaikki ositettavat alkiot pivot-alkiota lukuun ottamatta päätyvät yhteen ja samaan ositteeseen, kun taas toinen osite jää tyhjäksi syötteessä runsaasti duplikaatteja epäedullisin syöte: vektori jo valmiiksi lajiteltuna! Ellei lajittelu valmistu kohtuullisessa ajassa, kannattaa pyytää ohjelmaa tulostamaan kuvaruudulle kulloinkin partitioinnin kohteena oleva vektorin osuus (tapahtuuko edistymistä vai ei?).

Esimerkki 3: Yksi versio pikalajittelualgoritmista (kts. https://trakla.cs.hut.fi/ebook_fi/ebook-Pikajarjestamismenetelma_el.html) void quicksort(int left; int right) { /* left, right: kulkijat */ int i; /* kiintoarvo */ if (right > left) { i = partition(left, right); quicksort(left, i-1); quicksort(i+1, right); } int partition(int left; int right) { /* left, right: kiintoarvot; alueen reunat */ int i, j, pivot, apu; pivot = a[right]; /* pivot: kiintoarvo */ i = left - 1; /* i: askeltaja; vasemmalta oikealle */ j = right; /* j: askeltaja; oikealta vasemmalle */ do { do i++; while (a[i] < pivot); j--; while (a[j] > pivot); apu = a[i]; /* apu: tilapäissäilö; vaihto */ a[i] = a[j]; a[j] = apu; } while (j > i); a[j] = a[i]; a[i] = a[right]; a[right] = apu; return i;

6.2. Laskentalajittelu Tehokas ja yksinkertainen lajittelumenetelmä, joka on kuitenkin käytettävissä vain tarkoin rajoitetusta määrittelyjoukosta peräisin oleville avaimille. On tiedettävä etukäteen, mitä avaimia syötteessä voi esiintyä! Ei voida käyttää reaalilukujen lajitteluun, eikä sitä myöskään kannata käyttää sellaisten kokonaislukujen lajitteluun, joilla arvoalueen vaihteluvälin pituus muuttuu herkästi. Joudutaan varautumaan aina leveimpään mahdollisesti esiintyvään vaihteluväliin: ohjelman suoritus hidastuu tarpeettomasti.

7. Ehtolauseiden ja silmukoiden vartijat Usein esiintyy yksinkertaisia ohjelmointivirheitä ehtolauseissa ja toistorakenteissa. Seuraavassa esitelty muutamia tilanteita: Silmukan tai ehtolauseen aloitusehto asetetaan liian väljäksi esimerkkejä: 1. if ((x > 3) || (x < 7)) /* merkintä || on TAI-operaattori */ { … } 2. if ((x  1) || (x  2)) Edellisten ehtolauseiden vartijat ovat aina tosia! Tapauksessa 1: jos x olisi pienempi tai yhtäsuuri kuin 3, jälkimmäinen vaihtoehto x < 7 toteutuu väkisin. Tapauksessa 2: jos x olisi yhtäsuuri kuin 1, se olisi varmasti erisuuri kuin 2, eli jälleen ehtolause suoritettaisiin kaikissa tapauksissa!

7. Ehtolauseiden ja silmukoiden vartijat Silmukan tai ehtolauseen aloitusehto asetetaan liian tiukaksi esimerkkejä: 1. while !((x  1) || (x  2)) /* merkintä ! on EI-operaattori */ { … } 2. if ((x < 0) && (x > 10)) /* merkintä && on JA-operaattori */ Edellisten ehtolauseiden vartijat ovat aina epätosia! Tapauksessa 1: erisuuruus kahdesta eri vakiosta on aina tosi, joten sen negaatio ei toteudu koskaan! Tapauksessa 2: x ei voi olla samanaikaisesti sekä negatiivinen että suurempi kuin 10, joten ehtolauseen vaatimus ei täyty koskaan.

7. Ehtolauseiden ja silmukoiden vartijat Kirjoitusvirhe operaattoria valittaessa esimerkki: if (x < 0) || (x = 5)) /* merkintä = on C-kielen asetusoperaattori! */ Mitä ilmeisimmin haluttaisiin testata, onko muuttujan x arvo suoritushekellä joko negatiivinen tai 5, mutta nyt tutkitaan, onko muuttujan x arvo negatiivinen asetetaan muuttujalle x arvoksi 5, mikä (tietystikin) onnistuu. Tällöin ehtolauseen ehdoksi tulee tosi, ja ehtolause suoritetaan, oli x:n arvo lauseen suorituksen käynnistyessä ihan mitä vaan! Todellisuudessa olisi varmastikin haluttu kirjoittaa if (x < 0) || (x == 5)) /* merkintä == on C-kielen vertailuoperaattori! */ kyseinen ehtolause suoritetaan ainoastaan, jos x on 5 tai negatiivinen.