Tietorakenteet, laskuharjoitus 3, ratkaisuja

Samankaltaiset tiedostot
58131 Tietorakenteet ja algoritmit (syksy 2015)

Tietorakenteet, laskuharjoitus 2,

(p j b (i, j) + p i b (j, i)) (p j b (i, j) + p i (1 b (i, j)) p i. tähän. Palaamme sanakirjaongelmaan vielä tasoitetun analyysin yhteydessä.

4 Tehokkuus ja algoritmien suunnittelu

Algoritmit 1. Luento 3 Ti Timo Männikkö

Pino S on abstrakti tietotyyppi, jolla on ainakin perusmetodit:

(a) L on listan tunnussolmu, joten se ei voi olla null. Algoritmi lisäämiselle loppuun:

Algoritmit 1. Luento 4 Ke Timo Männikkö

ALGORITMIT 1 DEMOVASTAUKSET KEVÄT 2012

A TIETORAKENTEET JA ALGORITMIT

4. Perustietorakenteet: pino, jono ja lista

Algoritmit 1. Luento 2 Ke Timo Männikkö

Nopea kertolasku, Karatsuban algoritmi

Algoritmit 2. Luento 7 Ti Timo Männikkö

Rajapinta (interface)

Koe ma 1.3 klo salissa A111, koeaika kuten tavallista 2h 30min

f(n) = Ω(g(n)) jos ja vain jos g(n) = O(f(n))

58131 Tietorakenteet ja algoritmit (kevät 2016) Ensimmäinen välikoe, malliratkaisut

Tietorakenteet (syksy 2013)

A TIETORAKENTEET JA ALGORITMIT

Tietorakenteet, laskuharjoitus 1,

private TreeMap<String, Opiskelija> nimella; private TreeMap<String, Opiskelija> numerolla;

Algoritmit 1. Demot Timo Männikkö

Algoritmit 1. Demot Timo Männikkö

811312A Tietorakenteet ja algoritmit II Perustietorakenteet

TIETORAKENTEET JA ALGORITMIT

Sisältö. 22. Taulukot. Yleistä. Yleistä

Sisältö. 2. Taulukot. Yleistä. Yleistä

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

811312A Tietorakenteet ja algoritmit, , Harjoitus 3, Ratkaisu

Algoritmit 1. Luento 12 Ke Timo Männikkö

58131 Tietorakenteet ja algoritmit (kevät 2013) Kurssikoe 1, , vastauksia

Algoritmit 1. Luento 12 Ti Timo Männikkö

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Pikalajittelu: valitaan ns. pivot-alkio esim. pivot = oikeanpuoleisin

Tietorakenteet, laskuharjoitus 4,

Tietorakenteet ja algoritmit syksy Laskuharjoitus 1

Ohjelmointi 2 / 2010 Välikoe / 26.3

Algoritmit 1. Luento 1 Ti Timo Männikkö

1.4 Funktioiden kertaluokat

Ohjelmoinnin jatkokurssi, kurssikoe

Algoritmit 1. Luento 5 Ti Timo Männikkö

18. Abstraktit tietotyypit 18.1

Hakupuut. tässä luvussa tarkastelemme puita tiedon tallennusrakenteina

1. (a) Seuraava algoritmi tutkii, onko jokin luku taulukossa monta kertaa:

AVL-puut. eräs tapa tasapainottaa binäärihakupuu siten, että korkeus on O(log n) kun puussa on n avainta

Algoritmit 2. Luento 3 Ti Timo Männikkö

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Tietorakenteet, laskuharjoitus 6,

Tietorakenteet ja algoritmit - syksy

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmoinnin perusteet Y Python

58131 Tietorakenteet ja algoritmit (kevät 2014) Uusinta- ja erilliskoe, , vastauksia

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Algoritmit 2. Luento 3 Ti Timo Männikkö

Tietorakenteet, laskuharjoitus 7, ratkaisuja

Tarkennamme geneeristä painamiskorotusalgoritmia

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

lähtokohta: kahden O(h) korkuisen keon yhdistäminen uudella juurella vie O(h) operaatiota vrt. RemoveMinElem() keossa

Java kahdessa tunnissa. Jyry Suvilehto

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

Listarakenne (ArrayList-luokka)

3. Laskennan vaativuusteoriaa

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Kaksiloppuinen jono D on abstrakti tietotyyppi, jolla on ainakin seuraavat 4 perusmetodia... PushFront(x): lisää tietoalkion x jonon eteen

Algoritmi on periaatteellisella tasolla seuraava:

1. Omat operaatiot 1.1

Tietorakenteet, laskuharjoitus 10, ratkaisuja. 1. (a) Seuraava algoritmi tutkii, onko jokin luku taulukossa monta kertaa:

Taulukot. Taulukon määrittely ja käyttö. Taulukko metodin parametrina. Taulukon sisällön kopiointi toiseen taulukkoon. Taulukon lajittelu

ITKP102 Ohjelmointi 1 (6 op), arvosteluraportti

3. Hakupuut. B-puu on hakupuun laji, joka sopii mm. tietokantasovelluksiin, joissa rakenne on talletettu kiintolevylle eikä keskusmuistiin.

A) on käytännöllinen ohjelmointitekniikka. = laajennetaan aikaisemmin tehtyjä luokkia (uudelleenkäytettävyys)

Algoritmit 2. Luento 14 Ke Timo Männikkö

Diskreetin matematiikan perusteet Laskuharjoitus 2 / vko 9

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

4. Algoritmien tehokkuus

58131 Tietorakenteet (kevät 2009) Harjoitus 9, ratkaisuja (Antti Laaksonen)

Algoritmit 1. Demot Timo Männikkö

A ja B pelaavat sarjan pelejä. Sarjan voittaja on se, joka ensin voittaa n peliä.

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

3. Binääripuu, Java-toteutus

Taulukot. Taulukon käsittely. Tämän osan sisältö. Esimerkki. Taulukon esittely ja luonti. Taulukon alustaminen. Taulukon koko

Algoritmit 1. Luento 8 Ke Timo Männikkö

Algoritmit 2. Luento 2 To Timo Männikkö

Algoritmien suunnittelu ja analyysi (kevät 2004) 1. välikoe, ratkaisuja

Luokka Murtoluku uudelleen. Kirjoitetaan luokka Murtoluku uudelleen niin, että murtolukujen sieventäminen on mahdollista.

Tietorakenteet ja algoritmit

Algoritmit 2. Luento 2 Ke Timo Männikkö

Luku 8. Aluekyselyt. 8.1 Summataulukko

2. Perustietorakenteet

2. Olio-ohjelmoinista lyhyesti 2.1

1 Tehtävän kuvaus ja analysointi

Algoritmit 2. Luento 13 Ti Timo Männikkö

Java-kielen perusteet

Tehtävä 1. Tehtävä 2. Arvosteluperusteet Koherentti selitys Koherentti esimerkki

Esimerkkejä polynomisista ja ei-polynomisista ongelmista

TKT20001 Tietorakenteet ja algoritmit Erilliskoe , malliratkaisut (Jyrki Kivinen)

Määrittelydokumentti

Ohjelmoinnin perusteet Y Python

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

Transkriptio:

Tietorakenteet, laskuharjoitus 3, ratkaisuja 1. (a) Toistolauseen runko-osassa tehdään yksi laskuoperaatio, runko on siis vakioaikainen. Jos syöte on n, suoritetaan runko n kertaa, eli aikavaativuus kokonaisuudessaan O(n) Algoritmi käyttää yhtä apumuuttujaa, tilavaatisuus on näinollen vakio O(1) (b) Rekursiivisen algoritmin aikavaativuus on O(n) ja tilavaativuus on O(n), koska funktiota kutsutaan n kertaa ja yksi kutsu vie vakiomäärän aikaa ja muistia. Esimerkiksi jos n = 4, funktiota kutsutaan seuraavasti: Summa(4) Summa(3) Summa(2) Summa(1) Summa(0) Kun funktio saa parametrin 0, se ei enää kutsu itseään vaan kutsuketju alkaa purkautua. Tässä vaiheessa algoritmin muistinkäyttö on suurimmillaan: muistissa ovat samaan aikaan kaikki n funktiokutsua. (c) Summan voi laskea myös ajassa O(1) seuraavan summakaavan avulla: 1+2+3+...+n = n (n+1)/2 Tuloksena oleva algoritmi on selvästi vakioaikainen: Summa(k) return k * (k+1)/2 2. public int paalla(){ return top.key; public int toisiksiylimpana(){ return top.next.key; public int kolmanneksiylimpana(){ return top.next.next.key; public void tulostakolmeylinta(){ System.out.print("kolme ylintä: "); System.out.print( top.key + " " ); System.out.print( top.next.key + " " ); System.out.print( top.next.next.key + " " ); System.out.println(""); public int poistatoisiksiylin(){ PinoSolmu pois = top.next; top.next = pois.next; return pois.key; 1

3. public void lisaatoiseksi(int lisattava) { PinoSolmu vanhatoka = top.next; PinoSolmu uusi = new PinoSolmu(lisattava, vanhatoka ); top.next = uusi; public int koko() { int koko = 0; PinoSolmu p = top; while ( p!=null ) { koko++; p = p.next; return koko; public void tulostakaikki(){ System.out.print("pinossa: "); PinoSolmu p = top; while ( p!=null ) { System.out.print(p.key + " "); p = p.next; System.out.println(""); public void lisaapohjalle(int lisattava){ // HUOM: ei toimi tyhjälle pinolle! PinoSolmu uusi = new PinoSolmu(lisattava, null); PinoSolmu p = top; while ( p.next!=null ) { p = p.next; p.next = uusi; Metodi lisaapohjalle vie nyt aikaa O(n) sillä koko pino käytävä läpi pohjaa etsiessä onnistuu vakioaikaisesti jos pinolle lisätään pohjan muistava attribuutti. Push- ja pop- operaatioissa täytyy tällöin huomioida pohjan muistavan attribuutin päivitys tapauksissa joissa lisätään ensimmäinen tai poistetaan viimeinen. Näin syntyy seuraava tietorakenne: public class PinoJono { private PinoSolmu top; private PinoSolmu bottom; public PinoJono() { top = null; 2

bottom = null; public void push(int k) { PinoSolmu uusi = new PinoSolmu(k, top); top = uusi; // lisättään ensimmäinen if ( bottom==null ) bottom = uusi; public int pop() { PinoSolmu x = top; top = x.next; // poistetaan viimeinen if ( top==null ) bottom = null; return x.key; public boolean empty() { return (top == null); public void lisaapohjalle(int lisattava){ PinoSolmu uusi = new PinoSolmu(lisattava, null); if ( bottom==null ){ top = uusi; else { bottom.next = uusi; bottom = uusi; public void tulostakaikki(){ System.out.print("pinojonossa: "); PinoSolmu p = top; while ( p!=null ) { System.out.print(p.key + " "); p = p.next; System.out.println(""); 4. Määritellään rajapinta jonka pinot toteuttavat. public interface Pino { int pop(); void push(int lisattava); boolean empty(); 3

Koska molemmat pinot toimivat lähes samalla tavalla, tehdään niille abstrakti yläluokka. Ainoa poikkeava kohta eli kasvatuksen yhteydessä tulevan taulukon uuden koon laskenta määritellään abstraktiksi metodiksi. public abstract class KasvavaPino implements Pino { protected int[] taulukko; private int top; public KasvavaPino() { taulukko = new int[100]; top = 0; public int pop() { return taulukko[--top]; public void push(int luku) { if ( top==taulukko.length ) kasvatataulukkoa(); taulukko[ top++ ] = luku; public boolean empty() { return top == 0; private void kasvatataulukkoa() { int[] uusi = new int[laskeuusikoko()]; for ( int i=0 ; i < taulukko.length; i++ ) uusi[i] = taulukko[i]; taulukko = uusi; abstract protected int laskeuusikoko(); Edellisestä on helppo erikoistaa TuplautuvaPino jossa taulukon pituus aina kasinkertaistuu ja VakiollaKasvavaPino jossa taulukon koko kasvaa aina sadalla. public class TuplautuvaPino extends KasvavaPino { protected int laskeuusikoko() { return taulukko.length*2; 4

public class VakiollaKasvavaPino extends KasvavaPino { protected int laskeuusikoko() { return taulukko.length + 100; Mittausta varten luodaan oikeantyyppiset pino-instanssit: Pino p1 = new TuplautuvaPino(); Pino p2 = new VakiollaKasvavaPino(); for ( int operaatioita = 1000; operaatioita<1000000; operaatioita *= 2 ) { mittaaaika(p1, operaatoita); mittaaaika(p2, operaatoita); Pinojen käyttämä aika suhteessa tehtyihin push-operaatioihin nähdään seuraavista kuvista (x-akselissa käytetty aika ja x-akselilla operaatioiden määrä), huomaa kuvien erilaiset skaalat: 5

Pino jossa käytettiin tuplausstrategiaa osoittautui huomattavasti tehokkaammaksi. Pinoa jota kasvatetaan vain 100 alkiota kerrallaan vaivaa se, että joka sadas operaatio on lineaarinen alkioiden lukumäärän suhteen, sillä tällöin alkiot on kopioitava vanhasta taulukosta uuteen isompaan taulukkoon. Kopioinnin takia aikaa vievä push tehdään, niin usein, että keskimääräiseksi push:in aikavaativuudekeksi tulee O(n), ja n kpl push:eja vie aikaa O(n 2 ). Ylempi kuva näyttää tämän. Pino, jossa taulukko tuplataan on paljon nopeampi, koska kallis lineaarisen ajan vievä operaatio suoritetaan verrattain harvoin. Karkeasti ottaen jokaista O(n) aikaa vievää kallista push-operaatiota kohti tehdään n kappaletta halpoja push:eja. Näinollen yksittäisen pushin keskimääräiseksi aikavaativuudeksi tuleekin vain O(1), ja n kpl push:eja vie aikaa O(n). Alempi kuva näyttää tämän. Tässä tehty aikavaativuus "analyysi" ei siis arvioi tavanomaiseen tapaan operaation pahinta tapausta, vaan yksittäisen operaation keskimäärin vievää aikaa suoritettaessa pitempi ketju komentoja. Tälläistä analysointitapaa sanotaan tasoitetuksi analyysiksi (engl. amortized analysis). Tasoitettu analyysi ei varsinaisesti kuulu tietorakenteisiin. Hieman formaalimpi analyysi edellisestä: Jos ajatellaan taulukon yhden alkion kopiontiin kuluvan yhden askelen niin voidaan laskea kuinka monta askelta joudutaan suorittamaan n:ssä lisäyksessä, jotka tehdään tyhjään pinoon. Laskelmissa on oletuksena, että taulukon koon lisäys aloitetaan yhdestä alkiosta. Tämä ei vaikuta mitenkään laskujen suuruusluokkaan, mutta helpottaa arviointia. Lasketaan ensin sadalla kasvavan tapaus 1. 1 Lausekkeessa käytetään lattiafunktiota, joka on pyöristys lähimpään lukua pienempään kokonaislukuun. Esi- 6

n n 100 1+ 100 i = n+100 i=1 i=1 n ( n +1) 100 100 2 n+100 n (n+1) 2 = 50n 2 +51n = O(n 2 ) Kun tehdään n:kpl push-operaatioita, suoritettavien kopiointiaskelten määrä siis on luokaa O(n 2 ). Siis yksittäinen push vie keskimäärin O(n) kopiointiaskelta, eli keskimääräinen aikavaativuus push-operaatioille on lineaarinen.. Tarkastellaan nyt Pino2:n n:ssä peräkkäisessä lisäyksessä käytettävien askelten lukumäärää. Jälkimmäinen summa alla olevassa lausekkeessa seuraa siitä, että taulukko tarvitsee tuplata log 2 n kertaa, ja jokaisella tuplauksella kaikki alkiot pitää kopioida taulukosta toiseen. Oletetaan tässä kuitenkin laskujen yksinkertaistamiseksi, että luku log 2 n on kokonaisluku. Kun taulukko on yhden kokoinen pitää kopioida 1 = 2 0 alkiota, kun taulukko on kahden kokoinen pitää kopioida 2 = 2 1 alkiota jne. n 1+ i=1 log 2 n i=0 2 i = n+2 log 2 n+1 1 n+2 2 log 2 n = n+2 n = 3n Siis keskimäärin n:n lisäyksen jonossa jokainen lisäys vie vakioajan vaikka pahimman tapauksen aikavaativuus onkin lineaarinen. Tälläisessä tapauksessa sanotaan, että operaatio on tasoitetulta vaativuudeltaan vakioaikainen. Taulukkoon voidaan toteuttaa myös taulukon kutistaminen sopivalla tavalla poisto- operaatioiden yhteyteen. Sopiva tapa tarkoittaa tässä sitä, että taulukon koko puolitetaan esimerkiksi silloin, kun sen täyttöaste on 1 4. Tällöin voidaan osoittaa, että peräkkäisistä lisäyksistä ja poistoista saadaan tasoitetulta vaativuudeltaan vakioaikaisia. Ohitamme tässä perustelut, koska tasoitettu vaativuus ei varsinaisesti kuulu kurssin sisältöön. On kuitenkin hyödyllistä tuntea käsite. Lisäksi taulukkoja muuttuvan kokoisia taulukoita tarvitaan melko usein, joten on hyvä tietää miten se voidaan toteuttaa tehokkaasti. (a) Tehtävänä on toteuttaa algoritmi, jolle annetaan n lukua sisältävä taulukko ja joka laskee taulukon lukujen summan. Jos taulukossa olevat alkiot ovat toisistaan riippumattomia, on alkioiden summa selvästi riippuvainen jokaisesta taulukon alkiosta. Eli summan selvittämiseksi ei ole muuta mahdollisuutta kuin "katsoa"jokaisen alkion arvoa. Arvon katsominen onnistuu ajassa O(1) ja koska alkioita on n kappaletta, ei kaikkia voida käydä katsomassa nopeammin kuin ajassa O(n). (b) Algoritmin tilavaativuus ei voi olla aikavaativuutta suurempi, koska jokainen algoritmin askel voi viitata vain kiinteään määrään muistipaikkoja. Tämän vuoksi ei ole mahdollista, että algoritmi olisi vakioaikainen mutta sen muistinkäyttö kasvaisi lineaarisesti. merkiksi 3.14 = 3 ja 3 = 3 7

(c) Algoritmien analyysissa oletetaan yleensä, että luvut vievät vakiomäärän muistia ja esimerkiksi laskutoimitukset tapahtuvat vakioajassa. Tämä ei ole kuitenkaan järkevä oletus, jos luvut ovat huomattavan suuria. Tarkasti ottaen kokonaisluku n vie tilaa O(log n), koska siinä on luokkaa log n numeroa. Tässä logaritmin kantaluku riippuu lukujärjestelmästä, mutta sillä ei ole vaikutusta suuruusluokkaan. Esimerkiksi luvun 12345 bittiesitys 11000000111001 sisältää 14 numeroa ja log 2 12345 = 14. Jos taulukossa on n positiivista kokonaislukua, tulo 2 T[1] 3 T[2] 5 T[3] on varmasti ainakin 2 T[1] 2 T[2] 2 T[3], joka taas on ainakin 2 1 2 1 2 1 eli 2 n. Luvun tallennus vie siis muistia Ω(log2 n ) eli Ω(n). Käytännössä voidaan usein olettaa, että käsiteltävät luvut eivät ole kovin suuria vaan esimerkiksi 32-bittinen muuttuja riittää. Tällöin luvun suuruudella on yläraja ja se vie vain vakiomäärän tilaa. Mutta jos luvut voivat kasvaa rajatta, logaritmi kertoo todellisen muistinkäytön. 8