111A Tietoraketeet ja algoritmit, 016-017, Harjoitus, Ratkaisu Harjoitukse aiheita ovat algoritmie aikakompleksisuus ja lajittelualgoritmit Tehtävä.1 Selvitä seuraavie rekursioyhtälöide ratkaisuje kompleksisuusluokat käyttäe lueoissa esitettyä lausetta M (Master Theorem): a) T( ) 9T ( /), b) c) T( ) 16T ( / 4), T( ) T ( / ). Lause M. (Master Theorem) Olkoot 1b 1 a kokoaislukuvakioita ja f fuktio. Rekursio T( ) a T( / b ) f ( a T( / b ) f ( ) samoi kui rekursio ) T( ) ratkaisulle pätee seuraavaa: log a 1) Jos f ( ) O( b ) jollaki vakiolla 0, ii T( ) logb a log a ) Jos f ( ) b ), ii ) Jos log a ( ) ( b ) vakiolla 1 ) logb a T( ) lg ) f jollaki vakiolla 0 ja jos a f ( / b) c f ( ) c ja riittävä suurilla luvu arvoilla, ii T( ) f ( )) jollaki Ratkaisu. Kohdassa a o a 9, b, jote log a log 9 log. Edellee o b 1 f ( ) O( ) ja voidaa soveltaa lausee M esimmäistä kohtaa (tässä siis 1) ja saadaa log a T( ) b ) ). Kohdassa b o a 16, b 4, jote log a log 16 log 4. Edellee f ( ) ( ) b 4 4, jote sovelletaa lausee M toista kohtaa ja saadaa log a T( ) b lg ) lg ). f ( ). Näi olle log a log 1 Kohdassa c taas o a, b ja b ja koska 1 f ( ) O( ), sekä a f ( / b) / ) 1/ 4 1/ 4 f ( ), voidaa soveltaa lausee kolmatta kohtaa (tässä ) ja saadaa T( ) f ( )) )
Tehtävä. Seuraava rekursiivie algoritmi toteuttaa puolitushau järjestettyy taulukkoo. Testaa algoritmia hakemalla taulukosta A = [11,,1,47,,6,71,9,94,10,11,16,1,14] alkio 10. Määritä lausee M avulla tiukka kompleksisuusluokka ( -otaatio). Syöte: Taulukko A[1,..,], >= 1, tauluko alkiot ovat kasvavassa järjestyksessä A[1] <= A[] <= <= A[]. Luvut 1<=p<=q<=. Luku x jota haetaa taulukosta väliltä A[p,..,q]. Tulostus: Alkio x ideksi taulukossa tai arvo -1, jos x ei esiiy taulukossa välillä A[p,..,q]. HAKU(A,p,q,x) 1. if p==q. if A[p]==x. retur p 4. else. retur -1 6. else 7. r = (p + q)/. if x<=a[r] 9. retur HAKU(A,p,r,x) 10. else 11. retur HAKU(A,r+1,q,x) Ratkaisu. Ku haetaa arvoa 10 taulukosta A, saadaa seuraava kutsujoo: HAKU(A,1,14): r = 7 ja A[r] = 71; 10 > A[r] HAKU(A,,14): r = 11 ja A[r] = 11; 10 <= A[r] HAKU(A,,11): r = 9 ja A[r] = 94 <= 10; 10 > A[r] HAKU(A,10,11) : r = 10 ja A[10] = 10; 10 <= A[r] HAKU(A,10,10): p==q ja A[10]==10: palautetaa 10 Algoritmi palauttaa siis alkio 10 ideksi taulukossa A. Olkoo algoritmi suoritusaika T (), ku haetaa tauluko osasta, joka koko o paikkaa. Lukuu ottamatta rekursiivista kutsua, algoritmissa suoritetaa vakiomäärä vakioaikaisia operaatioita (korkeitaa kaksi vertailua ja yksi sijoitus). Näi olle rekursiota lukuu ottamatta suoritusaika o vakio c. Jos >1, taulukko puolitetaa ja kutsutaa algoritmia puolitetulle taulukolle. Tämä vie aja T (/ ). Näi saadaa rekursioyhtälö T( ) T( / ) c. 0 Sovelletaa lausetta M: Nyt a 1, b, jote log a log 1 log 0. Edellee f ( ) c c 0 ( log a 0 T( ) b lg ) lg ) lg ). 0 ). Site lausee M tapaukse mukaa Algoritmi o siis kompleksisuusluokaltaa logaritmie. b
Tehtävä. Järjestä tauluko A:,, 6,, 7,, 4 alkiot pieimmästä suurimpaa käyttäe (algoritmit ovat liitteeä tehtävie jälkee). a) pikalajittelua (Quicksort), b) kekolajittelua (Heapsort). Ratkaisu. a) Quicksort Oletetaa seuraavassa, että taulukko A o ideksoitu ollasta lähtie, jolloi tauluko viimeise alkio ideksi o 6, jote aluksi kutsutaa algoritmia QUICKSORT(A,0,6). Site kutsutaa algoritmia PARTITION(A,0,6), joka hakee taulukosta oikea paika tauluko viimeiselle alkiolla 4, siirtää tätä pieemmät vasemmalle, suuremmat oikealle ja lopuksi palauttaa alkio 4 ideksi uudessa taulukossa. Tuloksea o taulukko 4 7 6 ja paluuarvo Nyt algoritmissa kutsutaa rekursiivisesti algoritmia QUICKSORT(A,0,1) ja QUICKSORT(A,,6) QUICKSORT(A,0,1) kutsuu algoritmia PARTITION(A,0,1), joka hakee (tauluko puaisella merkitystä osasta) oikea paika alkiolle ja palauttaa arvo 0. Tuloksea alkiot ja ovat järjestyksessä ja kutsutaa algoritmia QUICKSORT(A,0,-1) ja QUICKSORT(A,1,1), joista kumpikaa ei eää tee mitää. Nyt taulukko o järjestyksessä 4 7 6 QUICKSORT(A,,6) kutsuu algoritmia PARTITION(A,,6), joka hakee (tauluko siisellä merkitystä osasta) oikea paika alkiolle 6 ja palauttaa arvo 4. Tuloksea o taulukko 4 6 7 Nyt kutsutaa QUICKSORT(A,,) ja QUICKSORT(A,,6).Esimmäie kutsu ei eää tee mitää ja toie kutsuu PARTITION(A,,6), joka palauttaa arvo 6, mikä jälkee myös tämä kutsuhaara päättyy ja taulukko o järjestyksessä 4 6 7
b) Heapsort Merkitää alle taulukko ja sitä vastaava biääripuu. Aluksi tauluko arvot sijoitetaa täydellisee biääripuuhu. 6 7 4 6 7 4 Esimmäisessä vaiheessa biääripuusta tehdää maksimikeko. Käydää isäsolmut (joilla o lapsia) läpi ideksie järjestyksessä,, 1. Tapahtuvat seuraavat alkioide arvoje vaihdot: (,) (,) ja vielä (,) (,) sekä (,7) (7,). Saadaa taulukko seuraavaa järjestyksee ja sitä vastaava maksimikeko. 7 6 4 7 6 4 Nyt alkaa toie vaihe, jossa esimmäise ja viimeise alkio paikat vaihdetaa (,4) (4,) ja 1-paikalle tullut arvo 4 tippuu suuremma lapse alipuussa oikealle paikallee (4,7) (7,4) ja (4,) (,4). Kekoa pieeetää yhdellä eli poistetaa viimeie paikka, joka jää kuiteki taulukkoo. Saadaa seuraava taulukko ja maksimikeko.
7 6 7 6 4 Jällee vaihdetaa esimmäise ja viimeise alkio paikat (7,) vaihdetaa (,7) ja 1-paikalle tullut arvo tippuu suuremma lapse alipuussa oikealle paikallee (,6) (6,). Kekoa pieeetää yhdellä eli poistetaa se viimeie paikka, joka jää kuiteki taulukkoo. Saadaa seuraava taulukko ja maksimikeko. 6 4 7 6 4 7 Taas vaihdetaa esimmäise ja viimeise alkio paikat (6,4) (4,6) ja 1-paikalle tullut arvo 4 tippuu suuremma lapse alipuussa oikealle paikallee (4,) (,4). Kekoa pieeetää yhdellä eli poistetaa se viimeie paikka, joka jää kuiteki taulukkoo. Saadaa seuraava taulukko ja maksimikeko.
4 6 7 4 6 7 Vaihdetaa (,) (,) ja 1-paikalle tullut arvo tippuu suuremma lapse alipuussa oikealle paikallee (,4) (4,). Kekoa pieeetää yhdellä eli poistetaa se viimeie paikka, joka jää kuiteki taulukkoo. Saadaa seuraava taulukko ja maksimikeko. 4 6 7 4 6 7 Vaihdetaa taas (4,) (,4) ja 1-paikalle tullut arvo tippuu suuremma lapse alipuussa oikealle paikallee (,) (,). Kekoa pieeetää yhdellä eli poistetaa se viimeie paikka, joka jää kuiteki taulukkoo. Saadaa seuraava taulukko ja maksimikeko.
4 6 7 4 6 7 Esimmäise ja viimeise alkio paikat vaihdetaa (,) (,). Kekoa pieeetää yhdellä eli poistetaa se viimeie paikka, joka jää kuiteki taulukkoo. Tällöi tauluko alkiot äkyvät seuraavassa kuviossa. 4 6 7 4 6 7 Lajittelu päättyy tähä. Taulukkoo o saatu alkiot suuruusjärjestyksee,, 4,,6, 7,.
Tehtävä.4 Lukujoo tyyppiarvoksi eli moodiksi saotaa siiä useimmi esiityvää lukua. Esimerkiksi joo,1,,,,,4,1,4,4,, tyyppiarvo o. Suuittele algoritmi, joka etsii lukutauluko A moodi. Algoritmi aikakompleksisuusluokka saa olla O( lg()), ku syötetauluko koko o. Ratkaisu. Käytetää hyväksi lajittelua: Suuruusjärjestyksee saatetusta taulukosta o helppo hakea moodi käymällä taulukko kerra läpi: Lasketaa kuika mota kertaa sama arvo esiityy peräkkäi ja pidetää yllä tietoa siitä, mikä arvo o esiityyt suurimma määrä kertoja. Tällöi algoritmiksi saadaa seuraava: Syöte: Taulukko A[1,..,], >= 1. Tulostus: Tauluko alkioide tyyppiarvo MODE(A) 1. HEAPSORT(A) //Lajittele taulukko käyttäe kekolajittelua. mode = A[1]. freq = 1 4. temp = 1. i = 6. while i <= do 7. if A[i]!= A[i-1]. temp = 1 9. else 10. temp = temp + 1 11. if temp > freq 1. freq = temp 1. mode = A[i] 14. i = i+1 1. retur mode Ku tullaa algoritmi riville, taulukko o järjestyksessä. Aluksi muuttuja mode o tauluko esimmäie arvo. Silmukassa tarkastellaa tauluko jokaie alkio ideksistä lähtie. Muuttuja temp laskee kulloiki tarkasteltava tauluko arvo esiitymiskertoja: ku arvo vaihtuu, aloitetaa uudellee arvosta 1. Muuttuja freq arvo o toistaiseksi useimmi esiityee arvo esiitymislukumäärä: jos muuttuja temp arvo ylittää muuttuja freq arvo, muuttujaa freq päivitetää ja myös muuttuja mode päivitetää taulukossa esiityväksi arvoksi. Näi olle muuttuja mode arvo o tauluko tarkastellu osa moodi. Ku algoritmi päättyy, muuttuja arvo o koko tauluko moodi. Algoritmi etsii siis tauluko moodi. Kekolajittelu aikakompleksisuus o luokkaa O( lg()). Tämä lisäksi algoritmissa tehdää vakioaikaie operaatio jokaiselle tauluko alkiolle. Algoritmi loppuosa aikakompleksisuus o siis luokkaa O() ja algoritmi kokoaiskompleksisuus luokkaa O( lg()). Yllämaiittu algoritmi o implemetoitu C- ja Pytho-kielillä likitettyihi kooditiedostoihi.
Tehtävä. Kirjoita ohjelma (joko C- tai Pytho-kielellä), jolla voidaa testata lajittelualgoritmie suoritusaikoja. Kirjoita fuktiot, jotka suorittavat lisäyslajittelu ja pikalajittelu (Quicksort) ja testaa äide suoritusaikoja erikokoisilla kokoaislukutaulukoilla, jotka alustetaa satuaisluvuilla. Algoritmit ovat liitteeä tehtävie jälkee. Testaa vielä seuraavaa: Pikalajittelua voi yrittää parataa site, että lajittelee tauluko esi karkeaa suuruusjärjestyksee Quicksort-algoritmilla site, että rekursiivista Quicksort-kutsua ei tehdä, mikäli lajiteltava väli pituus o jotaki kyysarvoa pieempi. Liitteeä o tällaie algoritmi (QuicksortCutoff). Tällöi taulukkoo jää kyysarvoa lyhempiä välejä, jotka eivät ole järjestyksessä, mutta edellise osaväli arvot ovat pieempiä kui seuraava. Tällöi taulukko voidaa opeasti lajitella loppuu käyttäe lisäyslajittelua. Implemetoi tämä ja testaa aiaki kyysarvoilla 10, 0 ja 100. Nopeutuuko lajittelu puhtaasee Quicksort-lajitteluu verrattua? Ratkaisu. Esimerkkiohjelmat C- ja Pytho-kielillä o likitetty ohee. Lisäyslajittelu aikakompleksisuus o luokkaa Θ( ), jote se suoritusaika kasvaa suureksi huomattavasti ee luokkaa Θ(lg()) olevaa Quicksort-algoritmia. C-ohjelma suoriutuu samassa ajassa oi kymmee kertaa pitemmästä taulukosta kui Pytho-ohjelma. Testikoeella saatii seuraavia suoritusaikoja lisäyslajittelulle: Lisäyslajittelu: C-ohjelma Tauluko koko 10000 0000 100000 00000 Aika (s) 0.11.17 9.6 4.11 Lisäyslajittelu: Pytho-ohjelma Tauluko koko 1000 000 10000 0000 Aika (s) 0.10.09.19. Quicksort-algoritmia voidaa suorittaa huomattavasti suuremmilla tauluko arvoilla. C-kielisellä ohjelmalla algoritmi suoriutui kohtuudella vielä taulukoista, joide koko oli 0000000. Yhdistelmäalgoritmi toimi hiema opeammi. Paras kyysarvo kokeilluista oli 0, tosi erot eivät olleet suuret Quicksort: C-ohjelma Tauluko koko 1000 10000000 0000000 0000000 Aika (s) 1.9.4 6.0 16.1 Quicksort+lisäyslajittelu, kyysarvo 10: C-ohjelma Tauluko koko 1000 10000000 0000000 0000000 Aika (s) 1.1.76.9 1. Quicksort+lisäyslajittelu, kyysarvo 0: C-ohjelma Tauluko koko 1000 10000000 0000000 0000000 Aika (s) 1.9.7.74 14.9 Quicksort+lisäyslajittelu, kyysarvo 0: C-ohjelma Tauluko koko 1000 10000000 0000000 0000000 Aika (s) 1..7.76 1.0
Pytho-ohjelmassa Quicksorti ja lisäyslajittelu yhdistelmä suoritusaikaa vaikutti voimakkaasti se, kuika suurelta lukuväliltä satuaislukuja arvottii. Tämä oki luoollista, koska tauluko kokoo ähde piei lukuväli aiheuttaa se, että taulukkoo arvotaa samoja lukuja. Tällöi taulukko o hyvi lähellä järjestettyä taulukkoa jo keskeytety Quicksorti jälkee. Alla aetuissa suoritusajoissa o arvottu satuaislukuja väliltä [1,1000000] : Quicksort: Pytho-ohjelma Tauluko koko 00000 00000 1000000 000000 Aika (s) 1.4 4.0.66 17.90 Quicksort+lisäyslajittelu, kyysarvo 10: Pytho -ohjelma Tauluko koko 00000 00000 1000000 000000 Aika (s) 1..7.1 16.7 Quicksort+lisäyslajittelu, kyysarvo 0: Pytho-ohjelma Tauluko koko 00000 00000 1000000 000000 Aika (s) 1.4.99.6 17.4 Quicksort+lisäyslajittelu, kyysarvo 0: Pytho-ohjelma Tauluko koko 00000 00000 1000000 000000 Aika (s) 1. 4. 9. 1.9 Käytetyt ohjelmakoodit o likitetty ratkaisutiedosto alle.
Neuvoja tehtävää.: C-ohjelma. Satuaisluvu geeroimie ja joki operaatio kesto laskemie sekueissa tapahtuu seuraavasti: #iclude <stdlib.h> #iclude <time.h> clock_t start,ed; double totaltime; it rad_x; // Satuaisluku rad_x = rad(); // Isoilla taulukoilla kaattaa varmistaa että satuaisluku // o varmasti -bittie ja kutsua fuktiota usiged it bigradom(){ usiged it radom = (((usiged it) rad() << 0) & 0x0000FFFF) (((usiged it) rad() << 16) & 0xFFFF0000); } retur radom; // Operaatio kesto start = clock(); // OPERAATIO ed = clock(); totaltime = (double)(ed-start)/clocks_per_sec; Pythoissa vastaavat operaatiot voidaa tehdä seuraavasti: import time import radom #Satuaisluku x ii, että väliltä a <= x <= b x = radom.radit(a,b) #Operaatio kesto start = time.perf_couter() #OPERAATIO ed = time.perf_couter() - start
Heapsort (kekolajittelu) Liite. Tehtävie lajittelualgoritmit Syöte: Taulukko A[1,,] (=A.legth) HEAPSORT(A) Tuloste: Taulukko A järjestyksessä 1. BUILD_MAX_HEAP(A). for i = A.legth dowto. vaihda A[1] A[i] 4. A.heap_size = A.heap_size 1. MAX_HEAPIFY(A, 1) BUILD_MAX_HEAP(A) Tuloste: Taulukosta A maksimikeko 1. A.heap_size = A.legth. for i = A.legth/ dowto 1. MAX_HEAPIFY(A, i) MAX_HEAPIFY(A, i) 1. lft = LEFT(i) ( = *i). rgt = RIGHT(i) ( = *i+1). if lft <= A.heap_size ad A[lft] > A[i] 4. largest = lft. else 6. largest = i 7. if rgt <= A.heap_size ad A[rgt] > A[largest]. largest = rgt 9. if largest!= i 10. vaihda A[i] A[largest] 11. MAX_HEAPIFY(A, largest) Lisäyslajittelu Syöte: Taulukko A[0,1,..,-1], >= 1 Tulostus: Järjestys A[0] <= <= A[-1] INSERTION_SORT(A) 1. for j = 1 to -1. k = A[j]. i = j-1 4. while i>=0 ad A[i]>k. A[i+1] = A[i] 6. i = i-1 7. A[i+1] = k
Quicksort ja QuicksortCutoff Syöte: Tauluko osa A[p,..,r] Tuloste: A[p,..,r] lajiteltua QUICKSORT(A, p, r) 1. if p < r. q = PARTITION(A, p, r). QUICKSORT(A, p, q-1) 4. QUICKSORT(A, q+1, r) Syöte: Tauluko osa A[p,..,r], kyysarvo threshold Tuloste: A[p,..,r] karkeasti lajiteltua kyysarvo mukaa QUICKSORTCUTOFF(A, p, r,threshold) 1. if r-p > threshold. q = PARTITION(A, p, r). QUICKSORTCUTOFF(A, p, q-1, threshold) 4. QUICKSORTCUTOFF(A, q+1, r, threshold) PARTITION(A,p,r) 1. x = A[r]. i = p - 1. for j = p to r 1 4. if A[j] <= x. i = i + 1 6. vaihda A[i] A[j] 7. vaihda A[i + 1] A[r]. retur i + 1