TIE-20100 Tietorakenteet ja algoritmit 198 9 Erilaisia tapoja järjestää Käsitellään seuraavaksi järjestämisalgoritmeja, jotka perustuvat muihin kuin vertailuun alkioiden oikean järjestyksen saamiseksi. Lisäksi tutkitaan vertailuun perustuvien algoritmien parasta mahdollista tehokkuutta. Lopuksi mietitään hieman algoritmin valintaan vaikuttavia tekijöitä.
TIE-20100 Tietorakenteet ja algoritmit 199 9.1 Muita järjestämisalgoritmeja Kaikki tähän mennessä käsitellyt järjestämisalgoritmit perustuvat vertailemiseen. Ne hankkivat tietoa oikeasta järjestyksestä vain vertaamalla alkioita keskenään. Järjestämisen apuna on kuitenkin toisinaan mahdollista käyttää muutakin informaatiota kuin vertailun tuottamaa.
TIE-20100 Tietorakenteet ja algoritmit 200 Järjestäminen laskemalla Oletetaan, että alkioiden avainten arvoalue on pieni, enintään alkioiden määrän suuruusluokkaa. Yksinkertaisuuden vuoksi oletamme, että järjestettävien alkioiden avaimet ovat peräisin joukosta {1, 2,..., k}, ja k = O(n). Kullekin avaimelle lasketaan, kuinka monella alkiolla on kyseinen avain. Tuloksen perusteella siirretään alkiot suoraan lopullisille paikoilleen.
TIE-20100 Tietorakenteet ja algoritmit 201 COUNTING-SORT(A, B, k) 1 for i := 1 to k do 2 C[ i ] := 0 (alustetaan aputaulukko C nollilla) 3 for j := 1 to A.length do 4 C[ A[ j ].key ] := C[ A[ j ].key ] + 1 (lasketaan monenko alkion avain = i) 5 for i := 2 to k do 6 C[ i ] := C[ i ] + C[ i 1 ] (lasketaan monenko alkion avain i) 7 for j := A.length downto 1 do (käydään taulukko läpi lopusta alkuun) 8 B[ C[ A[ j ].key ] ] := A[ j ] (sijoitetaan alkio paikalleen tulostaulukkoon) 9 C[ A[ j ].key ] := C[ A[ j ].key ] 1(seuraava oikea paikka on pykälän vasemmalla) Algoritmi asettelee alkiot oikeille paikoilleen käänteisessä järjestyksessä vakauden varmistamiseksi.
TIE-20100 Tietorakenteet ja algoritmit 202 Suoritusaika: Ensimmäinen ja kolmas for-silmukka kuluttavat aikaa Θ(k). Toinen ja viimeinen for-silmukka kuluttavat aikaa Θ(n). Ajoaika on Θ(n + k). Jos k = O(n), ajoaika on Θ(n). Kaikki perusoperaatiot ovat yksinkertaisia, ja niitä on kussakin silmukassa vähän, joten ajoajan vakiokerroin on pieni. COUNTING-SORTIA ei kannata käyttää, jos k n. Algoritmin muistinkulutus on Θ(k). Tavallisesti k n. esimerkiksi: kaikki mahdolliset henkilötunnukset TTY:n henkilökunnan henkilötunnukset
TIE-20100 Tietorakenteet ja algoritmit 203 Järjestäminen moniosaisella avaimella Toisinaan järjestettävä avain on moniosainen tenttituloslista ensin koulutusohjelmittain, sitten aakkosjärjestyksessä sukunimen mukaan. päiväykset ensin vuoden, sitten kuukauden ja sitten päivän mukaan. korttipakka ensin maan, sitten numeron mukaan. Kirjan hakemisto ensin hakusanoittain aakkosjärjestykseen, sitten hakusanojen alakäsitteet sivujärjestyksessä.
TIE-20100 Tietorakenteet ja algoritmit 204 Eriarvoiset kriteerit huomioidaan siten, että: eniten merkitsevä kriteeri, jonka mukaan alkiot ovat erilaiset, ratkaisee vertailun tuloksen. jos alkiot ovat kaikkien kriteerien mukaan yhtä suuret, ne katsotaan kokonaan yhtä suuriksi. Ongelma voidaan ratkaista vertailuun perustuvan algoritmin avulla käyttämällä tilanteeseen sopivaa vertailua, esimerkiksi päiväysten vertailu: DATE-COMPARE(x, y) 1 if x.year < y.year then return smaller 2 if x.year > y.year then return greater 3 if x.month < y.month then return smaller 4 if x.month > y.month then return greater 5 if x.day < y.day then return smaller 6 if x.day > y.day then return greater 7 return equal
TIE-20100 Tietorakenteet ja algoritmit 205 Toisinaan on kuitenkin tarkoituksenmukaista käsitellä aineisto kriteeri kerrallaan. Esimerkiksi korttipakka on helpointa lajitella ensin neljäksi kasaksi maittain, ja sitten jokainen kasa erikseen. Tällöin merkitsevien kriteerien arvoalue on usein pieni verrattuna alkioiden määrään COUNTING-SORT on käyttökelpoinen.
TIE-20100 Tietorakenteet ja algoritmit 206 Moniosaisen avaimen mukaan järjestämiseen on kaksi erilaista algoritmia. LSD-RADIX-SORT järjestetään taulukko ensin vähiten merkitsevän numeron mukaan, sitten seuraavaksi vähiten merkitsevän mukaan jne. vaatii vakaan järjestelyalgoritmin - muutenhan taulukko olisi lopuksi järjestyksessä ainoastaan eniten merkitsevän kriteerin mukaan vertailuun perustuvia algoritmeja ei kannata käyttää, koska ne järjestäisivät taulukon suunnilleen samalla vaivalla kaikkien kriteerien mukaan kerralla sopiva järjestysalgoritmi on COUNTING-SORT LSD-RADIX-SORT(A, d) 1 for i := 1 to d do (käydään kriteerit läpi vähiten merkitsevästä lähtien) 2 järjestä A jollain vakaalla järjestämisalgoritmilla kriteerin i mukaan
TIE-20100 Tietorakenteet ja algoritmit 207 MSD-RADIX-SORT järjestetään taulukko ensin eniten merkitsevän numeron mukaan, ja samanarvoisten alkioiden osataulukot tarvittaessa seuraavaksi merkitsevän numeron mukaan jne. ei vaadi järjestysalgoritmilta vakautta käyttökelpoinen esimerkiksi mahdollisesti erimittaisia merkkijonoja järjestettäessä tarkastaa kriteerejä ainoastaan siihen asti kun järjestämisen vuoksi on tarpeen LSD-RADIX-SORTIA monimutkaisempi toteuttaa emme esitä sille algoritmia tässä
TIE-20100 Tietorakenteet ja algoritmit 208 RADIX-SORTIN ajankulutus, kun apuna on COUNTING-SORT: järjestäminen yhden kriteerin mukaan: Θ(n + k) kriteerejä on kaikkiaan d kpl kokonaisaika Θ(dn + dk) k on yleensä vakio kokonaisaika Θ(dn), tai Θ(n), jos d:kin vakio RADIX-SORT näyttäisi siis tietyin edellytyksin olevan O(n) järjestämisalgoritmi. Onko se yleisessä tapauksessa vertailuun perustuvia algoritmeja parempi?
TIE-20100 Tietorakenteet ja algoritmit 209 Järjestämisalgoritmin suorituskykyä tarkasteltaessa on järkevää olettaa, että kaikki (tai ainakin useimmat) alkiot ovat erisuuria. Esimerkiksi INSERTION-SORT on O(n), jos kaikki alkiot ovat saman suuruisia. Jos kaikki alkiot ovat erisuuria, ja yhden kriteerin arvoalueen koko on vakio k, niin k d n d log k n = Θ(lg n) RADIX-SORT on Θ(dn) = Θ(n lg n), jos oletetaan, että alkiot ovat enimmäkseen eri suuria. RADIX-SORT on asymptoottisesti yhtä hidas kuin muutkin hyvät järjestämisalgoritmit. Jos oletetaan vakio d, niin RADIX-SORT on Θ(n), mutta silloin isoilla n:n arvoilla useimmat alkiot ovat samoja.
TIE-20100 Tietorakenteet ja algoritmit 210 RADIX-SORTIN etuja ja haittoja Etuja: RADIX-SORT on nopeutensa puolesta kilpailukykyinen esimerkiksi QUICKSORTIN kanssa jos avaimet ovat esimerkiksi 32-bittisiä lukuja, ja taulukko järjestetään 8 bitin mukaan kerrallaan k = 2 8 ja d = 4 COUNTING-SORTia kutsutaan neljästi RADIX-SORT sopii hyvin moniosaisen avaimen mukaan järjestämiseen, kun avaimen osilla on pieni arvoalue. esim. tekstitiedoston järjestäminen annetuilla sarakkeilla olevien merkkien mukaan (vrt. Unix tai MS/DOS sort) Haittoja: COUNTING-SORT tarvitsee toisen n:n mittaisen taulukon B, johon se rakentaa lopputuloksensa sekä k:n kokoisen aputaulukon. Sen apumuistin tarve on siis Θ(n), eli merkittävästi suurempi kuin esimerkiksi QUICKSORTilla ja HEAPSORTilla.
TIE-20100 Tietorakenteet ja algoritmit 211 Bucket sort Oletetaan, että avaimet kuuluvat tunnetulle välille, ja avainten arvot ovat jakautuneet tasan. Jokainen avaimen arvo yhtä todennäköinen. Esimerkin vuoksi oletamme, että avainten arvot sijoittuvat nollan ja ykkösen välille. Otetaan käyttöön n ämpäriä (bucket) B[ 0 ]... B[ n 1 ]. BUCKET-SORT(A) 1 n := A.length 2 for i := 1 to n do (käydään kaikki alkiot läpi) 3 INSERT(B[ n A[i] ], A[i]) (heitetään alkio oikeaan ämpäriin) 4 k := 1 (aloitetaan taulukon täyttäminen kohdasta 1) 5 for i := 0 to n 1 do (käydään ämpärit läpi) 6 while B[ i ] ei ole tyhjä do (tyhjennetään epätyhjät ämpärit...) 7 A[ k ] := EXTRACT-MIN(B[ i ]) (... siirtämällä alkiot pienimmästä alkaen...) 8 k := k + 1 (... oikeaan kohtaan tulostaulukkoa)
TIE-20100 Tietorakenteet ja algoritmit 212 Ämpäreiden toteutus: Tarvitaan operaatiot INSERT ja EXTRACT-MIN. Ämpäri on itse asiassa prioriteettijono. Ämpärien koko vaihtelee suuresti. yleensä ämpärin alkioiden määrä 1 kuitenkin jopa kaikki alkiot voivat joutua samaan ämpäriin kekoon perustuvan toteutuksen tulisi varata muistia Θ(n) jokaiselle ämpärille, yhteensä Θ(n 2 ) Toisaalta toteutuksen ei tarvitse olla kovin nopea suurille ämpäreille, koska niitä syntyy harvoin. Käytännössä ämpärit kannattaa toteuttaa listoina. INSERT linkittää tulevan alkion oikealle paikalleen listaan, aikaa kuluu Θ(listan pituus) EXTRACT-MIN poistaa ja palauttaa listan ensimmäisen, aikaa kuluu Θ(1)
TIE-20100 Tietorakenteet ja algoritmit 213 BUCKET-SORTIN keskimääräinen suorituskyky: Oletimme, että avainten arvot ovat jakautuneet tasan. Yhteen ämpäriin tulee siis keskimäärin yksi alkio, ja hyvin harvoin paljoa enempää. Ensimmäinen for-silmukka käy kaikki alkiot läpi, Θ(n). Toinen for-silmukka käy kaikki ämpärit läpi, Θ(n). while-silmukka käy kaikilla kierroksillaan yhteensä kaikki alkiot läpi kerran, Θ(n). INSERT on keskimäärin vakioaikainen, koska ämpärissä on keskimäärin yksi alkio. EXTRACT-MIN on vakioaikainen. Kokonaisaika on keskimäärin Θ(n). Hitaimmassa tapauksessa kaikki alkiot joutuvat samaan ämpäriin ja tulevat suuruusjärjestyksessä. INSERT kuluttaa lineaarisesti aikaa Kokonaisaika on pahimmillaan Θ(n 2 ).
TIE-20100 Tietorakenteet ja algoritmit 214 9.2 Kuinka nopeasti voi järjestää? Taulukon järjestäminen itse asiassa tuottaa sen permutaation, joka tekee alkuperäisestä taulukosta täysin järjestetyn taulukon. Jos taulukon kaikki alkiot ovat erisuuret, ko. permutaatio on yksikäsitteinen. Järjestäminen vastaa kyseisen permutaation etsintää kaikkien mahdollisten permutaatioiden joukosta. 31 41 59 26 58 41 26 31 41 41 58 59 Esimerkiksi INSERTION-SORT, MERGE-SORT, HEAPSORT ja QUICKSORT perustuvat vertailemiseen. Ne hankkivat tietoa oikeasta permutaatiosta vain vertaamalla alkioita keskenään. Mikä on pienin määrä vertailuja, joka riittää takaamaan oikean permutaation löytymisen?
TIE-20100 Tietorakenteet ja algoritmit 215 n:n erisuuren alkion taulukolla on 1 2 3... n eli n! permutaatiota. Vertailuja on suoritettava niin monta, että ainut oikea vaihtoehto tulee poimituksi niiden joukosta. Jokainen vertailu A[ i ] A[ j ] (tai A[ i ] < A[ j ]) jakaa permutaatiot kahteen ryhmään: ne, joissa A[ i ]:n ja A[ j ]:n keskinäinen järjestys on vaihdettava, ja ne, joissa ei ole, joten... yksi vertailu riittää poimimaan ainoan oikean vaihtoehdon enintään kahdesta kaksi vertailua riittää poimimaan ainoan oikean vaihtoehdon enintään neljästä... k vertailua riittää poimimaan ainoan oikean vaihtoehdon enintään 2 k :stä oikean poimimiseen x:stä vaihtoehdosta tarvitaan ainakin lg x vertailua
TIE-20100 Tietorakenteet ja algoritmit 216 Jos taulukon koko on n, niin permutaatiota on n! Vertailuja on suoritettava ainakin lg n! kappaletta. vertailemiseen perustuva järjestämisalgoritmi joutuu käyttämään Ω( lg n! ) aikaa. Paljonko on lg n!? lg n! lg n! = n k=1 lg k n k= n 2 lg n 2 n 2 lg n 2 = 1 2 n lg n 1 2 n = Ω(n lg n) Ω(n) = Ω(n lg n) toisaalta lg n! < n lg n + 1 = O(n lg n) lg n! = Θ(n lg n)
TIE-20100 Tietorakenteet ja algoritmit 217 Jokainen vertailemiseen perustuva järjestämisalgoritmi joutuu siis käyttämään hitaimmassa tapauksessa Ω(n lg n) aikaa. Toisaalta HEAPSORT ja MERGE-SORT ovat hitaimmassakin tapauksessa O(n lg n). Vertailemiseen perustuva järjestäminen on mahdollista hitaimmassa tapauksessa ajassa Θ(n lg n), mutta ei yhtään nopeammin. HEAPSORT ja MERGE-SORT ovat hitaimman tapauksen ajan kulutukseltaan asymptoottisesti optimaalisia. Järjestäminen on aidosti asymptoottisesti työläämpää kuin esim. mediaanin etsintä, joka onnistuu hitaimmassakin tapauksessa ajassa O(n).