Previous Up Next

Peat�kk 5  Algoritmide keerukus

K�esolevas peat�kis tutvume algoritmi keerukuse m�istega ja vaatleme p�hilisi meetodeid algoritmide keerukuse hindamiseks.

5.1  Algoritmi keerukuse m�iste

Algoritmi �igsuse k�rval on t�htis ka selle efektiivsus --- v�ib juhtuda, et algoritm on k�ll teoreetiliselt korrektne, aga praktikas ei j�tku meil selle alusel koostatud programmi t�itmiseks ressursse.

Algoritmi efektiivsust v�ib hinnata mitme erineva ressursi kasutamise seisukohalt. K�ige levinum on algoritmi alusel koostatud programmi t��aja hindamine --- seda t��pi anal��si nimetatakse algoritmi ajalise keerukuse (ingl time complexity) uurimiseks. Sageli on vaja hinnata ka programmi t��ks kasutatava m�lu mahtu --- seda nimetatakse algoritmi mahulise keerukuse (ingl space complexity) uurimiseks. Muude ressursside hindamise vajadust tuleb praktikas oluliselt harvem ette.

Esmapilgul v�ib tunduda, et anal��si asemel v�ib teha lihtsa katse --- kirjutame programmi, paneme selle k�ima ja m��dame meid huvitava ressursi kulu vahetult. Siiski pole selline lahendus alati kasutatav.

Esiteks v�ib juhtuda, et �lesanne on nii suur ja keeruline, et otsene m��tmine pole praktiliselt v�imalik --- n�iteks v�ib m�ne �lesande lahendamine ebaefektiivse algoritmiga isegi parimatel superarvutitel v�tta sajandeid. On selge, et nii kaua lahendust oodata ei saa. Sageli ongi algoritmi keerukuse anal��simise eesm�rgiks hinnata, kas �lesanne on olemasolevate ressurssidega lahendatav.

Teiseks v�ib algoritmi t��aeg erinevate algandmete korral olla erinev, mis t�hendab, et �hest katsest ei piisa, neid tuleks teha mitmeid. Ja selleks, et otsustada, kui palju ja milliste andmetega katseid teha, on ikkagi vaja mingit eelnevat teoreetilist t��d.

Selleks, et arvestada algoritmi t��aja (v�i muu ressursikulu) s�ltuvust algandmetest, v�ljendatakse algandmete ``raskusaste'' mingi arvulise suurusena ja avaldatakse t��aja hinnang funktsioonina, mille parameetriks on see algandmete raskusaste.

Anal��si lihtsustamiseks p��takse andmete raskust v�ljendada v�imalikult v�ikese arvu parameetrite abil. K�ige sagedamini kasutatakse parameetrina just algandmete mahtu: faili t��tlemise algoritmi puhul faili suurust, arvujada t��tlemisel selle elementide arvu jne.

N�iteks arvujadast maksimaalse v��rtuse leidmiseks tuleb �ldjuhul jada elemendid k�ik �kshaaval l�bi vaadata. Kui me loeme algoritmi sammuks jada �he elemendi uurimise, siis kulutab selline j�rjestikune l�bivaatus n-elemendilise jada t��tlemiseks t�pselt n sammu.

Sageli toob algandmete raskusastme lihtsustamine �heks parameetriks kaasa m�ningase ebat�psuse.

N�iteks selleks, et kontrollida, kas antud arv antud n-elemendilises jadas esineb v�i mitte, tuleb halvimal juhul l�bi vaadata k�ik n elementi --- enne ei saa me kindlalt v�ita, et otsitavat nende hulgas pole. Seevastu parimal juhul on otsitav element jadas esimene ja me saame vaid �he v�rdluse j�rel kinnitada, et see arv jadas esineb. Edaspidises n�eme, et keskmiselt kulub n-elemendilises jadas esineva elemendi leidmiseks n / 2 v�rdlust.

Jadast v��rtuse leidmine pole selles m�ttes erandlik �lesanne. Paljude algoritmide puhul on v�imalik r��kida eraldi nende keerukusest halvimal juhul (ingl worst-case complexity), parimal juhul (ingl best-case) v�i keskmiselt (ingl average-case).

Lauaarvuti rakendusprogrammides huvitab meid tavaliselt keskmine keerukus. N�iteks otsimisprogrammi kasutaja jaoks on �ldjuhul piisav teada, et programm suudab sekundis l�bi vaadata keskmiselt 500 kB andmeid ja pole kuigi oluline, kas programm kulutab faili esimeses pooles iga kilobaidi l�bivaatamiseks 1 ms ja teises pooles 3 ms v�i vastupidi.

Hoopis teine on lugu nn sards�steemide puhul, kus arvuti juhib mingit reaalajas t��tavat seadet. N�iteks auto pidureid juhtiva pardaarvuti puhul on kindlasti oluline just selle reaktsiooniaeg halvimal juhul. Vaevalt lepib piduris�steemi viivituse t�ttu haiglasse sattunud s�itja tehase lohutusega, et keskmiselt reageerivad pidurid piisavalt kiiresti ja teised sama marki auto omanikud pole veel avariid teinud.

5.1.1  As�mptootiline keerukus

Sageli on �he �lesande lahendamiseks v�imalik kasutada mitut erinevat algoritmi. Tavaliselt on programmi kasutajad sellisel juhul huvitatud v�imalikult efektiivsest lahendusest. Algoritmide keerukuse anal��si �ks oluline rakendus ongi erinevate algoritmide keerukuse v�rdlemine.

Kuna programmide ressursivajadused on enamasti suuremad just suuremahuliste andmete t��tlemisel, on ka algoritmide efektiivsuse anal��simisel loomulik p��rata t�helepanu p�hiliselt sellele juhule.

Algoritmi sellist uurimist nimetatakse as�mptootilise keerukuse (ingl asymptotic complexity) hindamiseks ja seda t��pi anal��sis keskendutakse just algoritmi keerukusfunktsiooni kasvukiiruse (ingl growth) uurimisele.

Algoritmi keerukusfunktsiooni kasvukiirus n�itab, kui kiiresti kasvab selle algoritmi alusel koostatud programmi ressursivajadus t��deldavate andmete mahu kasvades.

Veidi lihtsustades v�ib eeldada, et kui mingi algoritmi ajaline keerukus on f(n), siis selle alusel koostatud programmi t��aeg avaldub kujul c f(n), kus c on kasutatava arvuti kiirust iseloomustav konstant. Kui c on �he arvuti piires muutumatu konstant, siis v�ime algoritmide efektiivsuse v�rdlemisel piirduda ainult f(n) uurimisega.

Tabelist 5.1 on n�ha, et sisendandmete mahu suurenemisel v�ib programmi t��aja kasv, s�ltuvalt kasutatava algoritmi keerukusest, olla v�ga erinev. Seejuures s�ltub kasv algoritmi keerukust iseloomustavast funktsioonist f(n), kuid ei s�ltu arvutit iseloomustavast konstandist c. Samast on n�ha ka, et programmi t��aja kasv on lausa plahvatuslik, kui algoritmi keerukus avaldub eksponentfunktsioonina.1

Funktsioon c f(n) Suhe f(25) / f(5)
c1 1
c2 logn 2
c3  n 5
c4  n logn 10
c5  n2 25
c6  n3 125
c7  2n 1 048 576
c8  3n 3 486 784 401

Table 5.1: Programmi t��aja kasv andmete mahu kasvades


Pol�noomide2 ja eksponentfunktsioonide kasvu p�him�ttelist erinevust illustreerib veel paremini tabel 5.2. Selle tabeli esimeses veerus on programmi ajalise keerukuse hinnang, kolmes j�rgmises veerus programmi poolt sekundis t��deldavate andmete maht vastavalt 1, 10 ja 100 MOPS (miljonit operatsiooni sekundis) j�udlusega arvutil ja viimases veerus t��deldavate andmete mahu kasv arvuti j�udluse 10-kordsel kasvul.

Keerukus 1 MOPS 10 MOPS 100 MOPS Vahe
n 1 000 000 10 000 000 100 000 000 10 korda
n2 1 000 ~ 3 162 10 000 ~ 3 korda
n3 100 ~ 215 ~ 464 ~ 2 korda
2n ~ 20 ~ 23 ~ 26 ~ 3 v�rra
3n ~ 13 ~ 15 ~ 17 ~ 2 v�rra

Table 5.2: T��deldavate andmete mahu kasv arvuti kiiruse kasvades


Tabelist on n�ha, et kui algoritmi keerukusfunktsioon on pol�noom, siis arvuti j�udluse kasvamisel kordades kasvab kordades ka t��deldavate andmete maht. Kui aga algoritmi keerukus avaldub eksponentfunktsioonina, kasvab t��deldavate andmete maht ainult mingi konstandi v�rra.

5.1.2  Funktsioonide kasv

Nagu eelpool m�rgitud, avaldub algoritmi keerukus algandmete raskusastme funktsioonina. Seega on meil algoritmide keerukuse v�rdlemiseks vaja osata v�rrelda funktsioone.

Osutub, et see polegi nii lihtne. Vaatleme n�iteks joonist 5.1. Kui me v�rdleme f(n) ja g(n) v��rtusi kohal n0, saame tulemuseks f(n0) < g(n0); kohal n1 saame f(n1) = g(n1); kohal n2 aga hoopis f(n2) > g(n2).



Joonis 5.1: Funktsioonide v�rdlemine

Selle ``vastuolu'' p�hjus on muidugi asjaolus, et funktsioonide v��rtused s�ltuvad nende argumentide v��rtustest3 ja selle lahendamiseks on leitud mitmesuguseid funktsioonide v�rdlemise viise. Algoritmide anal��simisel on neist k�ige kasulikumaks osutunud funktsioonide v�rdlemine nende kasvukiiruse j�rgi.

�eldakse, et funktsiooni f(n) kasvukiirus ei �leta funktsiooni g(n) kasvukiirust, ja kirjutatakse f(n) = O(g(n)), kui leiduvad positiivsed arvud c1 ja n0, mille korral kehtib
" n n0 : f(n) c1 g(n).



Joonis 5.2: (a) f(n) = O(g(n)); (b) f(n) = W(g(n)); (c) f(n) = Q(g(n))

Paneme t�hele, et f(n) = O(g(n)) definitsioon ei piira mitte f(n) v�i g(n) v��rtusi endid, vaid nende v��rtuste suhet f(n) / g(n), mis ei tohi l�pmatult kasvada.

Kuna konstant c1 piirab suhet f(n) / g(n) ``�lalt'', v�ib funktsioonide kasvule O-hinnanguid anda suvalise ``liiaga'' --- g(n) v�ib kasvada kuitahes palju kiiremini kui f(n).

N�iteks f(n) = 3 n2 + 1 korral kehtivad nii f(n) = O(n2) kui ka f(n) = O(n3), f(n) = O(n4) ja isegi f(n) = O(2n). Nende k�igi puhul on O definitsiooni tingimused rahuldatud, kui valime c1 = 4 ja n0 = 1.

K�ll aga ei kehti f(n) = O(n) (ehk teisiti �eldes, kehtib f(n) O(n)), sest pole v�imalik valida O definitsiooni n�udeid rahuldavaid c1 ja n0. T�epoolest, mistahes c1 korral f(n) > c1 n, kui n c1/3, seega pole �hegi c1 jaoks v�imalik leida O definitsioonis n�utud konstanti n0.

Veel kirjutatakse f(n) = W(g(n)), kui leiduvad positiivsed c2 ja n0, mille korral kehtib
" n n0 : f(n) c2 g(n).

V�ited f(n) = W(g(n)) ja g(n) = O(f(n)) on samav��rsed. T�epoolest, oletame, et f(n) = W(g(n)). Vastavalt W definitsioonile peavad leiduma sellised positiivsed n0 ja c2, et iga n n0 korral kehtib f(n) c2 g(n). Kui n��d v�tame c1 = 1 / c2, siis peab iga n n0 korral kehtima ka g(n) c1 f(n). See aga on t�pselt g(n) = O(f(n)) definitsioonis n�utud tingimus. Vastupidise j�relduse v�ime t�estada analoogiliselt.

Veel kirjutatakse f(n) = Q(g(n)), kui leiduvad positiivsed c1, c2 ja n0, mille korral kehtib
" n > n0 : c2 g(n) f(n) c1 g(n).

J�llegi on lihtne veenduda, et f(n) = Q(g(n)) parajasti siis, kui f(n) = O(g(n)) ja f(n) = W(g(n)).

Erinevalt seosest O piirab seose Q definitsioon funktsioonide f(n) ja g(n) v��rtuste suhet mitte ainult ``�lalt'', vaid ka ``alt''. Seega f(n) = Q(g(n)) t�hendab, et f(n) ei kasva k�ll kiiremini kui g(n), kuid samal ajal ei j�� kasvus ka oluliselt maha.

�laltoodud n�ites vaadeldud f(n) = 3 n2 + 1 korral f(n) = Q(n2), kuid f(n) Q(n3), f(n) Q(n4) ja f(n) Q(2n). V�ite f(n) = Q(n2) kehtivuses veendumiseks valime c1 = 4, c2 = 3 ja n0 = 1. �lej��nud juhtudel on mistahes positiivse c2 korral v�imalik n�idata, et n piisavalt suurte v��rtuste juures j��vad f(n) v��rtused teistega v�rreldes liiga v�ikesteks.

Avaldisi f(n) = W(g(n)) ja f(n) = Q(g(n)) loetakse vastavalt ``eff-enn on oomega-gee-enn'' ja ``eff-enn on teeta-gee-enn''. Avaldist f(n) = O(g(n)) loetakse ``eff-enn on oo-gee-enn'', kuigi rangelt v�ttes peaks selles avaldises v�rdusm�rgist paremal olema kreeka t�ht omikron.

Edasises kasutame m�nikord seoseid O, W ja Q ka mitme muutuja funktsioonidel. Need �ldistused defineeritakse loomulikul viisil: n�iteks f(n, m) = O(g(n, m)), kui leiduvad c1, n0 ja m0, mille korral kehtib
" n n0, m > m0 : f(n, m) c1 g(n, m).

5.1.3  Kasvuseoste omadused

Nagu eelnevatest l�ikudest n�ha, v�ib t�mmata paralleele funktsioonide vahel kehtivate seoste O, W ja Q ning arvude vahel kehtivate seoste , ja = vahele. See ei tohiks olla eriline �llatus, sest funktsioonide kasvu asusimegi uurima just eesm�rgiga leida vahend nende v�rdlemiseks.

Siiski pole k�ik arvude v�rdlemise seoste omadused funktsioonidele �le kantavad. Nimelt tekitavad funktsioonide kasvukiiruste v�rdlemise seosed funktsioonide vahel osalise j�rjestuse (ingl partial order), kuid arvude v�rdlemise seosed arvude vahel lineaarse j�rjestuse (ingl total order).

Nende erinevus seisneb selles, et mistahes kaks arvu on alati v�rreldavad --- suvaliste arvude a ja b puhul kehtib alati v�hemalt �ks v�idetest a b v�i a b ---, aga kahe funktsiooni f(n) ja g(n) puhul ei tarvitse kehtida ei f(n) = O(g(n)) ega f(n) = W(g(n)). �ks v�rreldamatute kasvukiirustega funktsioonide paar on n�iteks f(n) = n ja g(n) = n1 + sinn.

Algoritmide keerukuse anal��simisel on sageli kasu seoste O, W ja Q j�rgmistest omadustest:
  1. " c > 0 : c f(n) = Q(f(n))
    see t�hendab, et funktsiooni korrutamine konstandiga ei muuda selle kasvukiirust; erijuhul c = 1 saame f(n) = Q(f(n)), j�relikult on seos Q refleksiivne, t�pselt nagu seos = arvudel; samast j�reldub, et ka seosed O ja W on refleksiivsed, t�pselt nagu seosed ja arvudel; erijuhul f(n) = 1 saame c = Q(1), j�relikult on k�igi konstantsete funktsioonide kasvukiirused v�rdsed;

  2. f(n) = O(g(n)) Ù g(n) = O(h(n)) É f(n) = O(h(n))
    f(n) = W(g(n)) Ù g(n) = W(h(n)) É f(n) = W(h(n))
    see t�hendab, et seosed O ja W on transitiivsed, t�pselt nagu seosed ja arvudel; sellest j�reldub, et ka seos Q on transitiivne, t�pselt nagu seos = arvudel;

  3. f(n) = Q(h(n)) Ù g(n) = O(h(n)) É (f(n) + g(n)) = Q(h(n))
    kahe funktsiooni summa kasvu m��rab kiirema kasvuga liidetav;

  4. f1(n) = Q(g1(n)) Ù f2(n) = O(g2(n)) É (f1(n) f2(n)) = O(g1(n) g2(n))
    f1(n) = Q(g1(n)) Ù f2(n) = W(g2(n)) É (f1(n) f2(n)) = W(g1(n) g2(n))
    seda omadust v�ib t�lgendada nii, et kahe funktsiooni korrutise kasv on tegurite kasvude korrutis;

  5. kui p(n) on k. astme pol�noom, siis p(n) = Q(nk)
    see t�hendab, et pol�noomi kasvu m��rab selle k�rgeima astmega liige; v�ga kasulik omadus, mille p�hjal v�ime mistahes pol�noomi uurimise asendada �ksliikme uurimisega;

  6. " 0 r s : nr = O(ns)
    see t�hendab, et madalama astmega astmefunktsioon ei kasva kiiremini k�rgema astmega astmefunktsioonist; kehtib ka tugevam v�ide: madalama astmega funktsioon kasvab alati rangelt aeglasemalt;

  7. " k 0, b > 1 : nk = O(bn)
    see t�hendab, et mitte �kski astmefunktsioon ei kasva kiiremini �hestki eksponentfunktsioonist; tegelikult kasvab astmefunktsioon alati rangelt aeglasemalt;

  8. " k > 0, b > 1 : logb n = O(nk)
    see t�hendab, et �kski logaritmfunktsioon ei kasva kiiremini �hestki astmefunktsioonist; tegelikult kasvab logaritmfunktsioon alati rangelt aeglasemalt;

  9. " b1 > 1, b2 > 1 : logb1 n = Q(logb2 n)
    see t�hendab, et k�ik logaritmfunktsioonid kasvavad sama kiirusega;

  10. " r 0 : i = 1n ir = Q(nr + 1)
    see t�hendab, et r. j�rku astmerea summa kasvab nagu (r+1). astme pol�noom.
Vaatleme n�iteks funktsioone f(n) = (n2 - n) / 2 ja g(n) = 6 n ning uurime nende kasvukiirusi. Selliste lihtsate funktsioonide puhul pole muidugi raske vahetult tuletada, et f(n) > c g(n), kui n > 12 c - 1 ja sellest j�reldada, et f(n) O(g(n)), f(n) = W(g(n)) ja f(n) Q(g(n)).

Pannes t�hele, et f(n) = (n2 - n) / 2 = 0,5 n2 - 0,5 n on 2. astme pol�noom, saame omaduse 5 p�hjal f(n) = Q(n2). Kuna g(n) on ilmselt 1. astme pol�noom, kehtib sama omaduse p�hjal g(n) = Q(n). Edasi saamegi omaduse 6 alusel f(n) O(g(n)), f(n) = W(g(n)) ja f(n) Q(g(n)), mis muidugi ei erine eelmises l�igus otseselt saadud tulemustest.

Erinevalt eelmise n�ite lihtsatest funktsioonidest oleks f(n) = n sqrt(n) ja g(n) = n + logn kasvukiiruste v�rdlemine otseselt seoste O, W ja Q definitsioonide p�hjal �sna t�likas. Neid funktsioone on oluliselt mugavam v�rrelda seoste O, W ja Q eeltoodud omadusi kasutades.

T�epoolest, elementaaralgebrast on teada, et f(n) = n sqrt(n) = n1,5 ning omaduste 8 ja 3 p�hjal saame g(n) = n + logn = O(n). Edasi on omaduse 6 n�rgema v�ite p�hjal n�ha, et g(n) = O(f(n)) ehk f(n) = W(g(n)); sama omaduse tugevama v�ite p�hjal saame f(n) O(g(n)), millest omakorda j�reldub f(n) Q(g(n)).

Avaldiste teisendamisel kirjutatakse sageli n�iteks f(n) = n + logn = Q(n) + O(n) = Q(n). Selle (rangelt v�ttes veidi ebakorrektse) kirjutise t�hendus on, et f(n) koosneb kahest liidetavast, milles �ks kasvab t�pselt sama kiiresti kui n ja teine mitte kiiremini kui n, seega peab ka summa kasvama t�pselt sama kiiresti kui n.

Ebakorrektsus seisneb selles, et O(n) pole konkreetne funktsioon, mida oleks v�imalik m�ne teise funktsiooniga liita. Siiski on selline notatsioon just mugavuse t�ttu laialt levinud. Tuleb ainult olla ettevaatlik, et mitte teha eba�igeid teisendusi. N�iteks ``taandamine'' f(n) = logn / loglogn = O(n) / O(n) = O(1) on eba�ige, sest kuigi logn ja loglogn on m�lemad O(n), pole nende kasvukiirused sugugi v�rdsed --- nad ainult kasvavad m�lemad aeglasemalt kui n.

5.2  Algoritmide keerukuse hindamine

J�rgnevates l�ikudes vaatleme t�htsamaid v�tteid algoritmide ajalise keerukuse hindamiseks. Kuna just juhtkonstruktsioonid m��ravad, milliseid primitiive ja kui palju kordi t�idetakse, ei tohiks olla eriline �llatus, et nii algoritmi ajaline keerukus kui ka selle hindamise v�tted on otseselt seotud algoritmi struktuuriga.

Mahulise keerukusega tegeleme j�rgmistes peat�kkides andmestruktuure vaadeldes, sest programmi m�luvajadus s�ltub otseselt just andmete, mitte algoritmi struktuurist.

5.2.1  Maksumuse mudel

Selleks, et hinnata algoritmi k�igi primitiivide t�itmiseks kuluvat koguaega, on muidugi vaja teada iga primitiivi t�itmiseks kuluvat aega. Seda infot k�igi primitiivide kohta kokku nimetatakse s�steemi maksumuse mudeliks (ingl cost model). Erinevate operatsioonide maksumused v�ivad s�ltuda nii arvuti riistvarast, operatsioonis�steemist kui ka kasutatavatest programmeerimisvahenditest.

Lihtsaima mudeli puhul eeldame, et k�igi primitiivide maksumused on v�rdsed. Sellisel juhul saame algoritmi ajaliseks keerukuseks primitiivi maksumuse ja t�idetud primitiivide arvu korrutise. Kuigi selline mudel pole peaaegu kunagi p�ris t�pne, on see sageli siiski piisav.

N�iteks v�ime arvutusalgoritmide hindamisel v�rdsustada k�ik aritmeetilised tehted k�igi standardsete arvut��pide vahel. Kuigi tehte sooritamiseks kuluv aeg s�ltub tavaliselt nii sooritatavast tehtest kui ka operandide t��pidest (ja veel paljudest muudest asjadest), on need erinevused suhteliselt v�ikesed ja, mis peamine, konstantsed. Seega, kui meid huvitab ainult t��aja kasvu s�ltuvus algandmetest, pole �ksikute tehete vahelistel erinevustel meie jaoks erilist t�htust.

Selline lihtne mudel pole aga piisav, kui programmeerimiss�steemi primitiivide hulgas on ka keerulisemaid operatsioone. N�iteks mingi andmehulga sorteerimine v�i sellest hulgast vajalike andmete leidmine on operatsioonid, mille ajakulu ei s�ltu mitte ainult andmet��pidest, vaid ka andmemahtudest. Selliseid s�ltuvusi enam eirata ei v�i.

5.2.2  Lineaarne algoritm

Algoritmi, milles ainsa juhtkonstruktsioonina on kasutusel j�rjend, nimetatakse lineaarseks (ingl linear). Kuna sellises algoritmis t�idetakse iga primitiivi t�pselt �ks kord, on terve algoritmi t�itmiseks kuluv aeg primitiivide t�itmisaegade summa.

T�psemalt, kui algoritm on kujul
k�sk-1
k�sk-2
...
k�sk-k
ning k�sk-i t�itmiseks kuluv aeg on fi(n), siis avaldub algoritmi t�itmise koguaeg T(n) kujul
T(n) = f1(n) + f2(n) + ... + fk(n).

Erijuhul, kui k�igi primitiivide t�itmisajad on konstandid, on seda ilmselt ka terve lineaarse algoritmi t�itmise aeg.

Kui primitiivide t�itmise ajad on keerukamad funktsioonid, v�ib kasvukiiruse avaldise lihtsustamiseks kasutada l�igus 5.1.3 vaadeldud omadusi.

5.2.3  Hargnemistega algoritm

Algoritmi, milles juhtkonstruktsioonidena on kasutusel j�rjend ja valik, nimetatakse hargnemistega (ingl branching) algoritmiks. Sellises algoritmis t�idetakse iga primitiivi �limalt �ks kord, seega ei saa algoritmi t�itmiseks kuluv aeg mingil juhul �letada k�igi primitiivide t�itmisaegade summat, k�ll aga v�ib olla sellest v�iksem.

T�psemalt, kui algoritm on kujul
kui tingimus
    t�ene-k�sud
muidu
    v��r-k�sud
l�ppkui
ning t�ene-k�sud ajaline keerukus on f1(n), v��r-k�sud ajaline keerukus f2(n) ja tingimuse t�ev��rtuse arvutamise keerukus f0(n), siis terve algoritmi ajaline keerukus on parimal juhul
Tmin(n) = f0(n) + min(f1(n), f2(n))
ja halvimal juhul
Tmax(n) = f0(n) + max(f1(n), f2(n)).
Kui tingimus kehtib t�en�osusega p4, on algoritmi keskmine keerukus
T(n) = f0(n) + p f1(n) + (1 - p) f2(n).

Selle valemi p�hjenduseks paneme t�hele, et igal juhul tuleb v��rtustada tingimus, kulutades selleks f0(n). Edasi tuleb t�en�osusega p t�ita t�ene-k�sud, kulutades selleks f1(n), v�i t�en�osusega 1 - p t�ita v��r-k�sud, kulutades selleks f2(n). Kui me k�ivitame seda algoritmi M korda, siis keskmiselt p M juhul on tingimus t�ene, �lej��nud M - p M = (1 - p) M juhul aga on tingimus v��r. Kokku kulutame algoritmi M-kordsel k�ivitamisel M f0(n) + p M f1(n) + (1 - p) M f2(n), mis annabki �he k�ivitamiskorra keskmiseks hinnaks f0(n) + p f1(n) + (1 - p) f2(n).

Vaatleme n�iteks �hest valikulausest koosnevat algoritmi:
--- n, k N; n k > 0
kui n / k N
    --- midagi, mis on Q(n)
muidu
    --- midagi, mis on Q(1)
l�ppkui
Kui n ja k on programmeerimiss�steemi standardsed t�isarvud, v�ime eeldada, et nende jaguvus on kontrollitav konstantse ajaga. Seega on selle algoritmi ajalise keerukuse hinnangud vastavalt eelpool toodud seostele

5.2.4  Iteratiivne algoritm

Algoritmi, milles juhtkonstruktsioonina on kasutusel kordus, nimetatakse iteratiivseks (ingl iterative). Sellises algoritmis t�idetakse korduslause kehas olevaid primitiive korduvalt, seega v�ib selle keerukus olla �sna suur.

Kui iteratiivne algoritm on kujul
korda i1 ... k
    k�sud
l�ppkorda
ning k�sud ajaline keerukus on f(n), siis on kogu korduslause ajaline keerukus muidugi k f(n).

Vaatleme n�iteks juba tuttavat algoritmi arvujada liikmete summa leidmiseks:
--- a1 ... n --- summeeritav jada
s0
korda i1 ... n
    ss + ai
l�ppkorda
v�ljasta s
Korduse kehaks on siin �ks liitmis- ja �ks omistamistehe, mille t�itmiseks kulub ilmselt konstantne hulk aega, seega antud juhul f(n) = Q(1). Seda korduse keha t�idetakse t�pselt n korda, seega on korduse keerukuseks n Q(1). Lisaks sellele t�idetakse enne kordust veel �ks omistamine (keerukus Q(1)) ja p�rast seda �ks v�ljastamine (j�lle Q(1)). Seega on algoritmi kogukeerukus
T(n) = Q(1) + n Q(1) + Q(1) = Q(1) + Q(n) + Q(1) = Q(n).

�lesanne muutub raskemaks, kui korduse keha t�itmise hind pole korduse k�igil l�bimistel sama. Sellisel juhul peame k�sud keerukuse avaldama kahe muutuja funktsioonina f(n, i) ja kogu korduslause ajaline keerukus avaldub summana
T(n) =
k
i = 1
f(n, i) = f(n, 1) + f(n, 2) + ... + f(n, k).

V�liselt on summa sarnane eelpool vaadeldud lineaarse algoritmi keerukuse avaldisega, kuid sellest ei tohi ennast petta lasta. Oluline erinevus v�rreldes lineaarse algoritmiga seisneb asjaolus, et lineaarses algoritmis on summas esinevate liidetavate arv m��ratud programmi struktuuriga, kordusega algoritmis aga v�ib s�ltuda sisendandmetest.

Vaatleme n�iteks kahest pesastatud kordusest koosnevat algoritmi, mis loendab antud arvujadas k�ik inversioonid (paarid, kus suurem arv on jadas v�iksemast eespool):
--- a1 ... n --- uuritav jada
k0
korda i1 ... n
    korda ji + 1 ... n
       kui ai > aj
          kk + 1
       l�ppkui
    l�ppkorda
l�ppkorda
v�ljasta k
Selliste pesastatud juhtkonstruktsioonidega algoritmide keerukust on sageli lihtsam arvutada, liikudes algoritmi struktuuris seestpoolt v�ljapoole.

Valikulauses on nii arvude v�rdlemine kui ka tingimuslikult t�idetav omistamine konstantse ajaga operatsioonid, seega on kogu valikulause ajaline keerukus Q(1).

Valikulause ise on sisemise korduslause keha, teda t�idetakse j v��rtustel i + 1 kuni n, see t�hendab t�pselt n - i korda. Kuna selle korduse keha t�itmise maksumus on k�igil l�bimistel sama, on meil tegemist lihtsama juhuga ja korduse kogukeerukus f(n, i) = (n - i) Q(1).

Sisemine korduslause omakorda on v�limise korduse keha, teda t�idetakse i v��rtustel 1 kuni n. Kuna selle korduse keha t�itmise maksumus on erinevatel l�bimistel erinev, on meil seekord tegemist keerukama juhuga ja korduse kogukeerukus
T(n) = (n - 1) Q(1) + (n - 2) Q(1) + ... + (n - n) Q(1)  
  = ((n - 1) + (n - 2) + ... + (n - n)) Q(1)  
  =
n(n - 1)
2
  Q(1) = Q(n2) Q(1) = Q(n2).
 

Toodud n�ites on �leminek teiselt realt kolmandale tehtud aritmeetilise jada summa valemi p�hjal. Kahjuks pole selliste summade lihtsustamiseks �ldist eeskirja --- neisse tuleb suhtuda loominguliselt.

Muidugi tasub v�imalusel alati kasutada seoste O ja Q l�igus 5.1.3 vaadeldud omadusi, korduslausete puhul on sageli abiks just omadus 10.

Osutub, et ka k�esoleval juhul saaks me kasutada just seda omadust: T(n) avaldises
T(n) = ((n - 1) + (n - 2) + ... + (n - n)) Q(1)
v�ime sulgudes olevat summat S teisendada j�rgmiselt:
S = (n - 1) + (n - 2) + ... + (n - n)  
  = 0 + 1 + ... + (n - 1)  
  =
0 +
n
i = 1
i - n
 
  = Q(1) + Q(n2) - Q(n) = Q(n2).  

Veel v�ib olla kasulik meeles pidada, et O-hinnanguid v�ib, erinevalt Q-hinnanguist, anda suvalise liiaga. See t�hendab, et kui korduse keerukuse t�pse avaldise lihtsustamine ei �nnestu, v�ime Q-hinnangu asemel anda m�ningase liiaga O-hinnangu, ``�mardades'' k�ik liidetavad �lespoole mingiks lihtsustamiseks soodsama kujuga avaldiseks.

N�iteks inversioonide loendamise programmi keerukuse avaldise lihtsustamisel v�ime kasutada �ra asjaolu, et k�igis liidetavates on esimene korrutis n-st v�iksem arv, seega saame
T(n) = (n - 1) Q(1) + (n - 2) Q(1) + ... + (n - n) Q(1)  
  < n Q(1) + n Q(1) + ... + n Q(1)  
  = n2 Q(1) = Q(n2)  
T(n) = O(n2).  

Selle n�ite puhul juhtus, et ka ``�lespoole �mardatud'' hinnang on t�pne, aga alati ei pruugi see nii olla. Sellep�rast ei v�i me liiaga antud hinnangu avaldamisel enam kasutada seost Q.

5.2.5  Rekursiivne algoritm

Rekursiivse alamprogrammina avaldatavat algoritmi nimetatakse samuti rekursiivseks (ingl recursive). Nagu rekursiivne alamprogramm ise defineeritakse iseenda kaudu, tehakse seda ka tema keerukusfunktsiooniga.

Vaatleme n�iteks juba varasemast tuttavat rekursiivset algoritmi antud naturaalarvu n faktoriaali leidmiseks:
--- n N
kui n = 0
    tagasta 1
muidu
    --- rekursioon
    tagasta n * (n - 1)!
l�ppkui
Selle algoritmi t��aja hinnangu T(n) v�ib avaldada kujul
T(n) =

Q(1), kui n = 0,
T(n - 1) + Q(1), kui n > 0,
kus juhul n > 0 v�ljendab T(n - 1) rekursiivsele v�ljakutsele kuluvat aega ja Q(1) selle v��rtuse kasutamist n! arvutamisel.

Sellised rekurrentsed (ingl recurrent) v�rrandid ja v�rrandis�steemid pole matemaatikas midagi haruldast. K�ige �ldisem meetod nende lahendamiseks on: tuleb rekurrentseid p��rdumisi m�ned korrad avaldisse sisse asendada ja p��da vastuse �ldkuju �ra arvata. Tekkinud h�poteeside t�estamiseks on tavaliselt k�ige parem matemaatilise induktsiooni meetod.

N�iteks faktoriaali leidmise algoritmi puhul saame
T(n) = T(n - 1) + Q(1)  
  = T(n - 2) + Q(1) + Q(1)  
  = T(n - 3) + Q(1) + Q(1) + Q(1),  
millest on juba k�llalt lihtne pakkuda T(n) = (n + 1) Q(1) = Q(n).

Tabelis 5.3 on �ra toodud m�nede rekursiivsete algoritmide anal��simisel sageli esinevate rekurrentsete v�rrandite lahendid. Tabeli vasakpoolses veerus on antud T(n) avaldis n > 0 jaoks, lisaks eeldatakse k�igil juhtudel rajatingimust T(0) = Q(1).

T(n) avaldis lahend
T(n / c1) + Q(1) Q(logn)
T(n - 1) + Q(1) Q(n)
c2 T(n / c2) + Q(1) Q(n)
c3 T(n / c3) + Q(n) Q(n logn)
T(n - 1) + Q(n) Q(n2)
c4 T(n - 1) + Q(1) Q(c4n)

Table 5.3: M�nede sageli esinevate rekurrentsete v�rrandite lahendid


5.3  Praktiline n�uanne

Algoritmide efektiivsuse hindamisele p�hendatud peat�ki l�petuseks tasub m�rkida, et efektiivsus pole siiski algoritmi k�ige t�htsam omadus. �igsus on alati efektiivsusest olulisem. T�epoolest, millist kasu v�iks loota programmist, mis t��tab k�ll v�lkkiirelt, kuid annab valesid vastuseid?

�lesanded


�lesanne 1   Kontrollida otseselt seoste O, W ja Q definitsioonidest l�htudes, kas kehtivad v�ited
(a) 2n+1 = O(2n); (d) 22n = O(2n);
(b) 2n+1 = W(2n); (e) 22n = W(2n);
(c) 2n+1 = Q(2n); (f) 22n = Q(2n).
P�hjendada oma vastuseid ka l�igus 5.1.3 toodud omaduste abil.

�lesanne 2   Vaatleme j�rgmisi funktsioone:
sqrt(n) n 2n n logn
n - n3 + 7n5 n2 + logn n2 n3
logn n1/3 + logn (logn)2 log2 n
loglogn (1/3)n (3/2)n 6
Grupeerida need funktsioonid nii, et �hes grupis oleks koos sama kasvukiirusega funktsioonid. J�rjestada grupid omavahel funktsioonide kasvukiiruste kasvamise j�rjekorras. (K�igi nende funktsioonide kasvukiirused on omavahel v�rreldavad.)

�lesanne 3   Vaatleme j�rgmist algoritmi:
--- n N
korda
i1 ... n
    korda
j1 ... i
       v�ljasta
i, j
    l�ppkorda

l�ppkorda
Milline on selle algoritmi ajaline keerukus parimal, halvimal ja keskmisel juhul? Kas teie antud hinnangud on t�psed v�i liiaga? P�hjendada oma vastuseid.

�lesanne 4   Vaatleme algoritmi Bitte naturaalarvu kahendkujus esinevate 1-bittide loendamiseks (kus  div ja  mod t�hendavad t�isarvulise jagamise jagatist ja j��ki):
--- n N
kui
n = 0
    tagasta
0
muidu

    tagasta
Bitte(n  div  2) + (n  mod  2)
l�ppkui
Milline on selle algoritmi ajaline keerukus?


Kirjandus



J�ri Kiho. Algoritmid ja andmestruktuurid. Tartu �likool, 2003.
Andmestruktuuride realiseerimist ja klassikalisi algoritme k�sitlev �pik, milles leidub ka keerukuse p�him�istetele p�hendatud peat�kk. 148 lk.


Valdo Praust. Keerukusteooria alused. �likoolide Informaatikakeskus, 1996.
Algoritmide ja �lesannete keerukuse teoreetilisemale poolele keskenduv �pik. 212 lk.


Thomas H. Cormen, Clifford Stein, Charles Leiserson, Ronald Rivest. Introduction to Algorithms. MIT, 2001.
Eelmise tr�kiga klassikaks saanud algoritmide ja andmestruktuuride p�hi�pik, mis annab �sna hea �levaate ka algoritmide keerukuse hindamisest ja selleks vajalikust matemaatilisest aparatuurist. 1180 lk.


Томас Кормен, Чарльз Лейзерсон, Рональд Ривест. Алгоритмы: построение и анализ. МЦНМО, 2001.
Eelmise t�lge. 960 lk.


Herbert S. Wilf. Algorithms and Complexity. Prentice Hall, 1986.
�likooli keskmiste kursuste tasemele orienteeritud konspekt. 240 lk.
http://www.cis.upenn.edu/~wilf/AlgComp2.html



1
Eksponentfunktsiooniks nimetatakse funktsiooni kujul ax, kus a on konstant ja x funktsiooni argument. Mitte segi ajada astmefunktsiooniga, mis avaldub kujul xa.
2
Astmefunktsioonid on pol�noomide lihtsamad erijuhud.
3
See ju ongi funktsioonide m�te!
4
Siin ja edaspidi m�rgime t�en�osusi mitte protsentides, vaid reaalarvudega 0 ... 1. Arvestades, et protsent t�hendab sajandikku, saame 20% = 20 / 100 = 0,2. Vahe on selles, et viimast kuju kasutades ei pea valemites t�en�osusi sajaga jagama, mis muudab valemid veidi lihtsamaks ja �levaatlikumaks.

Previous Up Next