OSNOVNI ALGORITMI Boˇstjan Vilfan 23. januar 2011 CIP - Kataloˇzni zapis o publikacijah Narodna in univerzitetna knjiˇznica, Ljubljana 510.5:5:004.42(075.8) VILFAN, Boˇstjan Osnovni algoritmi / Boˇstjan Vilfan - 2. izdaja. - Ljubljana : Fakulteta za raˇcunalniˇstvo in informatiko, 2002 ISBN 961-6209-13-2 116054784 c Copyright 2002 Zaloˇzba FE in FRI. All rights reserved. Razmnoˇzevanje (tudi fotokopiranje) dela v celoti ali po delih brez predhodnega dovoljenja Zaloˇzbe FE in FRI prepovedano. Recenzenta: strokovni – dr. Borut Robiˇc, izr. prof. jezikovni – Alma Korenini, prof. Zaloˇzila: Fakulteta za raˇcunalniˇstvo in informatiko, 2002 ˇ Urednik: mag. Peter Sega Natisnil: FORMATISK, Ljubljana Naklada: 200 izvodov 2. izdaja Kazalo PREDGOVOR vii 1 ALGORITMI 1.1 Pojem algoritma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Preverjanje pravilnosti . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Preverjanje pravilnosti s poskusi . . . . . . . . . . . . . . . . . 1.2.2 Logiˇcno dokazovanje pravilnosti . . . . . . . . . . . . . . . . . . 1.2.3 Dokazovanje pravilnosti programov, ki so predstavljeni z diagrami poteka . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.4 Dokazovanje pravilnosti programov, ki so zapisani v programskem jeziku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Preverjanje ustavljanja . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4 Poraba ˇcasa in prostora . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Zapis za asimptotiˇcno rast funkcij . . . . . . . . . . . . . . . . ˇ 1.4.2 Cas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3 Prostor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5 Povzetek osnovnih pojmov . . . . . . . . . . . . . . . . . . . . . . . . . 1.6 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 15 16 18 19 22 22 23 2 UREJANJE 2.1 Naloga urejanja in klasifikacija metod urejanja . . . . . 2.2 Notranje urejanje . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Navadno vstavljanje . . . . . . . . . . . . . . . . 2.2.2 Navadno izbiranje . . . . . . . . . . . . . . . . . 2.2.3 Navadne zamenjave . . . . . . . . . . . . . . . . 2.2.4 Spodnja meja za ˇcas urejanja tabel . . . . . . . . 2.2.5 Shellovo urejanje (izboljˇsano vstavljanje) . . . . 2.2.6 Urejanje s kopico (izboljˇsano izbiranje) . . . . . . 2.2.7 Urejanje s porazdelitvami (izboljˇsane zamenjave) 2.2.8 Primerjava razliˇcnih metod urejanja . . . . . . . 2.2.9 Iskanje k-tega elementa v tabeli . . . . . . . . . . 2.3 Zunanje urejanje . . . . . . . . . . . . . . . . . . . . . . 25 25 29 30 34 34 35 39 41 44 51 51 53 iii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 4 4 5 6 iv KAZALO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 57 59 62 69 75 76 3 ALGORITMI Z REKURZIVNIM RAZCEPOM 3.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Mnoˇzenje matrik . . . . . . . . . . . . . . . . . . . . . 3.2.1 Metoda S. Winograda . . . . . . . . . . . . . . 3.2.2 Metoda V. Strassena . . . . . . . . . . . . . . . 3.3 Diskretna Fourierjeva transformacija in algoritmi zanjo 3.3.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . 3.3.2 Diskretna Fourierjeva transformacija . . . . . . 3.3.3 Interpretacija DFT s polinomi . . . . . . . . . 3.3.4 Konvolucija polinomov . . . . . . . . . . . . . . 3.3.5 Rekurzivni algoritem za DFT . . . . . . . . . . 3.3.6 Iterativni algoritem za DFT . . . . . . . . . . . 3.4 Povzetek osnovnih pojmov . . . . . . . . . . . . . . . . 3.5 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 79 79 80 81 82 82 82 84 85 89 91 98 98 ˇ ˇ 4 POZRE SNI ALGORITMI 4.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . 4.2 Osnovna zgradba poˇzreˇsnega algoritma . . . . 4.3 Razvrˇsˇcanje zapisov na magnetnem traku . . 4.4 Razvrˇsˇcanje poslov v delavnici z enim strojem 4.5 Povzetek osnovnih pojmov . . . . . . . . . . . 4.6 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 101 103 103 105 107 109 2.4 2.5 2.3.1 Navadno zlivanje . . . . . . . . . . . . . . . 2.3.2 Naravno, uravnoteˇzeno, dvosmerno zlivanje 2.3.3 Naravno, uravnoteˇzeno, veˇcsmerno zlivanje 2.3.4 Polifazno urejanje . . . . . . . . . . . . . . 2.3.5 Polifazno urejanje s predurejanjem . . . . . Povzetek osnovnih pojmov . . . . . . . . . . . . . . Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 PRETOKI IN LINEARNO PROGRAMIRANJE 5.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Problem maksimalnega pretoka skozi omreˇzje . . . . . . . . . . . . . . 5.2.1 Definicija problema . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.2 Algoritem za maksimalni pretok . . . . . . . . . . . . . . . . . 5.3 Linearno programiranje . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2 Konveksne poliedrske mnoˇzice . . . . . . . . . . . . . . . . . . . 5.3.3 Maksimum linearne funkcije na konveksni poliedrski mnoˇzici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.4 Lokalni pogoj za globalni maksimum linearne funkcije na konveksni poliedrski mnoˇzici . . . . . . . . . . . . . . . . . . . . . 5.3.5 Simpleksni algoritem . . . . . . . . . . . . . . . . . . . . . . . . 111 111 111 111 115 117 117 119 121 123 125 v KAZALO 5.4 5.5 Povzetek osnovnih pojmov . . . . . . . . . . . . . . . . . . . . . . . . . 127 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 ˇ ˇ POTI 6 DINAMICNO PROGRAMIRANJE IN NAJCENEJSE 6.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2 Problem 0-1 nahrbtnika . . . . . . . . . . . . . . . . . . . . . . . . 6.3 Problem najcenejˇsih poti . . . . . . . . . . . . . . . . . . . . . . . . 6.3.1 Najcenejˇse poti med zaˇcetnim vozliˇsˇcem in vsemi ostalimi . 6.3.2 Najcenejˇse poti med vsemi pari vozliˇsˇc . . . . . . . . . . . . 6.4 Prevedba 0-1 nahrbtnika na najcenejˇse poti . . . . . . . . . . . . . 6.5 Povzetek osnovnih pojmov . . . . . . . . . . . . . . . . . . . . . . . 6.6 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 131 132 140 141 149 151 153 154 7 SESTOPANJE 7.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Problem skakaˇcevega obhoda . . . . . . . . . . . . . . 7.3 Problem osmih dam . . . . . . . . . . . . . . . . . . . 7.4 Problem trdnih zakonov . . . . . . . . . . . . . . . . . 7.5 Naˇcini za omejevanje pretiranega razraˇsˇcanja iskalnega 7.6 Povzetek osnovnih pojmov . . . . . . . . . . . . . . . . 7.7 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . drevesa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 157 158 161 164 166 173 174 8 ISKANJE PO ZNAKOVNIH ZAPOREDJIH 8.1 Navadni algoritem za iskanje po znakovnih zaporedjih 8.2 Knuth-Morris-Prattov algoritem . . . . . . . . . . . . 8.3 Boyer in Mooreov algoritem . . . . . . . . . . . . . . . 8.4 Povzetek osnovnih pojmov . . . . . . . . . . . . . . . . 8.5 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 177 178 181 185 185 9 VZPOREDNI ALGORITMI 9.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . 9.2 Metoda prestavljanja kazalcev . . . . . . . . . . . 9.3 Asociativni produkt zaˇcetnih elementov seznama 9.4 Izraˇcun globine vozliˇsˇc dvojiˇskega drevesa . . . . 9.5 Povzetek osnovnih pojmov . . . . . . . . . . . . . 9.6 Naloge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 187 189 191 193 195 195 ˇ A KRATEK PRIROCNIK JEZIKA OBERON-2 A.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . A.2 Sintaksa . . . . . . . . . . . . . . . . . . . . . . . A.3 Osnovni elementi jezika ter njihova predstavitev . A.4 Deklaracije ter pravila o veljavnosti objektov . . A.5 Deklaracije konstant . . . . . . . . . . . . . . . . A.6 Deklaracije tipov . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 197 197 198 199 200 200 vi KAZALO A.6.1 Osnovni tipi . . . . . . . . . . . . . . . . . . A.6.2 Tabelariˇcni tipi . . . . . . . . . . . . . . . . A.6.3 Tipi zapisov . . . . . . . . . . . . . . . . . . A.6.4 Tipi kazalcev . . . . . . . . . . . . . . . . . A.6.5 Tipi procedur . . . . . . . . . . . . . . . . . A.7 Deklaracije spremenljivk . . . . . . . . . . . . . . . A.8 Izrazi . . . . . . . . . . . . . . . . . . . . . . . . . . A.8.1 Operandi . . . . . . . . . . . . . . . . . . . A.8.2 Operatorji . . . . . . . . . . . . . . . . . . . A.9 Stavki . . . . . . . . . . . . . . . . . . . . . . . . . A.9.1 Prirejanje . . . . . . . . . . . . . . . . . . . A.9.2 Klici procedur . . . . . . . . . . . . . . . . A.9.3 Stavˇcna zaporedja . . . . . . . . . . . . . . A.9.4 Pogojni stavki . . . . . . . . . . . . . . . . A.9.5 Izbirni stavek . . . . . . . . . . . . . . . . . A.9.6 Stavek While . . . . . . . . . . . . . . . . . A.9.7 Stavek Repeat . . . . . . . . . . . . . . . . A.9.8 Stavek For . . . . . . . . . . . . . . . . . . A.9.9 Stavek Loop . . . . . . . . . . . . . . . . . . A.9.10 Stavka Return ter Exit . . . . . . . . . . . . A.9.11 Stavek With . . . . . . . . . . . . . . . . . A.10 Deklaracije procedur . . . . . . . . . . . . . . . . . A.10.1 Formalni paramteri . . . . . . . . . . . . . . A.10.2 Procedure, ki so pridruˇzene tipom . . . . . A.10.3 Vnaprej deklarirane procedure . . . . . . . A.11 Moduli . . . . . . . . . . . . . . . . . . . . . . . . . A.12 Definicije pojmov . . . . . . . . . . . . . . . . . . . A.12.1 Skladnost seznamov formalnih parametrov . A.13 Sintaksa jezika Oberon-2 . . . . . . . . . . . . . . . A.14 Modul SYSTEM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 201 202 203 203 203 204 204 205 207 207 208 208 208 209 209 209 210 210 211 211 211 212 214 215 216 218 220 220 222 ˇ B PREDIKATNI RACUN PRVEGA REDA 225 B.1 Dodatni primeri uporabe predikatnega raˇcuna . . . . . . . . . . . . . . 229 Literatura 231 Stvarno kazalo 233 PREDGOVOR Predgovor k prvi izdaji Priˇcujoˇca knjiga vsebuje snov, ki je na dodiplomskem ˇstudiju raˇcunalniˇstva in informatike na Fakulteti za raˇcunalniˇstvo in informatiko Univerze v Ljubljani zajeta v predmetu Algoritmi in podatkovne strukture II. Snov obsega nekatere standardne algoritme, ki jih mora poznati vsak strokovnjak s podroˇcja raˇcunalniˇstva, kakor tudi nekatere obiˇcajne metode in naˇcela zasnove algoritmov. Knjiga se zaˇcne s poglavjem “Algoritmi”, ki je namenjeno osnovnim pojmom ter kratkemu pregledu podroˇcij preverjanja pravilnosti in ustavljanja, kakor tudi ocenjevanja porabe ˇcasa in prostora. Sledi poglavje o urejanju, ki obravnava osnovne metode urejanja tabel in datotek, od ostalih poglavij pa se razlikuje po tem, da je prisoten nekoliko veˇcji poudarek na tehniki programiranja kot v drugih poglavjih. Naslednje poglavje je namenjeno metodi rekurzivnega razcepa, obravnava pa dva tipiˇcna problema, ki ju reˇsujemo na ta naˇcin, in sicer mnoˇzenje matrik po Strassenu in izraˇcun diskretne Fourierjeve transformacije. Posebnost tega poglavja je, da zahteva doloˇceno matematiˇcno predizobrazbo (nekaj linearne algebre in nekaj elementarnih dejstev o obiˇcajnih matematiˇcnih strukturah). 4. poglavje obravnava tim. “poˇzreˇsno metodo” zasnove algoritmov za optimizacijske probleme na primeru dveh enostavnih problemov. 5. poglavje je namenjeno problemu maksimalnega pretoka skozi omreˇzje in linearnemu programiranju. Linearno programiranje obravnavamo na poenostavljen naˇcin z namenom, da prikaˇzemo osnovno idejo simpleksnega algoritma in brez obravnave doloˇcenih zapletov, do katerih lahko pride v praktiˇcnih primerih (npr. degeneriranost matrike problema). Kljub temu pa se tudi v tem poglavju seveda nismo mogli izogniti doloˇcenemu matematiˇcnemu predznanju iz linearne algebre. 6. poglavje obravnava metodo dinamiˇcnega programiranja na primeru problema 0-1 nahrbtnika in najcenejˇsih poti v grafu. 7. poglavje je namenjeno sestopanju in tehnikam omejevanja velikosti iskalnega drevesa. 8. poglavje obravnava dva uˇcinkovita algoritma za iskanje po znakovnih zaporedjih, zadnje, 9. poglavje pa predstavlja kratek uvod v vzporedno raˇcunanje na primeru nekaj tipiˇcnih problemov, ki jih je moˇzno na ta naˇcin uˇcinkovito reˇsevati. Priˇcujoˇca knjiga seveda ni nastala iz niˇc in se naslanja na izredno obseˇzno in raznoliko literaturo, ki danes pokriva podroˇcje algoritmov in podatkovnih struktur. Seveda sem nekatera dela veˇckrat uporabljal in med njimi bi na prvem mestu omenil delo Cormen in dr. [5], ki je zelo izˇcrpno in pokriva tako rekoˇc vsa podroˇcja algoˇ ritmov in podatkovnih struktur. Ceprav je kot celota nekoliko prezahtevno za naˇse vii viii PREDGOVOR potrebe, sem se pri mnogih vpraˇsanjih zgledoval prav po njem. Nadalje bi omenil danes ˇze klasiˇcno delo Aho in dr. [1], ki je prav tako zelo izˇcrpno, vendar podobno kot predhodno delo nekoliko prezahtevno. V poglavju o urejanju kakor tudi pri obravnavi preverjanja pravilnosti sem se v mnogoˇcem zgledoval po delih Niklausa Wirtha [16, 20], ki so pred ˇcasom izˇsla tudi v domaˇcem prevodu [17, 19]. Seveda se je do danes nabralo tudi nekaj domaˇce literature in bi lahko omenil poleg ˇze omenjenih prevodov Wirthovih del ˇse deli Kozak [10] in Kononenko [9]. Na koncu seveda gre zahvala vsem, ki so kakorkoli pripomogli temu, da je delo nastalo. Najprej bi omenil vse sodelavce in kolege, ki so s pripombami in nasveti izboljˇsali izbiro ali podajanje snovi. Po abecednem redu so to Tomaˇz Dobravec, Viljan Mahniˇc, Igor Roˇzanc in Boˇstjan Slivnik. Nato bi omenil strokovnega in jezikovnega recenzenta, ki sta s pozornim branjem odkrila marsikateri spodrsljaj in mi z nasveti pomagala, da sem besedilo izboljˇsal. In konˇcno (vendar ne nazadnje) gre zahvala ˇzeni Marjetki in najinim otrokom, ki so me podpirali in ustvarili vzduˇsje, v katerem je delo nastajalo. Ljubljana, januarja 1998 Boˇstjan Vilfan Predgovor k drugi izdaji V drugi izdaji so nastale naslednje spremembe in dopolnitve: 1. Odpravljene so bile tipkarske in druge manjˇse napake. 2. Odpravljene so bile vse odkrite napake v programih, ki se uporabljajo kot primeri. 3. Veˇc krajˇsih drugih dopolnitev in izboljˇsav. 4. V razdelku 2.2.7 je popravljen del, ki se nanaˇsa na spodnjo mejo za ˇcas urejanja. 5. Spremenjen je bil razdelek 3.3.6 o iterativnem algoritmu za diskretno Fourierjevo transformacijo. 6. Pojavil se je nov Dodatek A, ki podaja kratko informacijo o jeziku Oberon-2. Upam, da bodo naˇstete dopolnitve ali popravki prispevali k boljˇsi razumljivosti besedila. Ljubljana, januarja 2002 Boˇstjan Vilfan Predgovor k spletni izdaji Uˇcbenik ni veˇc v uporabi, objavljam pa ga za primer, da bi koga zanimal. Med pripravo za objavo na spletu sem odpravil neke dodatne napake. Ljubljana, januar 2011 Boˇstjan Vilfan Poglavje 1 ALGORITMI 1.1 Pojem algoritma Od bralca priˇcakujemo, da je seznanjen s pojmoma raˇcunalnik in raˇcunalniˇski program ter da je morebiti ˇze napisal in preizkusil kakˇsen program. Ne da bi se spuˇsˇcali v pretirane formalnosti, definiramo algoritem kot program, ki teˇce na nekem raˇcunalniku (ki je bodisi resniˇcen ali namiˇsljen) in na zaˇcetku dobi doloˇcene podatke, na koncu svojega delovanja pa sporoˇci rezultat. Podatkom algoritma pravimo tudi vhod , rezultatu pa izhod . Pomembno je, da je rezultat pri vseh podatkih natanˇcno doloˇcen. Po tem se algoritem razlikuje od sploˇsnejˇse definicije programa, oziroma recepta za izraˇcun, ki ima pri doloˇcenih vhodih lahko tudi nedefiniran rezultat. Glede na to, da je raˇcunalniˇskih modelov izredno veliko (npr. Turingovi stroji, konˇcni avtomati, programi Pomnilnik v vseh mogoˇcih programskih jezikih . . . ) imamo tudi najrazliˇcnejˇse predstavitve algoritmov. n−1 Ker imamo za konˇcni cilj neka spoznanja o algon−2 ritmih, ki naj bi bila uporabna v praktiˇcnih primerih, .. je primerno, da kot osnovo za predstavitev algorit. mov vzamemo stroj, ki je sicer namiˇsljen (abstrakProcesor 2 ten) v pomenu, da predstavlja neko poenostavitev re1 sniˇcnih strojev, vendar kljub temu dovolj soroden resniˇcnim strojem, da omogoˇca stvarno razmiˇsljanje. V 0 teoretiˇcnih raziskavah se je kot takˇsen stroj uveljavil stroj z enakopravnim dosegom do pomnilnika, za Slika 1.1: Raˇcunalnik vrste katerega uporabljamo angleˇsko kratico RAM1 . Stroj RAM je prikazan na sliki 1.1 in je sestavljen iz procesorja, ki je zmoˇzen izvajanja obiˇcajnih operacij (aritmetiˇcne 1 Kratica za angleˇ ski izraz Random Access Machine, ki se veˇ cinoma dobesedno prevaja kot stroj z nakljuˇ cnim dosegom do pomnilnika, vendar sem na podlagi opisa stroja raje izbral drug izraz. 1 2 POGLAVJE 1. ALGORITMI operacije, operacije nad biti, operacije, ki preusmerjajo izraˇcun), in pomnilnika, ˇcigar celice so vse enako dosegljive in lahko vsebujejo podatke poljubne velikosti. Procesor izvaja operacije eno za drugo (zaporedno), vsaka operacija pa traja eno ˇcasovno enoto. Poenostavitev stvarnih strojev, ki je prisotna pri stroju RAM, je, da ne vsebuje mnogih komponent stvarnih raˇcunalnikov (npr. vhodnih in izhodnih enot) in da ne upoˇsteva omejene dolˇzine njihove pomnilniˇske besede. Na ta naˇcin se izogibamo podrobnostim programiranja vhodnih in izhodnih enot in zanemarjamo probleme, ki se pojavljajo v zvezi z zaokroˇzitvami ˇstevilˇcnih koliˇcin. Ker pa ta vpraˇsanja niso predmet ˇ pri opisovanju algoritmov naˇsega zanimanja, s tem le pridobimo na preglednosti. Ce uporabljamo stroj RAM, lahko precej natanˇcno in stvarno ocenjujemo porabo ˇcasa (tako, da ˇstejemo izvrˇsene ukaze) in prostora za podatke (tako, da ocenjujemo ˇstevilo uporabljenih celic pomnilnika), vendar postanejo za naˇse potrebe opisi algoritmov razmeroma nepregledni. Zato ponavadi algoritme opisujemo v nekem viˇsjem jeziku in si predstavljamo RAM le kot “ciljni stroj”, v katerega prevajamo opise, ki so zapisani v viˇsjem jeziku. Torej je vloga stroja RAM le, da nam prikliˇce v spomin osnovne lastnosti algoritmov, ki jih privzemamo pri naˇsih razmiˇsljanjih: zaporedno izvajanje ukazov in enaka dosegljivost pomnilniˇskih celic. V podobnih knjigah, kot je priˇcujoˇca, se zelo pogosto uporablja zapis za algoritme, ki je nekoliko prirejen jezik algolskega tipa (npr. Algol, Pascal, Modula-2 ipd.) v smislu, da se ne drˇzi strogo jezikovne sintakse, kajti namenjen je ˇcloveˇskemu bralcu in ne izvajanju na raˇcunalniku. V tej knjigi pa bomo glede zapisa algoritmov nekoliko neenotni: v poglavjih, ki so zanimiva s staliˇsˇca tehnike programiranja, bomo algoritme predstavljali kot podrobno zapisane programe v jeziku Oberon-2 (gl. npr. Reiser in Wirth [14] ali Mahniˇc [4]), v drugih pa bomo marsikatero podrobnost programiranja zanemarili. Na sliki 1.2 je do zadnje podrobnosti zapisan algoritem dvojiˇskega iskanja, ki ga bomo uporabili za prikazovanje nekaterih lastnosti algoritmov in ki vsebuje neko posebnost glede na obiˇcajno obliko. Zanimiv je zato, ker uporablja pogojni stavek z dvema izidoma primerjanja, ≤, >, namesto obiˇcajnih treh, <, =, >, (gl. tudi Wirth [18, 1. del, 9, str. 38]). Res je, da pri taki obliki lahko pride do odveˇcnega raˇcunanja v primeru, ko je a[mid] = x v vrstici 12 resniˇcno (ker se izvajanje zanke nadaljuje do trenutka, ko vrednost l postane veˇcja od r, oziroma, ko se vrednost r spusti pod l). Vendar je v povpreˇcju prikazana oblika le hitrejˇsa, ker veˇcinoma velja a[mid] 6= x in prihranimo eno primerjanje. Glede izbire jezika Oberon za predstavitev programov je potrebno priznati, da je bila s staliˇsˇca kasnejˇsega razvoja morda manj utemeljena, kajti jezik se v praksi ni ˇsirˇse uveljavil. Vendar, ker ima izredno preprosto zgradbo, menim, da bralcu ne bo teˇzko razumeti programov, ki so zapisani z njim, v pomoˇc pa mu je tudi kratek dodatek z opisom jezika (Dodatek A). Vsi programi so preizkuˇseni s prevajalnikom Native XDS-X86[6], na voljo pa so tudi v elektronski obliki na streˇzniku zaphod.fri.uni-lj.si/~vilfan. Priˇcujoˇca knjiga ima veˇc ciljev. Prvi je zbrati nekaj koristnih algoritmov, ki jih vsak strokovnjak za raˇcunalniˇstvo mora poznati. Drugi je opisati najpogostejˇse prijeme, ki jih uporabljamo pri sestavljanju algoritmov. Tretji je seznaniti bralca z osnovnimi pojmi o preverjanju pravilnosti algoritmov in ˇcetrti, ne najmanj pomem- 1.1. POJEM ALGORITMA 1 2 3 4 5 6 7 8 9 10 11 12 13 15 15 16 17 19 19 3 PROCEDURE BinSearch(VAR a:ARRAY OF INTEGER; x ,n:INTEGER):INTEGER; (∗ a je urejena tabela, kjer ima prvi element indeks 1 (element a[0] ima nedefinirano vrednost), x je iskani element, n ≥ 0 pa ˇstevilo elementov v tabeli; rezultat je indeks iskanega elementa, ˇce je x prisoten v tabeli ali pa negativna vrednost indeksa v tabeli, kamor bi x sodil, ˇce elementa v tabeli ni ∗) VAR l ,mid ,r :INTEGER; BEGIN l :=1; r :=n; WHILE l <=r DO mid :=(l +r )DIV 2; IF a[mid ]>x THEN r :=mid −1 ELSE l :=mid +1 END END ; IF (r >0) & (a[r ]=x ) THEN RETURN r ELSE RETURN −l END END BinSearch; Slika 1.2: Algoritem dvojiˇskega iskanja ben, je prikazati razliˇcne naˇcine za ocenjevanje porabe raˇcunskih virov, med katerimi sta najvaˇznejˇsa ˇcas (ki ga nek algoritem porabi) in prostor (spomin, ki ga algoritem skupaj s svojimi podatki zaseda medtem, ko se izvaja). Preden pa se lotimo vpraˇsanj, ki smo jih pravkar naˇsteli, moramo opisati pojem, ki nam bo omogoˇcil, da delovanje algoritma analiziramo. Rekli mu bomo sled programa. Le-ta je prirejena nekemu programu in konkretnim vhodnim podatkom, je pa preprosto tabela z vrsticami, ki ustrezajo izbranim mestom v programu v ˇcasovnih trenutkih, ki si sledijo, in stolpci, ki vsebujejo vrednosti izbranih spremenljivk. Na primer sled algoritma s slike 1.2 pri vhodnih podatkih a = (9, 13, 27, 32, 41, 48) in x = 18 je prikazana na sl. 1.3a. V vrsticah so prikazane vrednosti spremenljivk na koncu vrstic 9, 12, 13 in 16 oziroma 17. Vˇcasih nas zanima le zaporedje stavkov, ki se izvajajo (na primer zato, da preˇstejemo ˇstevilo stavkov med izraˇcunom). V takem primeru ima sled eno samo vrednost v vrstici — ˇstevilko vrstice stavka. Vˇcasih je smiselno opazovati obnaˇsanje programa pri veˇc vhodih in namesto zaporedja vrednosti v zaporednih ˇcasovnih trenutkih imamo opravka z drevesom sledi. Na primer drevo sledi algoritma s slike 1.2 pri vhodnih podatkih a = (9, 13, 27, 32, 41, 48) in x = 18 ter a = (9, 13, 27, 32, 41, 48) in x = 41, ko nas zanima le zaporedje stavkov, ki se izvaja, ˇ sestavljamo drevo sledi in nas zanima veˇc podatkov je prikazano na sliki 1.3b. Ce (vrednost veˇc spremenljivk), vsebuje vsako vozliˇsˇce drevesa vse potrebne podatke. 4 POGLAVJE 1. ALGORITMI 9 vrstica 9 12 13 13 17 l mid r 1 ? 6 1 3 2 2 1 2 3 2 2 3 2 2 (a) Sled algoritma dvojiˇskega iskanja (konec vrstic 9, 12, 13, 16 in 17) pri podatkih a = (9, 13, 27, 32, 41, 48) in x = 18 12 13 13 13 13 12 17 16 (b) Drevo sledi algoritma dvojiˇskega iskanja pri podatkih a = (9, 13, 27, 32, 41, 48) in x = 18 (leva veja) ter a = (9, 13, 27, 32, 41, 48) in x = 41 (desna veja) Slika 1.3: Sled in drevo sledi. 1 2 3 4 5 6 7 8 PROCEDURE Multiply(x ,y:INTEGER):INTEGER; (∗ x > 0; vrednost je produkt x in y ∗) VAR u,z :INTEGER; BEGIN z :=0; u:=x ; REPEAT z :=z +y; u:=u−1; UNTIL u=0; RETURN z END Multiply; Slika 1.4: Mnoˇzenje dveh ˇstevil 1.2 Preverjanje pravilnosti Ko smo sestavili algoritem, je naprej potrebno preveriti, ali ima vse ˇzelene lastnosti, med katerimi je osnovna, da algoritem resniˇcno dela tisto, kar smo si zamislili, oziroma, da je pravilen. Za ugotavljanje tega sta se izoblikovala dva sploˇsna pristopa: preverjanje pravilnosti s poskusi na konkretnih podatkih in preverjanje pravilnosti z logiˇcno analizo. 1.2.1 Preverjanje pravilnosti s poskusi Ponavadi opravljamo poskuse s programom tako, da sestavljamo sledi delovanja programa pri doloˇcenih vhodnih podatkih. V tem primeru privzemamo, da “vemo”, kaj mora program poˇceti in lahko z analizo sledi pri posameznih vhodnih podatkih ˇ sestavimo drevo sledi, lahko naenkrat preverimo ugotovimo, ali deluje pravilno. Ce algoritem pri veˇc vhodnih podatkih. Kot primer lahko rabi sl. 1.3b, kjer smo preverili pravilno delovanje programa pri vhodnih podatkih a = (9, 13, 27, 32, 41, 48) in x = 18 5 1.2. PREVERJANJE PRAVILNOSTI vrstica 4 5 5 5 5 7 u 4 3 2 1 0 0 z 0 7 14 21 28 28 Slika 1.5: Sled algoritma za mnoˇzenje pri podatkih x = 4, y = 7 ˇ pa ta metoda kot metoda preverjanja pravil(leva veja) ter x = 41 (desna veja). Zal nosti programa pri vseh vhodih odpove, kadar je moˇznih izidov pri algoritmu toliko, da njihovo sledenje presega praktiˇcne moˇznosti. Verjetno je algoritem na sl. 1.2 eden od redkih, ki ga je ˇse moˇzno popolnoma preveriti (z nekoliko logiˇcnega sklepanja) pri praktiˇcnih velikostih problema (gl. nalogo 1). Povsem drugaˇcen problem pa predstavlja preverjanje pravilnosti programa za mnoˇzenje dveh ˇstevil, ki je prikazan na sl. 1.4. Prikazani program bi bil koristen v primeru, ko imamo raˇcunalnik, ki pozna kot elementarni operaciji samo seˇstevanje in odˇstevanje, ne pa mnoˇzenja. Sled algoritma, na podlagi katere sklepamo o njegovi pravilnosti pri podatkih x = 4 in y = 7, je priˇ pa bi se hoteli prepriˇcati o njegovi pravilnosti pri vseh moˇznih kazana na sl. 1.5. Ce parih 16-bitnih ˇstevil x in y, bi morali sestaviti 232 sledi, kar je praktiˇcno nemogoˇce. Zato je potrebno uporabiti popolnoma drugaˇcen pristop. 1.2.2 Logiˇ cno dokazovanje pravilnosti ˇ privzamemo, da ima vsak algoritem nek rezultat, lahko istovetimo nalogo alCe goritma z neko ˇzeleno lastnostjo rezultata. Le-to pa lahko opiˇsemo z neko logiˇcno trditvijo. Ker pa ima tudi sam algoritem (oziroma njegov opis) doloˇcene lastnosti, lahko poskuˇsamo z logiˇcnim sklepanjem ugotoviti, ali iz lastnosti algoritma nujno sledi ˇzelena lastnost rezultata. Vendar je to misel laˇze izreˇci, kot jo uresniˇciti! Ideja logiˇcnega dokazovanja pravilnosti je, da lastnost rezultata in prav tako lastnosti samega algoritma izrazimo s trditvami (stavki) v nekem jeziku formalne logike in nato dokaˇzemo, da iz lastnosti algoritma ter kot posledica ustavitve algoritma nujno sledi lastnost rezultata. Ko se strogo drˇzimo opisane metodologije ali ko za njeno realizacijo uporabljamo nek programski pripomoˇcek, uporabljamo za zapisovanje trditev, ki se nanaˇsajo na lastnosti algoritma, dobro znani predikatni raˇcun prvega reda ali kakˇsen drug logiˇcni formalizem. V dodatku B je zbranih nekaj osnovnih dejstev v zvezi s predikatnim raˇcunom prvega reda, ˇceprav v nadaljevanju uporabljamo le neˇ pa tehniko dokazovanja pravilnosti uporabljamo “roˇcno”, kaj najelementarnejˇsih. Ce ponavadi uporabljamo obiˇcajno neformalno sklepanje, ki ima prednost, da je preglednejˇse in bolj jedrnato, vendar je seveda podvrˇzeno napakam. 6 POGLAVJE 1. ALGORITMI z := 0 u := x Vhod Vhod vhodna podatka: vhodna podatka: x, y z := z + y u := u − 1 − q := 0 r := x r≥y x, y − + u=0 + rezultat: z q := q + 1 r := r − y Izhod (a) Mnoˇ zenje dveh ˇstevil Izhod rezultat: q, r (b) Celoˇstevilˇ cno deljenje Slika 1.6: Dva diagrama poteka 1.2.3 Dokazovanje pravilnosti programov, ki so predstavljeni z diagrami poteka Prvi primer programskega zapisa, za katerega bomo opisali postopek preverjanja pravilnosti, so diagrami poteka. Diagram poteka je usmerjen graf z enim vhodom in morebiti veˇc izhodi, ki ima tri vrste vozliˇsˇc: ena so pravokotne ˇskatle, ki vsebujejo prirejanja (x := f (. . .)), druga romboidne ˇskatle, ki vsebujejo preizkuse oz. predikate (P (x1 , x2 , . . .)), tretja pa so preprosto stekaliˇsˇca poti. Vhod v diagram poteka nakaˇzemo s puˇsˇcico, ki nima izvora, izhod pa s puˇsˇcico, ki nima ponora. Na sliki 1.6 vidimo dva primera, ki vsebujeta vse tri vrste vozliˇsˇc. Vhodni podatki so vrednosti spremenljivk, ki se ne pojavljajo na levih straneh prirejanj, izhodni podatki pa so vrednosti nekaterih izbranih spremenljivk. Za vse spremenljivke privzemamo, da so celoˇstevilˇcne. Pri logiˇcnem dokazovanju pravilnosti programov izhajamo iz pogojev, ki jih morajo izpolnjevati vhodni podatki, in lastnosti, ki doloˇca rezultat izraˇcuna. Na primer program na sliki 1.6a je doloˇcen z vhodnim pogojem, x > 0 ter izhodno lastnostjo, z = x ∗ y, ki sta pripisana toˇckama “Vhod” in “Izhod”, po vrsti. Podobno je vhodni pogoj programa 1.6b x ≥ 0, y > 0, izhodna lastnost pa x = y · q + r ∧ 0 ≤ r < y. Pri zapisovanju logiˇcnih trditev, ki ustrezajo tem in podobnim pogojem, se ne bomo popolnoma drˇzali pravil o zapisu predikatnega raˇcuna ali drugih virih, ki so opisana v dodatku B, saj si bralec vedno lahko predstavlja prevod iz nekoliko ohlapnejˇsega zapisa v povsem strogi zapis. Predvsem se ne bomo omejevali na logiˇcna 7 1.2. PREVERJANJE PRAVILNOSTI S1 Q1 S2 Sn S1 . . . Qn Q2 0 ≤ x < 10 P Q1 ⊃ P ∧ Q2 ⊃ P ∧ .. . Qn ⊃ P T (a) Pravilo S2 −10 < x < 10 −10 < x < 0 T (b) Primer Slika 1.8: Pravilo za stekaliˇsˇce poti in primer uporabe operatorja ⊃ in ¬, temveˇc bomo uporabljali tiste logiˇcne operatorje, ki so najprimernejˇsi za zapis trditve, ki nas zanima (na primer konjunkcija ∧ in disjunkcija, ∨). Nadalje bomo pogosto nadomestili kakˇsen simbol z bolj primernim (in morda manj vsiljivim). Na primer znak za konjunkcijo bomo pogosto nadomestili z vejico. Primer: namesto x > 0 ∧ y > 0 bomo pisali x > 0, y > 0. In konˇcno lahko omenimo, da bomo pogosto za logiˇcno implikacijo namesto znaka ⊃ uporabili znak ⇒. Postopek dokazovanja pravilnosti priˇcnemo tako, da vsakemu vozliˇsˇcu pripiˇsemo nabor trditev, in sicer P (antecedens) po eno vsaki vhodni povezavi in eno izhodni povezavi. Trditvam, ki so pripisane vhodnim povezavam, pravimo antecedensi stavka (oziroma ˇskatle), tistim, S ki so pripisane izhodnim povezavam, pa pravimo konsekvensi stavkov (gl. sliko 1.7). Antecedensi in konQ (konsekvens) sekvens morajo biti v povsem doloˇceni medsebojni zvezi, ki je predpisana z aksiomom, ki pripada vrsti Slika 1.7: Logiˇcne trditve privozliˇsˇca. Aksiomi za vse naˇstete vrste vozliˇsˇc, kakor rejene nekemu stavku tudi primeri njihove uporabe, so prikazani na slikah 1.8, 1.9 in 1.10. Aksiom za stekaliˇsˇce poti [slika 1.8a] zatrjuje, da mora v stekaliˇsˇcu poti vsak antecedens imeti za posledico konsekvens, kar je tudi razumljivo. Namreˇc, ˇce pred stekaliˇsˇcem velja neka trditev, mora veljati tudi po stekaliˇsˇcu. Aksiom za pogojni stavek [slika 1.9a] pravi, ˇce je antecedens pogojnega stavka trditev P , je konsekvens, ki je pripisan izpolnjenemu pogoju B, P ∧ B (razumljivo, saj v primeru, ko B ne bi bilo resniˇcno, program ne bi priˇsel do te toˇcke), konsekvens, ki je pripisan neizpolnjenemu pogoju B, pa je P ∧ ¬B. Aksiom za prirejanje [slika 1.10a] predpisuje, da v primeru, ko je konsekvens prirejanja trditev P in ko le-ta vsebuje prosto spremenljivko2 x, prirejanje pa ima obliko x := f (. . .), je antecedens prirejanja pridobljen iz konsekvensa tako, da zamenjamo 2 Spremenljivka x je prosta v primeru, ko ni v obmoˇ cju veljavnosti nekega kvantifikatorja, ∃ ali ∀. 8 POGLAVJE 1. ALGORITMI −10 < x < 10 P + + − − x<0 B P ∧ ¬B P ∧B 0 ≤ x < 10 −10 < x < 0 (a) Pravilo (b) Primer Slika 1.9: Pravilo za pogojni stavek in primer uporabe Pwv x + y = 10 z := x + y v := w P (a) Pravilo z = 10 (b) Primer Slika 1.10: Pravilo za prirejanje s primerom vse proste primerke spremenljivke x z desno stranjo prirejanja. Na ta naˇcin priˇ nekoliko pomislimo, izraˇza to pravilo dobljeni antecedens oznaˇcujemo z Pfx(...) . Ce natanko uˇcinek prirejanja, kajti po prirejanju je povsod moˇzno desno stran prirejanja nadomestiti z levo stranjo. Celotni postopek je prikazan na primeru programa za mnoˇzenje dveh ˇstevil (slika 1.11). Na tem mestu si bomo omenjeni program nekoliko bolj podrobno ogledali ter izpostavili nekatere znaˇcilnosti. Vhodni pogoj x > 0 nastopa implicitno v vseh trditvah, ne zapisujemo pa ga zato, ker se spremenljivki x in y med izraˇcunom ne spreminjata. Prav tako ne zapisujemo doloˇcenih oˇcitnih tavtologij, ki predstavljajo antecedense nekaterih prirejanj (gl. nalogo 2). V edinem stekaliˇsˇcu poti je eden od antecedensov z = 0, u = x, medtem ko je konsekvens z + u ∗ y = x ∗ y, u > 0. Oˇcitno je, da ima resniˇcnost prve trditve, v povezavi z implicitnim pogojem x > 0, posledico resniˇcnost trditve z + u ∗ y = x ∗ y, u > 0, kar pa je v skladu z aksiomom za stekaliˇsˇca. Nadalje, ˇce antecedens druge ˇskatle s prirejanji, z + u ∗ y = x ∗ y, u > 0, prepiˇsemo v obliko z + y + (u − 1) ∗ y = x ∗ y, u − 1 ≥ 0, preverjanje aksioma za prirejanje ne predstavlja posebnih teˇzav. In konˇcno je negativni izhod iz pogojnega stavka, na podlagi aksioma za pogojni stavek opremljen s trditvijo z + u ∗ y = x ∗ y, u ≥ 0, u 6= 0, kar ima zaradi u ≥ 0 na vhodu v pogojno ˇskatlo za posledico z + u ∗ y = x ∗ y, u > 0, kar pa je enako konsekvensu stekaliˇsˇca. Ker pa vemo, da vedno velja P ⊃ P , smo tako preverili, da so aksiomi za posamezne vrste vozliˇsˇc povsod veljavni in lahko 9 1.2. PREVERJANJE PRAVILNOSTI x>0 z := 0 u := x z = 0, u = x z + u ∗ y = x ∗ y, u > 0 z := z + y u := u − 1 z + u ∗ y = x ∗ y, u ≥ 0 − u=0 + z = x ∗ y, u = 0 Slika 1.11: Program za mnoˇzenje, opremljen s trditvami zatrjujemo, da je pravilnost programa na sliki 1.11 dokazana. Na podoben naˇcin je moˇzno analizirati program na sliki 1.12 za izraˇcun kvocienta in ostanka po deljenju dveh pozitivnih celih ˇstevil. Osnovni element, ki oteˇzuje doloˇcanje trditev prirejenih programu, so zanke. Ko imamo opravka z linearnim zaporedjem vozliˇsˇc (ˇskatel), je obiˇcajno moˇzno na enostaven naˇcin uporabiti aksiome in doloˇciti manjkajoˇce trditve na podlagi bodisi zaˇcetne (vhodne) trditve ali pa konˇcne (izhodne) trditve. V primeru pa, ko imamo opravka z zanko, smo pri izbiri trditev omejeni s pogojem, da mora zadnja trditev imeti za logiˇcno posledico prvo trditev (gl. sliko 1.13). V takem primeru postopamo tako, kot da bi bila pred prvim vozliˇsˇcem zanka prerezana, in imamo nato opravka z linearnim zaporedjem vozliˇsˇc. Ko izberemo trditve za vhode in izhode vseh vozliˇsˇc, preverimo, ˇ to ne velja, postopek ponovimo. Pri zanki ni moˇzno dati preproali velja Cn ⊃ A1 . Ce stega recepta za izbiro prve trditve, od katere je bistveno odvisna uspeˇsnost postopka generiranja ostalih trditev v skladu z aksiomi. Prav zaradi potrebe po iznajdljivosti, ki je tu prisotna, je zaˇcetna trditev pri zanki nujni del programove dokumentacije in ponavadi bistveno pripomore k njegovi razumljivosti. Tej trditvi, ki je postavljena na zaˇcetek zanke in ki je veljavna ne glede na to, kolikokrat ponovimo izvajanje zankinega jedra, pravimo zanˇcna invarianta. Na podoben naˇcin, kot lahko v predikatnem raˇcunu zdruˇzujemo veˇc aksiomov v nove trditve s pravili sklepanja, lahko tudi pri preverjanju programov kombiniramo ˇskatle s trditvami v veˇcje sklope. Primeren zgled so prav zanke. Prikazali bomo dve pravili sklepanja, ki nam omogoˇcata, da pridobimo antecedensa in konsekvensa dveh vrst zank. Prvo se nanaˇsa na zanko vrste WHILE in pravi, da ko je izpolnjen pogoj na sliki 1.14a (torej, ko je podan S z antecedensom P ∧ B in konsekvensom P ), je 10 POGLAVJE 1. ALGORITMI x ≥ 0, y > 0 q := 0 r := x q = 0, r = x x = y · q + r, r≥0 − r≥y x = y · q + r, 0≤r<y + q := q + 1 r := r − y x = y · (q + 1) + r − y, r−y ≥0 Slika 1.12: Program za deljenje, opremljen s trditvami S1 A1 Sn C1 Cn Ai An Ci Cj Si Aj Sj Slika 1.13: Logiˇcne trditve v zanki moˇzno sestaviti zanko na sliki 1.14b z zapisanima antecedensom in konsekvensom. Znaˇcilno za zanko vrste REPEAT (slika 1.15) je, da zahteva dva predpogoja. Namreˇc zanko REPEAT na sliki 1.15c si lahko predstavljamo, kot da je sestavljena iz stavka S, ki mu sledi zanka WHILE (gl. sliko 1.15d). Prav zaradi dveh predpogojev, ki morata biti izpolnjena za pravilno uporabo zanke, je ta zanka teˇzja za uporabo in bolj podvrˇzena zlorabam kot zanka vrste WHILE. To trditev potrjujejo tudi empiriˇcne izkuˇsnje. 11 1.2. PREVERJANJE PRAVILNOSTI P − B P ∧B S P ∧B + P ∧ ¬B S P (a) Predpogoj (b) Zanka Slika 1.14: Zanka vrste WHILE Q ∧ ¬B P S S Q Q (a) Predpogoj 1 (b) Predpogoj 2 P P S Q S − B + ¬B + Q ∧ ¬B − Q∧B S Q∧B (c) Zanka REPEAT (d) Diagram poteka, ki je ekvivalenten diagramu na sliki (c) Slika 1.15: Zanka vrste REPEAT 12 POGLAVJE 1. ALGORITMI 1.2.4 Dokazovanje pravilnosti programov, ki so zapisani v programskem jeziku Tehniko iz prejˇsnjega odstavka je moˇzno brez teˇzav prirediti tudi za programe, ki so napisani v linearnem zapisu, npr. v nekem algolskem jeziku. V tem primeru sta antecedens in konsekvens zapisana pred in za vsakim stavkom. Najprimerneje je, da logiˇcne trditve zapisujemo kot programske komentarje. Pravila, po katerih se morajo ravnati antecedensi in konsekvensi posameznih stavkov, so prikazana v naslednji tabeli: PRAVILA PREVERJANJA ZA PROGRAME, KI SO ZAPISANI V PROGRAMSKEM JEZIKU 1. Prirejanje (∗ Pwv ∗)v := w(∗ P ∗) 2. Sestavljeni stavek Predpogoja: (∗ P ∗)S1 (∗ Q ∗) (∗ Q ∗)S2 (∗ R ∗) Posledica: (∗ P ∗)S1 ; S2 (∗ R ∗) 3. Pogojna stavka Predpogoja: (∗ P ∧ B ∗)S1 (∗ Q ∗) (∗ P ∧ ¬B ∗)S2 (∗ Q ∗) Posledica: (∗ P ∗)IF B THEN S1 ELSE S2 END(∗ Q ∗) Predpogoja: (∗ P ∧ B ∗)S(∗ Q ∗) (P ∧ ¬B) ⊃ Q Posledica: (∗ P ∗)IF B THEN S END(∗ Q ∗) 4. Zanke Predpogoj: (∗ P ∧ B ∗)S(∗ P ∗) Posledica: (∗ P ∗)WHILE B DO S END(∗ P ∧ ¬B ∗) 13 1.2. PREVERJANJE PRAVILNOSTI 1 2 3 4 5 6 7 8 9 10 11 12 13 14 PROCEDURE Multiply(x ,y:INTEGER):INTEGER; VAR u,z :INTEGER; BEGIN (∗ x > 0 ∗) z :=0; u:=x ; (∗ z =0; u=x ∗) (∗ z +u∗y=x ∗y, u>0 ∗) REPEAT z :=z +y; u:=u−1; UNTIL u=0; (∗ z =x ∗y, u=0 ∗) RETURN z END Multiply; Slika 1.16: Program za mnoˇzenje opremljen s trditvami Predpogoj: (∗ P ∗)S(∗ Q ∗) (∗ Q ∧ ¬B ∗)S(∗ Q ∗) Posledica: (∗ P ∗)REPEAT S UNTIL B(∗ Q ∧ B ∗) 5. Izbirni stavek Predpogoj: (∗ P ∧ (i = Lk ) ∗)Sk (∗ Q ∗) pri vseh k Posledica: (∗ P ∗) CASE i OF L1 : S1 | L2 : S2 | .. . Ln : Sn END (∗ Q ∗) Kot primer je na sliki 1.16 prikazan program s slike 1.4, ki je popolnoma opremljen z logiˇcnimi trditvami za preverjanje. Pri zanki vrste REPEAT sta v vlogi trditev P in Q iz tabele na zaˇcetku tega odstavka trditvi z + u ∗ y = x ∗ y, u > 0 ter z + u ∗ y = x ∗ y, u ≥ 0 14 POGLAVJE 1. ALGORITMI (po vrsti). Za praktiˇcne potrebe so mnoge trditve, ki opisujejo lastnosti nekega programa, odveˇcne, oziroma samoumevne. Najvaˇznejˇse trditve, brez katerih program pogosto postane nerazumljiv, pa so pogoji, ki jih morajo izpolnjevati vhodne koliˇcine, izhodna trditev, ki opisuje rezultat, ter zanˇcne invariante. Te trditve naj bi tudi predstavljale ˇ se zateminimalno dokumentacijo, ki jo vsak algoritem ali program potrebuje. Ce kamo k dokazovanju, pa veˇcinoma uporabljamo neformalno sklepanje. Kot primer dokazovanja pravilnosti navajamo dokazovanje pravilnosti algoritma dvojiˇskega iskanja na sliki 1.2. Vhodne spremenljivke in rezultat so opisani v uvodnem komentarju in se bomo na tem mestu predvsem ukvarjali z invarianto, ki pripada zanki v vrsticah 10–15. Preden zanˇcno invarianto (ki je postavljena pred vrstico 10) zapiˇsemo, ugotovimo, da za aritmetiˇcno srednjo vrednost mid velja l ≤ mid ≤ r. Zanˇcno invarianto razstavimo na 2 disjunktivna ˇclena, n = 0 in n > 0, od katerih slednji razpade na nadaljnjih 5 disjunktivnih ˇclenov: (n = 0) ∧ (l = 1) ∧ (r = 0)∨ (n > 0) ∧ [ (l = 1) ∧ (x < a[l]) ∧ (0 ≤ r ≤ n) ∨ (r = n) ∧ (a[r] ≤ x) ∧ (1 ≤ l ≤ n + 1) ∨ (a[l] ≤ x < a[r]) ∨ (0 < r < n) ∧ (a[r] ≤ x < a[r + 1]) ∨ (1 < l < n + 1) ∧ (a[l − 1] ≤ x < a[l]) ] (1.1) Pred izvajanjem zanke je gotovo resniˇcen eden izmed 6 pogojev v vrsticah 1–6 v (1.1)3 . Sedaj bomo loˇceno obravnavali vsakega izmed njih. 1. (n = 0) ∧ (l = 1) ∧ (r = 0). V tem primeru je izstopni pogoj iz zanke ¬l ≤ r takoj resniˇcen in se jedro zanke ne izvaja, do izhoda iz procedure pa pride v vrstici 17 z rezultatom −1, kar je pravilno. 2. (l = 1) ∧ (x < a[l]) ∧ (0 ≤ r ≤ n). V tem primeru je pogoj v 12. vrstici resniˇcen in se vrednost r zmanjˇsa. Glede na to, da do izvajanja jedra zanke pride le v primeru r ≥ l, l pa ima vrednost 1, je r vedno v mejah 0 ≤ r ≤ n. Pogoj (n > 0) ∧ (l = 1) ∧ (x < a[l]) ∧ (0 ≤ r ≤ n) ostane v veljavi in s tem invarianta. Zato tudi pri nadaljnjih izvajanjih jedra zanke ostane pogoj 2 resniˇcen in se r zmanjˇsuje dokler ne doseˇze vrednosti 0. Takrat pride do izhoda iz zanke. Ker je zato pogoj v vrstici 16 neresniˇcen, postane v vrstici 17 rezultat −1, kar je pravilno. 3. (r = n) ∧ (a[r] ≤ x) ∧ (1 ≤ l ≤ n + 1). V tem primeru je pogoj v 12. vrstici neresniˇcen in se vrednost l poveˇca. Ker do izvajanja jedra zanke pride le v primeru l ≤ n, ostane l v mejah med 1 in n + 1. Torej ostane pogoj, ki doloˇca ta primer, v veljavi in s tem tudi celotna invarianta. Do izhoda iz zanke pride takrat, ko postane l = n + 1. Vrednost rezultata postane n (v vrstici 16) v primeru a[n] = x, sicer pa −n − 1 (v vrstici 17), kar je v obeh primerih pravilno. 3 Pred prvim izvajanjem celo samo eden od pogojev 1–4. 1.3. PREVERJANJE USTAVLJANJA 15 4. (n > 0) ∧ (a[l] ≤ x < a[r]). Primer razpade na nekaj podprimerov: (a) pogoj iz 12. vrstice je resniˇcen. Tedaj se zmanjˇsa r in po tem velja bodisi i. x < a[r] in imamo zopet opravka s 4. primerom, kar pomeni, da invarianta ostane v veljavi ali ii. 0 < r < n ∧ a[r] ≤ x < a[r + 1], ko nastopi primer 5 (in tudi tedaj invarianta ostane v veljavi). Doloˇceno podrobnost v zvezi s tem primerom obravnava tudi naloga 4a; (b) pogoj iz vrstice 12 je neresniˇcen, tedaj se poveˇca l in velja bodisi i. a[l] ≤ x ter zopet nastopi primer 4 in s tem invarianta ostane v veljavi ali pa ii. 1 < l < n + 1 ∧ a[l − 1] ≤ x < a[l]. V slednjem primeru nastopi 6. primer in invarianta zopet ostane v veljavi (gl. tudi nalogo 4b). 5. 0 < r < n ∧ a[r] ≤ x < a[r + 1]. V tem primeru se vrednost l poveˇca in pogoj ostane v veljavi do trenutka, ko postane l = r + 1. Po izhodu iz zanke je rezultat r v primeru a[r] = x, sicer pa −r − 1, kar je pravilno. 6. 1 < l < n + 1 ∧ a[l − 1] ≤ x < a[l]. V tem primeru se r zmanjˇsa in imamo opravka z istim primerom do trenutka, ko postane r = l − 1. Po izhodu iz zanke je rezultat bodisi r ali −r − 1, podobno kot v predhodnem primeru. Osnovni namen podane obravnave primera s slike 1.2 je prikazati, kako je analiza pravilnosti tudi navidez zelo preprostega programa lahko razmeroma zapletena. Kljub temu jo je v osnovnih potezah koristno izpeljati, kar se kasneje obrestuje z manjˇsim ˇstevilom napak. 1.3 Preverjanje ustavljanja Prva in pomembna ugotovitev je, da sta pravilnost in ustavljivost programa dva loˇcena problema. Trditev o pravilnosti ima vedno obliko “ˇce program pride do izstopne toˇcke, velja trditev P ”. Torej se lahko zgodi, da pravilnost dokaˇzemo, vendar program nikdar ne pride do izstopne toˇcke. Preverjanje ustavljanja je potemtakem dokaz, da program po konˇcnem ˇstevilu korakov dejansko pride do izstopne toˇcke. Glede razlike med definicijo programa in algoritma na strani 1 naj poudarimo, da pri algoritmu ˇze privzemamo, da pri vseh vhodih obstaja dokaz o ustavljanju ustreznega programa. Za preverjanje ustavljanja nekega programa bomo opisali neko tehniko, ki naˇceloma deluje vedno, ko se program dejansko ustavi. Uporabljali bomo predstavitev v obliki diagrama poteka. V prvem koraku izberemo neko funkcijo f (. . .), ki je odvisna od spremenljivk programa in ki ima lastnost f (. . .) ≥ lim v vsaki toˇcki programa, pri ˇcemer je lim neka spodnja meja. Nato v drugem koraku razdelimo diagram na odseke, ki ne vsebujejo zank in imajo lastnosti, 1. da je vsaka pot skozi diagram sestavljena iz nekaterih izbranih odsekov (ki se na 16 POGLAVJE 1. ALGORITMI poti lahko tudi ponavljajo) in 2. na vsakem odseku, ki se ne konˇca pri izhodu programa, se vrednost f (. . .) strogo zmanjˇsa. V primeru, ko smo opravili oba koraka, sledi iz lastnosti f (. . .) ≥ lim in 2., da nobena pot po diagramu ne more vsebovati neomejenega ˇstevila odsekov (ki se ne konˇcajo pri izhodu programa) in se torej program ustavi. 1.1 Primer Kot prvi primer obravnavamo diagram poteka na sliki 1.11. Kot funkcijo f izberemo f (u) = u, za katero ugotovimo, da velja f (u) ≥ 0 v vseh toˇckah programa, v katerih je funkcija definirana (in definirana postane po izvajanju drugega stavka). Odseki, na katere razdelimo diagram, pa so 1. Od vhoda v program do vstopa v odloˇcitveno ˇskatlo; 2. od odloˇcitvene ˇskatle preko njenega negativnega izhoda nazaj do vstopa v odloˇcitveno ˇskatlo; 3. od odloˇcitvene ˇskatle preko njenega pozitivnega izhoda do izhoda programa Ni se teˇzko prepriˇcati, da funkcija f in razdelitev diagrama na odseke izpolnjujeta vse zahtevane pogoje in zato sklepamo, da se program ustavi. 1.2 Primer Drugi primer je bolj zapleten in se nanaˇsa na diagram poteka na sliki 1.17 (gl. tudi Manna [11, str. 187]). Program raˇcuna najveˇcjo skupno mero ˇstevil x1 in x2 , ki sta obe veˇcji od niˇc. V diagramu predstavlja simbol / celoˇstevilˇcno deljenje (div). Glede njegove pravilnosti naj si bralec ogleda nalogo 5, na tem mestu pa nas bo zanimalo le dokazovanje ustavljanja. Za funkcijo f izberemo f = y1 · y2 , lim pa je enako 1. Odseki, na katere razdelimo diagram, pa so: 1-2-4 4-2-4 5-7-5 1-3-4 4-3-4 5-6-7-5 1-5-7-5 4-5-7-5 5-6-8 1-5-6-7-5 4-5-6-7-5 1-5-6-8 4-5-6-8 Ni se teˇzko prepriˇcati, da so pri tako izbranih elementih izpolnjene vse zahteve, ki smo jih postavili (gl. nalogo 2). 1.4 Poraba ˇ casa in prostora Pri ocenah porabe omenjenih virov izhajamo iz doloˇcenih osnovnih lastnosti raˇcunalnika, ˇ sta S1 in S2 dve zaporedji stavkov v programu in na katerem se algoritem izvaja. Ce ˇce T (Si ) pri 1 ≤ i ≤ 2 oznaˇcuje ˇcas, ki ga program porabi na zaporedju Si , je osnovna lastnost, iz katere izhajamo T (S1 ; S2 ) = T (S1 ) + T (S2 ). (1.2) Pri tem se moramo zavedati, da je to s staliˇsˇca danaˇsnje tehnologije doloˇcena poenostavitev. V bistvu je to omejitev na raˇcunalniˇski model preprostega zaporednega ˇ 1.4. PORABA CASA IN PROSTORA 17 VHOD 1 y1 := x1 y2 := x2 y3 := 1 DA sodo(y1 ) NE 5 DA 2 y2 := y2 /2 y3 := 2y3 sodo(y2 ) DA NE NE sodo(y2 ) 6 3 DA y1 := y1 /2 z := y2 y3 4 8 y1 = y2 y2 := y2 /2 7 NE y1 := y2 y2 := |y1 − y2 | IZHOD Slika 1.17: Algoritem za najveˇcjo skupno mero. procesorja, ki ukaze svojega programa izvaja enega za drugim in vsak program izvaja do konca, preden se loti naslednjega. Za stroj RAM, za katerega smo se domenili, da predstavlja tisti model, na katerem se izvajajo naˇsi algoritmi, je ta omejitev veljavna. Danes pa seveda obstajajo raˇcunalniki, ki vsebujejo veˇc procesorjev, tako da lahko izvajajo istoˇcasno veˇc ukazov nekega programa. Za tak raˇcunalnik relacija (1.2) seveda ne velja. Prav tako obstajajo raˇcunalniki, ki delo na nekem programu pred koncem prekinejo in zaˇcnejo, oziroma nadaljujejo z delom na drugem programu, ki je do tedaj miroval. Pri takem raˇcunalniku prav tako ne moremo napovedati ˇcasa delovanja programa na podlagi (1.2). Vendar kljub temu, da lastnost (1.2) na danaˇsnjih raˇcunalnikih ni vedno prisotna, ˇse vedno predstavlja osnovo za vsako analizo porabe ˇcasa. Tudi pri analizi porabe pomnilnega prostora izhajamo iz doloˇcene poenostavitve. 18 POGLAVJE 1. ALGORITMI Pri porabi prostora se moramo najprej zavedati, da je pomnilni prostor razdeljen na veˇc podroˇcij, na primer: prostor za program, prostor za spremenljivke, prostor za ˇ se na tem mestu sklad ter prostor, ki ga program dinamiˇcno zasega in sproˇsˇca. Ce omejimo na prostor za spremenljivke in ˇce prostor za spremenljivko x oznaˇcujemo z M (x), je najenostavnejˇse izhodiˇsˇce, da je velikost prostora za spremenljivki x1 in x2 enaka M (x1 ∪ x2 ) = M (x1 ) + M (x2 ). (1.3) Tudi v tem primeru je na danaˇsnjih raˇcunalnikih to poenostavitev, kajti obstajajo raˇcunalniki in ustrezna programska oprema, ki omogoˇcata prirejanje istega prostora razliˇcnim spremenljivkam4 . Namreˇc, ˇce izraˇcun, ki ga program uresniˇcuje, ne potrebuje koliˇcin x1 in x2 istoˇcasno, je moˇzno za x1 in x2 uporabiti isti prostor. Vendar tudi v primeru prostora velja, da omenjena poenostavitev predstavlja osnovo za ocene porabe tega vira. 1.4.1 Zapis za asimptotiˇ cno rast funkcij cg(n) f (n) g(n) n0 n Preden predstavimo naˇcine ocenjevanja porabe ˇcasa in prostora, bomo opisali nek zapis za asimptotiˇcno rast funkcij. Pogosto ˇzelimo izraziti misel, da je funkcija f (n), ko je n celoˇstevilˇcni argument, po velikostnem redu asimptotiˇcno omejena navzgor z g(n). Moˇzno je tudi zamenjati besede “omejena navzgor z” z “omejena navzdol z” ali “enaka”. Natanˇcna oblika (prve) trditve je, da ∃c, n0 > 0 ∀n ≥ n0 [f (n) ≤ cg(n)] (1.4) Slika 1.18: Asimptotiˇcna zgornja (gl. sliko 1.18). Za ta pojem obstaja meja z natanˇcnostjo do multiplika- uveljavljen zapis. Mnoˇzico funkcij f (n), tivnega faktorja ki zadoˇsˇcajo pogoju (1.4), oznaˇcujemo z O(g(n)): O(g(n)) = {f (n) | ∃c, n0 > 0 ∀n ≥ n0 [f (n) ≤ cg(n)]} (1.5) Torej je (1.4) enakovredno trditvi f (n) ∈ O(g(n)). Vendar se je pri tem zapisu uveljavila doloˇcena nedoslednost in namesto f (n) ∈ O(g(n)) dejansko piˇsemo f (n) = O(g(n)). Na to je potrebno biti pozoren. Podobno, kot smo definirali asimptotiˇcno zgornjo mejo, lahko definiramo asimptotiˇcno spodnjo mejo: ∃c, n0 > 0 ∀n ≥ n0 [f (n) ≥ cg(n)] 4 Pravzaprav taki raˇ cunalniki danes v praktiˇ cni uporabi prevladujejo. (1.6) ˇ 1.4. PORABA CASA IN PROSTORA 19 Ustrezno mnoˇzico sedaj oznaˇcujemo z Ω(g(n)) = {f (n) | ∃c, n0 > 0∀n ≥ n0 [f (n) ≥ cg(n)]} (1.7) in podobno kot prej imamo nekoliko zavajajoˇc zapis f (n) = Ω(g(n)). V primeru, ko velja f (n) = O(g(n)) kakor tudi f (n) = Ω(g(n)), piˇsemo f (n) = Θ(g(n)) (torej Θ(g(n)) = O(g(n)) ∩ Ω(g(n))) in pravimo, da je f (n) po velikostnem redu enaka g(n). 1.4.2 ˇ Cas Kot smo nakazali z lastnostjo (1.2), je najpomembnejˇsa tehnika pri ocenjevanju ˇcasa, ki ga porabijo programi, seˇstevanje ˇcasov za posamezne dele programa. Osnovni deli programa so posamezni stavki. Za porabo ˇcasa posameznih stavkov ponavadi vpeljemo doloˇcene poenostavitve: tipiˇcni sta privzetek, da vsi stavki porabijo enak ˇcas, ali pa, da nekateri porabijo ˇcas 1, drugi pa 0, kar pomeni, da ˇstejemo le nekatere stavke. Na primer v algoritmu na sliki 1.2 bi lahko kot merilo za porabo ˇcasa vzeli ˇstevilo izvajanj pogojnega stavka v vrstici 12. Za ta stavek ugotovimo, da se izvaja, dokler ni izpolnjen izstopni pogoj iz zanke. Ker se koliˇcina r − l pri vsakem izvajanju ˇ jedra zanke pribliˇzno prepolovi, se bo ta stavek izvajal pribliˇzno log2 n krat. Ce oznaˇcujemo s T (n) ˇcas, ki ga porabi algoritem na sliki 1.2 pri vhodnem parametru n, vsekakor velja T (n) = Θ(log2 n) (gl. nalogo 8). Ker so zanke vrste FOR, FOR i = 1 TO n DO Si , in drugaˇcne zanke med najpomembnejˇsimi elementi programov, ima pri ocenjevanju ˇcasa pomembno vlogo izraˇcun vrednosti vrst: n X Ti (1.8) i=1 Za ocenjevanje vrednosti vrst obstaja obseˇzna literatura (gl. npr. Bronstein in dr. [3, poglavje 7]), vendar je nekaj vrst tako znaˇcilnih, da jih bomo na tem mestu omenili. Aritmetiˇ cna vrsta n X i = 1 + 2 + . . . + n. i=1 Za takˇsno vrsto ugotovimo, da velja n X i=1 i= 1 n(n + 1) = Θ(n2 ), 2 kar je moˇzno na primer dokazati z indukcijo po n. (1.9) 20 POGLAVJE 1. ALGORITMI Geometriˇ cna vrsta n X xi = 1 + x + x2 + . . . + xn . i=0 Vrednost te vrste dobimo kot n X ( i x = i=0 n + 1, pri x = 1 xn+1 −1 , x−1 sicer. (1.10) V primeru, ko je x < 0, prvi ˇclen ˇstevca gre proti 0 in dobimo pri velikih vrednostih n: n X 1 xi ≈ . 1 − x i=0 (1.10) lahko dokaˇzemo bodisi ponovno z indukcijo po n ali pa na podlagi neposrednega Pn razcepa polinoma xn+1 − 1 na i=0 xi in x − 1. Ocenjevanje vrst z doloˇ cnimi integrali. Od sploˇsnih orodij za ocenjevanje vrednosti (vsote) vrst bomo navedli le ocenjevanje z doloˇcnimi integrali. V primeru, ko ˇzelimo izraˇcunati n2 X f (i), (1.11) i=n1 in ko je f (x) nepadajoˇca funkcija v intervalu [n1 − 1, n2 + 1], R n2 +1 n1 f (x)dx ter R n2 +1 n1 Z f (x − 1)dx pa sta definirana, velja n2 X n2 +1 f (x − 1)dx ≤ n1 Z n2 +1 f (i) ≤ f (x)dx (1.12) n1 i=n1 (gl. sliko 1.19). V primeru nenaraˇsˇcajoˇce funkcije f (x) sta pa neenakosti v (1.12) obrnjeni. Pn ˇ Primer. Zelimo izraˇcunati i=2 1i ; iz (1.12) in pripombe sledi, Z 2 oziroma Z 1 n n+1 n X1 1 dx ≥ ≥ x−1 i i=2 n X1 1 dx = ln n ≥ ≥ x i i=2 Z 2 n+1 Z 2 n+1 1 dx, x 1 dx = ln(n + 1) − ln 2, x ˇ 1.4. PORABA CASA IN PROSTORA 21 f (x − 1) Pn2 Rekurenˇ cne relacije. Drugo pomembno orodje za n1 f (x) ocenjevanje ˇcasa algoritmov so rekurenˇcne relacije, ki se pojavljajo zlasti pri algoritmih, ki imajo rekurzivno f (x) definicijo. V tipiˇcnem primeru imamo problem, katerega vhodne podatke je moˇzno opisati s parametrom n, ki n n1 n2 oznaˇcuje velikost problema. Algoritem zanj pa deluje tako, da vhodne podatke najprej transformira v c manjˇsih naborov in pridobi c sorodnih podproblemov Slika 1.19: Ocenjevanje vrevelikosti nc . Nato reˇsuje a izmed teh podproblemov in dnosti vrst z integrali ˇ reˇsitev kombinira v reˇsitev prvotnega problema. Ce je za postopek razcepa prvotnih podatkov na podprobleme in kasneje za zdruˇzevanje delnih reˇsitev v reˇsitev prvotnega problema potreben ˇcas velikostnega reda nr , kjer je r neka konstanta, dobimo za ˇcas, ki ga porabi algoritem, relacijo b, n=1 T (n) = (1.13) aT ( nc ) + bnr , n > 1, pri ˇcemer je b veˇcji izmed ˇcasov, ki jih algoritem porabi za primer n = 1 ali za razcep problem velikosti n > 1 na podprobleme in za kasnejˇso zdruˇzitev delnih rezultatov v rezultat prvotnega problema. Oceno za T (n) podaja naslednji izrek: 1.3 Izrek Naj bodo a, b, c in r nenegativne konstante. Pri c > 0 in a ≤ c je reˇsitev rekurzivne enaˇcbe (1.13) podana s a < cr , Θ(nr ), Θ(nr log(n)), a = cr , T (n) = Θ(nlogc a ), a > cr . Dokaz. Natanˇcno izpeljavo bomo podali za primer n = ck . Z indukcijo po k lahko pokaˇzemo, da velja k X T (n) = bckr qi (1.14) i=0 pri q = a cr , nato pa trditev izreka sledi iz (1.10). 2 ˇ Primer. Kot primer vzemimo rekurzivno obliko dvojiˇskega iskanja na sliki 1.20. Ce zapiˇsemo n = r−l+2, lahko uporabimo relacijo (1.13). Potrebno je le izbrati primerne vrednosti a, c in r. V tem primeru je c = 2, ker podatke razbijemo na spodnji in 22 POGLAVJE 1. ALGORITMI 1 2 3 4 5 6 7 8 10 10 11 12 13 15 15 16 18 18 PROCEDURE BinSearch(VAR a:ARRAY OF LONGINT ; l ,r ,x :LONGINT ); (∗ Ko je a prazno, imata l in r vrednosti 1 in 0; a[0] je vedno nedefinirano; rezultat je k, ko velja a[k]=x in −k, ko velja a[l −1] < x < a[l ] oziroma l = 1 in x < a[l ] oziroma l = n+1 in a[n] < x pri n = LEN (a) − 1∗) VAR mid :LONGINT ; BEGIN IF l <=r THEN mid :=(l +r )DIV 2; IF a[mid ] > x THEN RETURN BinSearch(a,l ,mid −1,x ) ELSE RETURN BinSearch(a,l ,mid +1,r ,x ) END ELSIF (r > 0) & (a[r ]=x ) THEN RETURN r ELSE RETURN −l END END BinSearch; Slika 1.20: Rekurzivni algoritem dvojiˇskega iskanja zgornji del tabele (pod indeksom mid in nad njim)5 , a = 1, ker v vsakem primeru sproˇzimo le en nadaljnji rekurzivni klic in r = 0, kajti poraba ˇcasa do rekurzivnega klica (in po vrnitvi iz njega) je neodvisna od n. Na podlagi izreka 1.3 sledi, da je v tem primeru T (n) = Θ(nr log(n)) = Θ(log(n)), kar je v skladu z rezultatom, ki smo ga zapisali na zaˇcetku tega razdelka. 1.4.3 Prostor V tem delu se bomo z ocenami porabe prostora sreˇcali le nekajkrat in ne bomo navajali posebnih metod za take ocene. 1.5 Povzetek osnovnih pojmov 1. Osnovni pojmi. Razlika med algoritmom in programom 2. Pojem stroja RAM 3. Sled algoritma. Drevo sledi 4. Dvojiˇsko iskanje z dvema izidoma primerjave 5 Pravzaprav je c malenkostno manjˇ si, kar pomeni, da pri privzetku, da T (n) naraˇsˇ ca z n, dobimo nekoliko precenjeno vrednost T (n). 23 1.6. NALOGE 5. Osnovne metode za preverjanje pravilnosti algoritmov 6. Preverjanje pravilnosti s poskusi 7. Preverjanje pravilnosti z logiˇcno analizo 8. Diagrami poteka 9. Antecedens, konsekvens ter aksiomi preverjanja pravilnosti 10. Preverjanje pravilnosti zank. Zanˇcna invarianta 11. Programa za mnoˇzenje in deljenje dveh ˇstevil 12. Preverjanje pravilnosti programov, ki so zapisani v programskem jeziku 13. Preverjanje ustavljanja 14. Izhodiˇsˇci za ocenjevanje porabe ˇcasa in prostora 15. Zapis O, Ω, Θ 16. Osnovne vrste in njihovo ocenjevanje 17. Ocenjevanje porabe ˇcasa rekurzivnih programov. 1.6 Naloge 1. Na sliki 1.3b sta prikazani dve sledi algoritma s slike 1.2, kjer nas zanima le zaporedje doloˇcenih “kontrolnih toˇck” pri izvajanju algoritma. Koliko je moˇznih takih sledi algoritma pri vseh moˇznih vhodih, ko ima vhodna tabela a dolˇzino n (in nas, denimo, zanimajo iste kontrolne toˇcke)? 2. Naj ima prirejanje x := 0 konsekvens x = 0. Zapiˇsite njegov antecedens. 3. Zakaj je v programu na sliki 1.11 potreben pogoj x > 0? 4. (a) Natanˇcno analizirajte, zakaj v primeru 4(a)ii na strani 15 ne more veljati r = 0 ali r = n; (b) podobno v 4(b)ii, zakaj ne more veljati l = 1 ali l = n + 1. 5. Prepriˇcajte se o pravilnosti programa za najveˇcjo skupno mero na sliki 1.17. Program je izboljˇsava klasiˇcnega algoritma WHILE x1 # x2 DO IF x1 > x2 THEN x1 := x1 − x2 ELSE x2 := x2 − x1 END END ; RETURN x1 Prikazani algoritem vsebuje nekaj predikatov “x je sodo”, katerih vlogo je potrebno razumeti in dokazati, da program deluje pravilno. 6. Za primer 1.2 sestavite podroben dokaz, da sta izpolnjena pogoja 1. in 2. razdelka 1.3 za razdelitev diagrama na odseke ter, da velja f (. . .) ≥ 1. za pogoj 1. (vsako pot je moˇzno sestaviti iz izbranih odsekov) sestavite razˇclenitev na posamezne primere. Na primer, vse moˇzne primere lahko iz zaˇcetka Napotek: podrobno razdelimo 24 POGLAVJE 1. ALGORITMI na tiste, kjer je y1 sodo in druge, kjer je liho. Slednjo skupino delimo naprej na podlagi lihosti y2 itn. Pogoj f (. . .) ≥ 1 preverimo tako, da pokaˇzemo, da je argument vsakega celoˇstevilˇcnega razpolavljanja sod. Pogoj 2. dokaˇzemo tako, da preverimo, da vsak odsek, ki se ne konˇca pri izhodu programa, vsebuje vsaj eno celoˇstevilˇcno razpolavljanje. 7. Pri naslednjih parih funkcij h2n2 , n2 i h5n, n2 i h3n, 1i hn3 , n3 i velja x = y(z), kjer sta x in z levi in desni element para, y pa je O ali Ω ali Θ. Za vse primere ugotovite, kaj je najprimernejˇsa vrednost za y. ˇ primerjamo algoritem na sliki 1.2, ki uporablja primerjanje z dvema izidoma, z 8. Ce algoritmom dvojiˇskega iskanja, ki uporablja tri izide primerjanja (<, =, >), kaj je osnovna razlika med njima glede asimptotiˇcnega obnaˇsanja porabe ˇcasa? 9. Poiˇsˇcite reˇsitev rekurzivne enaˇcbe, T (n) = b, aT ( nc ) + bn log n, n=1 n > 1. (1.15) Napotek: izpeljite relacijo, podobno (1.14), ki smo jo uporabili za dokaz izreka 1.3. Poglavje 2 UREJANJE 2.1 Naloga urejanja in klasifikacija metod urejanja Urejanje podatkov se nanaˇsa na prestavljanje podatkov v zaporedju, a1 , a2 , . . . , an , (2.1) na podlagi neke relacije popolne urejenosti ≺, tako da na koncu dobimo, ai1 , ai2 , . . . , ain , in velja, ai1 ai2 . . . ain . (2.2) Potrebno je poudariti, da znak ≺ ne predstavlja nujno obiˇcajne urejenosti ˇstevil. Urejanje veˇcinoma uporabljamo zato, da podatke spravimo v nekakˇsen red, ki nam kasneje olajˇsuje njihovo iskanje. Spomnimo se pri igrah s kartami, kaj storimo takoj, ko karte dobimo v roke: uredimo jih po barvah in vrednosti, tako da lahko kasneje z enim samim pogledom ugotovimo, ali imamo karto doloˇcene vrste. Zaradi tega je urejanje tudi ena izmed najpogostejˇsih operacij, ki se izvajajo na raˇcunalnikih. Kot t´ ako jo mora raˇcunalniˇski strokovnjak obvladati in biti dobro seznanjem z razliˇcnimi naˇcini in pogoji za njeno izvajanje. Poleg tega je moˇzno na primeru urejanja prikazati vrsto idej in osnovnih tehnik programiranja in naˇcrtovanja algoritmov. Osnovni pogoj za to, da je naloga urejanja smiselna, je popolna urejenost mnoˇzice elementov zaporedja (2.1), kar pomeni, da za razliˇcna elementa x in y velja bodisi x ≺ y ali y ≺ x. S to pripombo zaˇcnemo kratek miselni pregled razliˇcnih nalog urejanja in za vsako nalogo razliˇcnih algoritmov zanjo. V tem poglavju bomo algoritme predstavljali z razmeroma podrobnimi programi v jeziku Oberon-2 (gl. npr. Reiser in Wirth [14] ali Mahniˇc [4]), zgradba programov 25 26 POGLAVJE 2. UREJANJE MODULE Sort; CONST eq∗=0; less∗=1; grt∗=2; TYPE PItem∗=POINTER TO Item; Item∗=RECORD (∗ podatkovni elementi ∗) END; (∗ tip procedure za primerjanje podatkovnih elementov ∗) FCmpType∗=PROCEDURE(p,q:PItem;r :INTEGER):BOOLEAN ; END Sort. Slika 2.1: Osnovne deklaracije pri nalogi urejanja Urejanje nizov, ki so sestavljeni iz posameznih crk in so razlicno dolgi Slika 2.2: Podatki razliˇcne velikosti pa bo tudi ustrezala razˇclembi sploˇsnega problema urejanja, kot bo opisana v nadaljevanju. Najsploˇsnejˇsi nalogi urejanja priredimo nek izhodiˇsˇcni programski modul, ki vsebuje osnovne elemente, ki so skupni vsem razliˇcicam problema urejanja, nato pa s specializacijo in dedovanjem pridemo do posameznih konkretnih primerov. Neko moˇzno obliko takega izhodiˇsˇcnega modula prikazuje slika 2.1: podan je osnovni tip zaporedja ter kazalec nanj, tip procedure za primerjanje, kakor tudi tri konstante, ki doloˇcajo za kakˇsno primerjanje gre. Naj opozorimo na to, da modul Sort ne vsebuje nobenih operacij temveˇc le deklaracije. Na tem mestu je primerno opozoriti ˇse na neko posebnost zapisa, ki smo ga uporabili na sliki 2.1: gre za dogovor, ki vnaˇsa doloˇceno pravilnost v poimenovanje razliˇcnih ˇ imamo tip, ki nosi ime Nm, bomo objektov in s tem olajˇsuje njihovo pomnenje. Ce kazalec nanj poimenovali s PNm (“POINTER TO”), tabelo z osnovnim tipom Nm pa z ANm (“ARRAY OF”). Na primer na sliki 2.1 pomeni ime PItem, “POINTER TO Item”. Prav tako bomo vse tipe, ki opisujejo procedure, oziroma funkcije, zaˇceli s ˇcrko F (npr. “FCmpType”). Naloge urejanja se predvsem razlikujejo po vrsti podatkov, definiciji relacije ure- 2.1. NALOGA UREJANJA IN KLASIFIKACIJA METOD UREJANJA 27 MODULE IntSort; IMPORT S :=Sort; TYPE PItem∗= POINTER TO Item; Item∗ = RECORD(S .Item) k ∗:LONGINT END; END IntSort. Slika 2.3: Dodatne deklaracije za primer celoˇstevilˇcnih kljuˇcev jenosti ter po velikosti zbirke podatkov, posamezni algoritmi pa ˇse po pristopu, ki ga uporabljajo in po svoji zapletenosti. Razlikovali bomo dve osnovni vrsti podatkov, in sicer podatke iste dolˇzine ter razliˇcnih dolˇzin. Vendar to ni posebno pomembna znaˇcilnost, kajti urejanje podatkov razliˇcnih dolˇzin je moˇzno prevesti na urejanje podatkov iste dolˇzine z uporabo kazalcev in dinamiˇcnega dodeljevanja pomnilnika (glej sliko 2.2). Na ta naˇcin je osnovna predstavitev vseh podatkov (kazalec) enako dolga in se razliˇcne dolˇzine dejanskih podatkov kaˇzejo edino v neenakem trajanju operacije primerjanja podatkov. Dejansko ima dostop do podatkov s kazalci doloˇceno prednost pred neposrednim dostopom, zaradi ˇcesar ga bomo v tem poglavju vseskozi uporabljali in se pravzaprav z urejanjem podatkov razliˇcnih dolˇzin ne bomo ukvarjali. Seveda pa ima uporaba kazalcev tudi nekatere pomanjkljivosti. Na kratko so prednosti in pomanjkljivosti uporabe kazalcev naslednje: Dobre strani 1. Neodvisnost veˇcine programa od oblike podatkov ter s tem povezana moˇznost uporabe programskih sestavin v razliˇcnih aplikacijah. 2. Enostavnejˇsa oblika programa. Slabe strani 1. Veˇcja poraba pomnilnega prostora (dodaten prostor za kazalce). 2. Nekoliko manjˇsa hitrost. Relacijo urejenosti lahko definiramo na razliˇcne naˇcine in zato smo v osnovnem modulu Sort deklarirali le tip procedure FCmpType, ki mu pripadajo procedure, ki preverjajo relacijo ≺. Pravzaprav pa procedur omenjenega tipa niti ne bomo uporabljali, ker za naˇse potrebe zadostujejo obiˇcajne relacije, <,= in >, ki so (v jeziku Oberon-2) definirane tako za ˇstevilske tipe kot za znakovna zaporedja. To pa sta tudi najpogostejˇsa praktiˇcna primera pri urejanju. Torej v prvem primeru imamo primerjanje na podlagi neke ˇstevilˇcne komponente podatka, v drugem pa na podlagi nekega 28 POGLAVJE 2. UREJANJE PROCEDURE CompareStr (VAR a,b: ARRAY OF CHAR):INTEGER; (∗ Znakovni zaporedji a in b sta zakljuˇceni z niˇcelnim znakom ∗) VAR i:INTEGER; BEGIN i:=0; WHILE (a[i]=b[i])&(a[i]#0X)DO INC (i) END; RETURN ORD(a[i])−ORD(b[i]) END CompareStr ; Slika 2.4: Leksikografsko urejanje znakovnega zaporedja. Delu podatka, na podlagi katerega izvajamo primerjanje, ponavadi pravimo kljuˇc in v prvem primeru se deklaracija tipa Item razˇsiri tako, kot je prikazano na sliki 2.3, v drugem primeru pa takole: RECORD(Sort.Item) k :ARRAY OF CHAR END. (2.3) Mimogrede, relacija ≺, =, ali na znakovnih zaporedjih se raˇcuna po algoritmu, ki je ˇ prikazan na sliki 2.4. (Ceprav smo ugotovili, da so te relacije pri Oberonu-2 vgrajene v sam jezik, program navajamo zaradi dodatne razjasnitve pojma.) Tako definirani relaciji urejenosti pravimo leksikografska urejenost. Pomembna razlika med urejanjem na podlagi celoˇstevilˇcnega kljuˇca in leksikografskim urejanjem je, da sama operacija primerjanja v prvem primeru porabi konstanten ˇcas, v drugem pa spremenljiv ˇcas (tudi ˇce imamo opravka z znakovnimi zaporedji iste dolˇzine). Znaˇcilnost problema urejanja, ki nas bo v tem poglavju najbolj zanimala in doloˇcala naˇcin reˇsevanja problema, pa je ˇstevilo podatkov. Razlikovali bomo primer, ko je moˇzno hraniti vse podatke v notranjem pomnilniku raˇcunalnika, od primera, ko moramo uporabljati zunanje pomnilnike. Zaradi tega prvemu postopku pravimo notranje urejanje, drugemu pa zunanje urejanje. V prvem primeru za shranjevanje podatkov uporabljamo tabelariˇcno podatkovno strukturo (angl. array), v drugem pa uporabljamo datoteke (angl. file). Ko uporabljamo datoteke, ponavadi privzamemo, da so to zaporedne datoteke, katerih osnovna znaˇcilnost je, da elemente beremo enega za drugim od zaˇcetka proti koncu. V tem primeru velja, da imamo med branjem datoteke v vsakem trenutku dostop samo do omejenega ˇstevila sosednih elementov, ki so “vidni” skozi nekakˇsno okno na datoteko. Podatki iz okna so shranjeni v notranjem pomnilniku raˇcunalnika. Zaporednim datotekam bomo vˇcasih rekli kar trak , ker se dejansko obnaˇsajo kot klasiˇcni magnetni trakovi, kjer je tudi dejansko dosegljiv le majhen del podatkov razmeˇsˇcen blizu bralne glave traˇcnega mehanizma. Ponavadi algoritme za posamezne naloge urejanja lahko razvrstimo v dve skupini. Eni pripadajo algoritmi, ki so po svoji zasnovi preprosti, porabijo pa razmeroma veliko ˇcasa, drugi skupini pa pripadajo algoritmi, ki so po svoji zgradbi bolj zapleteni, vendar hitrejˇsi. Algoritmom prve vrste pravimo navadni algoritmi (za urejanje), algoritmom druge vrste pa izboljˇsani algoritmi . Vsi opisani kriteriji za razvrˇsˇcanje metod za 2.2. NOTRANJE UREJANJE 29 ˇ PODATKI STALNE DOLZINE ˇ PODATKI SPREMENLJIVE DOLZINE ˇ PRIMERJANJE PORABI KONSTANTEN CAS ˇ PRIMERJANJE PORABI SPREMENLJIV CAS KRATKA ZAPOREDJA = UREJANJE TABEL DOLGA ZAPOREDJA = UREJANJE DATOTEK ˇ ENOSTAVNI, A CASOVNO POTRATNI ALGORITMI ˇ ˇ ZAPLETENI, A CASOVNO UCINKOVITI ALGORITMI Slika 2.5: Kriteriji za razvrˇsˇcanje metod za urejanje urejanje so povzeti na sliki 2.5. Na koncu tega kratkega uvoda v urejanje naj na sliki 2.6 predstavimo shemo dedovanja med programskimi moduli, ki uresniˇcujejo metode, o katerih bomo razpravljali. Programska modula Sort in IntSort pravzaprav pri realizaciji urejanja niti ne bi bila potrebna, saj bi njuno vsebino zlahka vkljuˇcili v druge module. Izpostavili pa smo ju kot loˇcena modula prav zato, da nakaˇzemo miselno razˇclenitev problemov urejanja. 2.2 Notranje urejanje V tem razdelku si bomo najprej ogledali razliˇcne navadne algoritme za urejanje tabel in nato njihove izboljˇsave. Navadni algoritmi za notranje urejanje imajo znaˇcilnost, da porabijo O(n2 ) operacij za urejanje tabele velikosti n, izboljˇsani algoritmi pa za isto nalogo v veˇcini primerov O(n log n) operacij. Omejili se bomo na algoritme, ki izpolnjujejo tudi nek dodaten pogoj, in sicer, da ne zahtevajo bistveno veˇc prostora za podatke, kot jih zaseda vhodna tabela dolˇzine n. Algoritme za notranje urejanje nato delimo v tri skupine, na podlagi osnovnega pristopa, ki ga uporabljajo. Vse procedure za urejanje tabel bomo deklarirali znotraj modula ArrSort z zaˇcetnima dvema vrsticama: MODULE ArrSort; IMPORT I :=IntSort,S :=Sort; vsaka od procedur za urejanje, ki jih bomo predstavili, pa uporablja parameter a tabelariˇcnega tipa: APItem = ARRAY OF I .PItem. (2.4) 30 POGLAVJE 2. UREJANJE Osnovni podatek ˇstevilskim kljuˇ cem Ostale metode urejanja Sort s Metode urejanja tabel IntSort ArrSort FileSort Definicija osnovnega podatka. Tip procedure za primerjanje podatkov Metode urejanja datotek Slika 2.6: Shema dedovanja med programskimi moduli za urejanje Kasneje bomo potrebovali tudi kazalec na ta tip: PAPItem = POINTER TO APItem. (2.5) Preden predstavimo tri navadne metode urejanja tabel, naj omenimo neko skupno lastnost, ki nam olajˇsuje njihovo razumevanje: v vsakem trenutku delovanja procedure je tabela a razdeljena na dva dela, levega, ki ima dolˇzino i in je ˇze urejen, ter desnega, ki ima dolˇzino LEN (a) − i in je ˇse neurejen. (2.6) Skupno vsem navadnim algoritmom notranjega urejanja je, da imajo zgradbo preproste zanke z invarianto, ki jo predstavlja trditev (2.6) in jedrom, ki ima pri vsakem izvajanju uˇcinek, da poveˇca dolˇzino ˇze urejenega dela za 1. 2.2.1 Navadno vstavljanje Algoritem ima naslednjo zgradbo: FOR i:=1 TO n−1 DO BEGIN (∗ a[i] vstavimo na pravilno mesto v urejeni del tabele, a0 , . . . , ai−1 ∗) END Podrobno je algoritem prikazan na sliki 2.7. Na sliki je tudi nakazano mesto, kjer velja trditev (2.6) o delitvi tabele na zaˇcetni, urejeni del in neurejeni del. Na tem mestu lahko prikaˇzemo neko podrobnost, ki spada med drobne zvijaˇce programiranja, je pa koristna v vseh primerih, ko moramo neko tabelo preiskati od zaˇcetka do konca, tako kot na sliki 2.7 tabelo a[0], . . . , a[i − 1] (vrstice 8 do 10). 2.2. NOTRANJE UREJANJE 1 2 3 4 5 6 7 8 9 10 11 13 13 PROCEDURE InsertionSort∗(VAR a:APItem); VAR i,j ,n:LONGINT ;x :I .PItem; BEGIN n:=LEN (a); FOR i:=1 TO n−1 DO (∗ zaporedje, a[0], a[1], ..., a[i − 1], je urejeno; zaporedje, a[i], a[i+1], ... , a[n−1], neurejeno ∗) j :=i; x :=a[j ]; WHILE (j >=1) & (x .k <a[j −1].k ) DO a[j ]:=a[j −1]; DEC (j ) END; a[j ]:=x END END InsertionSort; Slika 2.7: Navadno vstavljanje 1 2 3 4 5 6 7 8 9 10 11 12 13 15 15 PROCEDURE SentinelSort∗(VAR a:APItem); (∗ Urejanje s ˇcuvajem; tabela a ima dodaten element na svojem zaˇcetku ∗) VAR i,j ,n:LONGINT ; BEGIN n:=LEN (a)−1; (∗ ˇstevilo elementov v tabeli ∗) FOR i:=2 TO n DO (∗ zaporedje, a[1], a[2], ..., a[i − 1], je urejeno; zaporedje, a[i], a[i + 1], ..., a[n], neurejeno ∗) j :=i; a[0]:=a[i]; WHILE a[0].k <a[j −1].k DO a[j ]:=a[j −1]; DEC (j ) END; a[j ]:=a[0] END END SentinelSort; Slika 2.8: Vstavljanje s ˇcuvajem 31 32 POGLAVJE 2. UREJANJE V spremenjenem algoritmu navadnega vstavljanja na sliki 2.8 poveˇcamo tabelo, za en element, tako da komponente a[1], . . . , a[n] vsebujejo zaporedje, ki ga urejamo, a[0] pa je dodaten element, ki mu v vrstici 9 priredimo vrednost a[i]. Na ta naˇcin smo odpravili potrebo po preverjanju pogoja za izstop iz notranje zanke, j ≥ 1, v algoritmu na sliki 2.7 in priˇsli do oblike, ki je prikazana na sliki 2.8. Seveda pa krajˇsi zapis izstopnega pogoja veˇcinoma pomeni hitrejˇse izvajanje. Takemu dodatnemu elementu na zaˇcetku ali koncu tabele, ki ima nalogo, da prepreˇci nekemu indeksu (v naˇsem primeru j), da prekoraˇci mejo tabele, pravimo ˇcuvaj. Analiza navadnega vstavljanja. Pri algoritmih nas zanima poraba razliˇcnih raˇcunskih virov, predvsem pomnilnega prostora in ˇcasa. Glede pomnilnega prostora smo se pri urejanju tabel ˇze omejili na algoritme, ki ne zahtevajo dodatnega prostora (poleg tistega, ki ga zasedajo podatki), tako da je analiza porabe prostora brezpredmetna. Pri porabi ˇcasa pa postopamo tako, da ˇcas ocenjujemo s ˇstevilom doloˇcenih znaˇcilnih operacij. Najznaˇcilnejˇse v algoritmu na sliki 2.8 je primerjanje v izstopnem pogoju notranje zanke. Poleg tega nas ponavadi zanima veˇc vrst ocen, na primer: najmanjˇsa poraba na vseh moˇznih zaporedjih dolˇzine n, najveˇcja poraba ter povpreˇcna poraba. Na podlagi zgradbe algoritma na sliki 2.8 lahko za vse vrste porabe zapiˇsemo relacijo, C= n X Ci , (2.7) i=2 kjer je C ˇstevilo primerjanj, in to bodisi najmanjˇse, najveˇcje ali povpreˇcno, Ci pa je isto ˇstevilo med izvajanjem jedra zanke pri vrednosti i. Na primer, do najmanjˇsega ˇstevila primerjanj v algoritmu na sliki 2.8 pride, ko je vhodno zaporedje ˇze urejeno in se jedro notranje zanke nikoli ne izvaja. V takem primeru je Cmin,i = 1 pri vseh vrednostih i in je, n n X X Cmin = Cmin,i = 1 = n − 1. i=2 i=2 Do najveˇcjega ˇstevila primerjanj pa pride, ko je vhodno zaporedje nasprotno urejeno in moramo a[i] vedno postaviti na prvo mesto. V tem primeru je Cmax,i = i − 1 in dobimo, n n X X 1 Cmax = Cmax,i = (i − 1) = (n − 1)n. 2 i=2 i=2 Za izraˇcun povpreˇcne vrednosti, Cave , bi bilo potrebno poznati porazdelitev verjetnosti posameznih vhodnih zaporedij (in na podlagi tega izraˇcunati porazdelitev verjetnosti Cave,i ), ˇce pa privzamemo, da so vse vrednosti Cave,i v intervalu [Cmin , Cmax ] enakoverjetne, dobimo oceno, Cave = 1 1 1 1 (Cmin + Cmax ) = (n − 1 + (n2 − n)) = (n − 1)(n + 2). 2 2 2 4 33 2.2. NOTRANJE UREJANJE 1 2 3 4 5 6 7 8 9 10 12 12 13 14 15 16 17 19 19 PROCEDURE BinaryIns∗(VAR a:APItem); VAR i,j ,l ,r ,m,n:LONGINT ;x :I .PItem; BEGIN n:=LEN (a); FOR i:=1 TO n−1 DO x :=a[i]; l :=1; r :=i; (∗ l − 1 in r − 1 predstavljata mejne indekse urejenega dela ∗) WHILE l <= r DO m:=(l +r )DIV 2; IF a[m−1].k > x .k THEN r :=m−1 ELSE l :=m+1 END END ; IF (r > 0) & (x .k =a[r −1].k ) THEN l :=r END; FOR j :=i TO l BY −1 DO a[j ]:=a[j −1] END; a[l −1]:=x END END BinaryIns; Slika 2.9: Navadno vstavljanje z dvojiˇskim iskanjem Poskus izboljˇ save navadnega vstavljanja. Ko razmiˇsljamo o moˇznih izboljˇsavah metode navadnega vstavljanja, ugotovimo, da bi to bilo moˇzno, ˇce bi osnovne operacije realizirali uˇcinkoviteje. Ena izmed operacij, ki se pojavlja v tem algoritmu, je iskanje pravega mesta v zaporedju a[1], . . . , a[i − 1] za element a[i]. Glede na to, da je slednje zaporedje ˇze urejeno, se nam utrne misel, uporabiti za omenjeno operacijo dvojiˇsko iskanje. Tako spremenjen algoritem navadnega vstavljanja je prikazan na sliki 2.9. Ker dvojiˇsko iskanje v tabeli dolˇzine n zahteva O(dlog2 ne) operacij, zahteva iskanje pravega mesta za a[i] pri vseh i, 2 ≤ i ≤ n, ˇstevilo operacij C= n−1 X dlog2 ie. i=1 To ˇstevilo pa je moˇzno oceniti z integralom, Z n 1 1 log2 x dx = x(ln x − 1)|n1 = n(ln n − 1), c c 1 ˇ kjer je c = 1/ ln 2 [gl. 1.12]. Zal, ko nekoliko premislimo, ugotovimo, da s takˇsno izboljˇsavo nismo veliko dosegli, kajti zanka v vrsticah od 14 do 16 pri vseh vrednostih i ˇse vedno zahteva O(n2 ) operacij (gl. nalogo 1). V tem primeru bi lahko rekli, da nas je zanaˇsanje na oceno ˇcasa z ˇstetjem primerjanj zavedlo in pripeljalo do napaˇcnih sklepov, kajti zanemarili smo ˇcas, ki se porabi za prestavljanje elementov zaporedja. Nauk je, da se moramo pri podobnih ocenah prepriˇcati, da smo resniˇcno upoˇstevali vse pomembne operacije algoritma, ki ga analiziramo. 34 POGLAVJE 2. UREJANJE 1 2 3 4 5 6 8 9 9 PROCEDURE SelectionSort∗(VAR a:APItem); VAR i,j ,n:LONGINT ; BEGIN n:=LEN (a); FOR i:=0 TO n−2 DO FOR j :=i+1 TO n−1 DO IF a[i].k > a[j ].k THEN Exch(a[i],a[j ]) END END END END SelectionSort; Slika 2.10: Navadno izbiranje 2.2.2 Navadno izbiranje Navadno izbiranje ima naslednjo zgradbo: FOR i :=0 TO n−2 DO (∗ Najmanjˇsi element med a[i] . . . a[n − 1] priredimo spremenljivki a[i] ∗) END ; popoln algoritem pa je prikazan na sliki 2.10. Pri vsakem izvajanju jedra zunanje zanke se notranja zanka sprehodi po vseh indeksih, ki so veˇcji od i in vsakiˇc, ko naleti na element z manjˇsim kljuˇcem kot a[i].k, zamenja a[i] in tekoˇci element a[j]. Pri analizi algoritma uporabljamo zopet relacijo (2.7) z nekoliko spremenjenima mejama seˇstevanja in dobimo 1 C = (n2 − n), 2 pri ˇcemer ugotovimo, da je v tem primeru koliˇcina C neodvisna od vhodnega zaporedja. V tem primeru uporabimo nauk iz razdelka 2.2.1 in se lotimo ˇse ocene ˇstevila zamenjav oziroma premikov, M . Izhajamo iz podobne relacije, kot je (2.7): M= n−2 X Mi . (2.8) i=0 Na podlagi podobnih premislekov kot prej, pridemo do rezultata, da je Mmin = 0 (v primeru ˇze urejene tabele) in Mmax = 12 n(n − 1) (v primeru nasprotno urejene ˇ tabele). Ceprav bi ˇcas lahko nekoliko izboljˇsali, ˇce bi uporabili operacije z indeksi (vaja!), pa to ne bi vplivalo na velikostni red ˇcasa. 2.2.3 Navadne zamenjave ˇ Ceprav smo ˇze pri algoritmu navadnega vstavljanja uporabljali operacijo zamenjave dveh elementov kot bistveno sestavino algoritma, pa pri algoritmu na sliki 2.11 igra 35 2.2. NOTRANJE UREJANJE 1 2 3 4 5 6 7 9 10 11 11 PROCEDURE BubbleSort∗(VAR a:APItem); VAR i,j ,n:LONGINT ; BEGIN n:=LEN (a); FOR i:=1 TO n−1 DO FOR j :=n−1 TO i BY −1 DO IF a[j −1].k > a[j ].k THEN Exch(a[j −1],a[j ]) END END END END BubbleSort; Slika 2.11: Navadne zamenjave ˇse pomembnejˇso vlogo, zaradi ˇcesar algoritmu pravimo urejanje z navadnimi zamenjavami. Pri vsakem izvajanju notranje zanke zaˇcnemo pri koncu tabele in potujemo proti zaˇcetku ter primerjamo sosednje elemente. V primeru napaˇcne urejenosti, opravimo zamenjavo. Glede ˇcasovne zahtevnosti ugotovimo, podobno kot v primeru navadnega izbiranja, da ˇstevilo primerjanj ni odvisno od vhodnih podatkov in da velja C= n(n − 1) . 2 Pri navadnih zamenjavah so seveda bistvena sestavina porabe ˇcasa operacije prirejanja elementov tipa PItem (gl. sliko 2.3). Izkaˇze se, da je tudi to ˇstevilo O(n2 ) (gl. nalogo 2). Algoritem navadnih zamenjav vsebuje neko zanimivost: medtem ko se najmanjˇsi element v neurejenem delu tabele spusti na svoje pravo mesto med enim samim izvajanjem notranje zanke1 , porabi najveˇcji element za to, da se dvigne do svojega pravega mesta, veˇckratno izvajanje notranje zanke. Na podlagi te ugotovitve pridemo do razliˇcice navadnih zamenjav, kjer smer gibanja “mehurˇckov” po vsakem izvajanju notranje zanke zamenjamo. Taki razliˇcici pravimo “izmeniˇcne zamenjave”. Izmeniˇcne zamenjave vsebujejo ˇse neko izboljˇsavo povpreˇcnega ˇcasa algoritma: ˇce se je med izvajanjem notranje zanke algoritma na sliki 2.11 zadnja zamenjava opravila pri indeksu k, je oˇcitno, da je podtabela z indeksi od 1 do k ˇze urejena in lahko spodnjo mejo notranje zanke, i, takoj poveˇcamo do k. Algoritem s temi izboljˇsavami je prikazan na sliki 2.12 2.2.4 Spodnja meja za ˇ cas urejanja tabel Pri navadnih metodah urejanja (navadno vstavljanje, izbiranje in zamenjave) smo ugotovili, da zahtevajo ˇstevilo operacij, ki je O(n2 ), ˇce je velikost tabele n. Do te 1 Elementi se spuˇ sˇ cajo na svoja prava mesta podobno, kot se dvigajo zraˇ cni mehurˇ cki v vodi. Od tod angleˇsko ime bubblesort. 36 POGLAVJE 2. UREJANJE 1 2 3 4 5 6 7 8 9 11 11 12 13 14 15 16 18 18 19 20 21 PROCEDURE ShakerSort∗(VAR a:APItem); VAR i,j ,l ,r ,n:LONGINT ; BEGIN n:=LEN (a); l :=1;r :=n−1;j :=n−1; REPEAT FOR i:=r TO l BY −1 DO IF a[i−1].k > a[i].k THEN Exch(a[i−1],a[i]); j :=i END END; l :=j +1; FOR i:=l TO r DO IF a[i−1].k > a[i].k THEN Exch(a[i−1],a[i]); j :=i END END; r :=j −1 UNTIL l >r END ShakerSort; Slika 2.12: Izmeniˇcne zamenjave vrednosti smo priˇsli tako, da smo ˇsteli operacije primerjanja dveh elementov vhodne ˇ ˇzelimo navadne metode tabele ter prirejanja elementov podatkovnega tipa PItem. Ce izboljˇsati s staliˇsˇca porabe ˇcasa, je dobro najprej oceniti, do katere meje lahko porabo ˇcasa izboljˇsujemo. Z drugimi besedami, ˇzelimo priti do neke spodnje meje za porabo ˇcasa za urejanje tabel dolˇzine n. Kot merilo za porabo ˇcasa ponavadi jemljemo ˇstevilo operacij doloˇcene vrste (prej smo omenili operaciji primerjanja in prirejanja). Pri doloˇcanju spodnje meje postopamo tako, da pri vnaprej doloˇcenem n obravnavamo “vse” algoritme urejanja tabel dolˇzine n in vsakemu algoritmu P pripiˇsemo nek ˇcas urejanja T (P, n). Seveda je pojem “vseh algoritmov” nekoliko nenatanˇcen in zato pravzaprav vzamemo neko natanˇcno doloˇceno mnoˇzico algoritmov P. Torej je spodnja meja za ˇcas urejanja tabele dolˇzine n, T (n) = MINP ∈P T (P, n). (2.9) Seveda pa vsak algoritem urejanja deluje na celi mnoˇzici vhodnih tabel dolˇzine n, ki se med seboj razlikujejo po urejenosti in vrednosti elementov. In zato se moramo odloˇciti o tem, katero vhodno tabelo naj upoˇstevamo pri raˇcunanju vrednosti T (P, n). Razumno je izbrati vhodno tabelo, ki pri danem algoritmu daje najslabˇsi (najveˇcji) ˇcas tako, da na koncu dobimo ˇcasovno vrednost, ki dejansko zagotavlja urejanje poljubne tabele dolˇzine n. Po tem premisleku dobimo, T (n) = MINP ∈P MAXIn ∈In T (P, In ), (2.10) 37 2.2. NOTRANJE UREJANJE if a[i]Ra[j] then S Z(i, j) goto l halt R predstavlja eno od relacij ≤, ≥, < ali >, S pa je eden od preostalih treh ukazov. Zamenjava a[i] in a[j]. l je ˇstevilka nekega stavka pri pogoju, da velja l > k, kjer je k ˇstevilka samega stavka goto. Ustavljanje. Slika 2.13: Dovoljene operacije pri ocenjevanju spodnje meje za ˇcas urejanja pri ˇcemer je In mnoˇzica vseh moˇznih tabel dolˇzine n, In je poljuben element slednje mnoˇzice in T (P, In ) je ˇcas, ki ga porabi algoritem P na vhodnih podatkih In . Bodimo pozorni na to, da uporabljamo isti funkcijski simbol T v razliˇcnih pomenih, odvisno od parametrov funkcije, ki jo simbol predstavlja: T (n) predstavlja spodnjo mejo za urejanje zaporedij dolˇzine n, ne glede na algoritem, ki ga uporabljamo; T (P, n) predstavlja najveˇcji ˇcas, ki ga algoritem P porabi na nekem vhodnem zaporedju dolˇzine n, T (P, In ) pa predstavlja ˇcas, ki ga porabi algoritem P na konkretnem zaporedju In . Sedaj moramo natanˇcneje doloˇciti mnoˇzici In ter P, kakor tudi koliˇcino T (P, In ) oziroma T (P, n). Od mnoˇzice algoritmov, ki jo izberemo, je odvisno, ali je reˇsitev, ki jo dobimo, ˇ algoritem uporablja neko zelo zapleteno in uˇcinkovito zanimiva, oziroma smiselna. Ce osnovno operacijo (“strojni ukaz”), potem je urejanje moˇzno opraviti zelo hitro. V skrajnem primeru je lahko ˇze sama operacija urejanja tabele dolˇzine n osnovni ukaz. Zato je potrebno algoritme, oziroma dovoljene osnovne operacije tako izbrati, da so primerljive z operacijami na dejanskih raˇcunalnikih. To predvsem pomeni, da morajo v enem koraku predelati omejeno koliˇcino informacije. Nabor operacij na sliki 2.13 gotovo zadoˇsˇca temu pogoju, ima pa tudi lastnost, da je z njim moˇzno realizirati urejanje tabele dolˇzine n pri poljubnem n (gl. nalogo 3). Dodaten pogoj, ki velja za stavek goto, je potreben za to, da prepovemo zanke in na ta naˇcin zagotovimo ustavljanje programa. Potrebno pa je poudariti, da je opisana vrsta programov le teoretiˇcni pripomoˇcek pri izpeljavi spodnje meje, saj nima posebne praktiˇcne vrednosti. Namreˇc, ne pozna cele vrste operacij, ki so potrebne in zaˇzelene pri praktiˇcnih programih, npr. operacije z indeksi in zanke, ki omogoˇcajo, da napiˇsemo program, ki zna urejati tabele poljubne dolˇzine in ne le neke konstantne, vnaprej doloˇcene dolˇzine. Glede vhodnih podatkov izgleda na prvi pogled, da je moˇznih kombinacij vhodnih podatkov zelo veliko — celo neskonˇcno, ˇce zanemarimo dejstvo, da so ˇstevila, ki jih lahko raˇcunalnik obdeluje, omejena na neko konˇcno mnoˇzico. Vendar glede na to, da smo izbrali mnoˇzico programov, ki razpoznavajo le medsebojno razmerje posameznih elementov tabele (≤,≥,<,>), je oˇcitno, da konkretne vrednosti elementov tabele niso 38 POGLAVJE 2. UREJANJE " " 1 2 3 " " 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 #" , #" , 1 2 3 2 1 3 #" 1 3 2 2 1 3 , 2 1 3 ne #" , 3 1 2 a[1] > a[2] ne da #" # " a[2] > a[3] ne " # 1 2 3 1 3 2 2 1 3 2 1 3 1 2 3 , #" 2 1 3 , 3 1 2 # 3 1 2 1 3 2 1 3 2 " " , da #" 1 3 2 # " 1 2 3 #" 2 3 1 a[2] > a[3] a[2] > a[3] ne da " # 1 2 3 2 3 1 1 2 3 1 3 2 , 1 2 3 3 1 2 2 3 1 1 2 3 2 1 3 #" , #" , 3 2 1 3 2 1 a[1] > a[2] ne da " #" # a[2] > a[3] ne " # 1 3 2 3 1 2 2 3 1 2 3 1 1 2 3 , 3 2 1 # 3 1 2 3 2 1 # 1 3 2 a[2] > a[3] ne da " # 1 2 3 3 2 1 # 1 2 3 # # Slika 2.14: Drevo sledi navadnih zamenjav pri n = 3. pomembne in da lahko vzamemo n konkretnih, razliˇcnih vrednosti, najenostavneje kar 1, 2, . . . , n. Seveda so moˇzna tudi vhodna zaporedja z veˇc enakimi vrednostmi, vendar se nam zdi, da je naloga urejanja zaporedja z veˇc razliˇcnimi vrednostmi teˇzja od naloge urejanja zaporedja z manj razliˇcnimi vrednostmi in zato, ˇce hoˇcemo dobiti zaporedje, na katerem se dani algoritem najslabˇse obnaˇsa, je smiselno vzeti kar n razliˇcnih vrednosti. Iz tega pa neposredno sledi, da namesto vseh moˇznih vhodnih zaporedij lahko obravnavamo le permutacije simbolov 1, 2, . . . , n, ki jih je skupno n!. Sedaj se bomo poglobili v naravo ˇcasovne karakteristike, T (P, n), algoritma P pri ˇ je P algoritem iz opisanega razreda, lahko zanj nalogi urejanja tabele dolˇzine n. Ce sestavimo drevo sledi za vhodne podatke dolˇzine n. To je dvojiˇsko drevo, ˇcigar vsaka veja predstavlja zgodovino izraˇcuna P na neki vhodni permutaciji. Vsako vozliˇsˇce drevesa je oznaˇceno z mnoˇzico permutacij, ki do njega pripelje. Na primer, koren drevesa je zaznamovan z mnoˇzico vseh permutacij (Sn ), pri neposrednih naslednikih korena odpadejo vse permutacije, ki niso zdruˇzljive z rezultatom primerjanja, ki pripelje do naslednika itn. 2.1 Primer Kot primer vzamemo metodo urejanja z navadnimi zamenjavami (angl. bubblesort) pri n = 3. Metodo realizira naslednji program 39 2.2. NOTRANJE UREJANJE 1. 2. 3. 4. if a[2] > a[3] then Z(2,3) ; if a[1] > a[2] then Z(1,2) ; if a[2] > a[3] then Z(2,3) ; halt ; Programu ustreza drevo sledi, ki je prikazano na sl. 2.14. Na sliki smo vsako permutacijo predstavili z dvema zaporedjema: na levi strani oklepaja je prvotna urejenost simbolov, na desni pa trenutna urejenost (pri opazovanem vozliˇsˇcu). Pri konˇcnih vozliˇsˇcih vidimo, da so na desnih straneh oklepajev povsod simboli pravilno urejeni, kar pomeni, da se je postopek urejanja pravilno konˇcal. ˇ Cas programa pri neki vhodni permutaciji ocenimo s ˇstevilom primerjanj od zaˇcetka programa do ustavitve. Na drevesu sledi je to enako ˇstevilu vozliˇsˇc na neki veji. Torej: dolˇzina veje predstavlja koliˇcino T (P, In ), kjer je In vhodna permutacija, ki pripelje do konˇcnega vozliˇsˇca veje. Za koliˇcino T (P, n) nato vzamemo dolˇzino najdaljˇse veje v drevesu sledi programa P . Spodnjo mejo za ˇcas urejanja tabele dolˇzine n dobimo iz naslednjih premislekov: 1. Vsakemu konˇcnemu vozliˇsˇcu drevesa sledi je prirejena najveˇc ena vhodna permutacija (dve razliˇcni permutaciji peljeta do razliˇcnih konˇcnih vozliˇsˇc). 2. Upoˇstevamo le programe, ki pravilno delujejo na vseh vhodnih permutacijah, ki jih je skupno n!. ˇ je T (P, n) najveˇcja dolˇzina veje drevesa sledi in ˇce je N ˇstevilo konˇcnih vozliˇsˇc, Ce oˇcitno velja, 2T (P,n) ≥ N ≥ n!. (2.11) Prva od zapisanih relacij (2.11) je dobro znano razmerje med viˇsino dvojiˇskega drevesa in ˇstevilom njegovih konˇcnih vozliˇsˇc, druga pa izhaja iz premislekov 1 in 2 zgoraj. Leva od relacij (2.11) se najbolj pribliˇzuje enakosti, ko je drevo uravnoteˇzeno. Koliˇcino n! predstavimo s pribliˇzkom, ki ga daje Stirlingova formula: √ (2.12) n! ≈ nn e−n 2πn (relativna napaka te formule je manj kot 10% pri n = 1 in se zmanjˇsuje pri naraˇsˇcajoˇcem n). Iz 2.11 in 2.12 pa dobimo, T (P, n) ≥ cn log2 n, (2.13) pri neki konstanti c > 0. Na podlagi (2.9) sledi, da velja tudi T (n) ≥ cn log2 n. 2.2.5 (2.14) Shellovo urejanje (izboljˇ sano vstavljanje) Shellov algoritem za urejanje ima danes le zgodovinski pomen, ker predstavlja prvi algoritem za urejanje tabel, ki je porabil manj ˇcasa kot Θ(n2 ), kjer je n dolˇzina tabele. Zanimiv pa je tudi zaradi nekaterih svojih na videz protislovnih lastnosti. 40 POGLAVJE 2. UREJANJE Program 2.1: Shellovo urejanje 1 2 4 4 5 6 7 9 9 10 11 13 14 14 15 16 17 19 19 20 21 22 23 24 25 27 27 28 29 30 31 32 33 34 35 36 37 38 39 40 42 42 PROCEDURE Lg∗(n:LONGINT ):LONGINT ; VAR res:LONGINT ; BEGIN res:=0; WHILE n#0 DO n:=n DIV 2 ; INC (res) END; RETURN res END Lg; PROCEDURE ShellParams∗(n:LONGINT ;VAR h,t:LONGINT ); (∗ Zaˇcetno ˇstevilo podtabel, t v tabeli z n podatki ter ˇstevilo korakov h ∗) VAR i:LONGINT ; BEGIN IF n>=4 THEN t:=Lg(n)−2 ELSE t:=1 END; h:=1; FOR i:=2 TO t DO h:=2∗h+1 END END ShellParams; PROCEDURE ShellSort∗(VAR a:APItem;h,t:LONGINT ); VAR i,j ,k ,s:LONGINT ; x :I .PItem; (∗ Tabela a vsebuje na svojem zaˇcetku h dodatnih elementov a[0], a[1], . . . , a[h−1], ki imajo vlogo ˇcuvajev med vstavljanjem v podtabele; zaporedje, ki ga urejamo, pa predstavljajo komponente a[h], a[h+1], . . . , a[LEN(a)−1]∗) BEGIN k :=h;(∗ zaˇcetni korak ∗) WHILE k >= 1 DO s:=h−k ; (∗ zaˇcetni ˇcuvaj ∗) FOR i:=h+k TO LEN (a)−1 DO x :=a[i];j :=i−k ; a[s]:=x ; WHILE x .k < a[j ].k DO a[j +k ]:=a[j ];j :=j −k END; a[j +k ]:=x ; s:=s+1; IF s=h THEN s:=h−k END END; k :=(k −1) DIV 2 END END ShellSort; Shellovo urejanje (konec) 41 2.2. NOTRANJE UREJANJE 1 2 3 1 2 3 1 2 3 1 2 3 0 1 2 3 4 5 6 7 8 9 10 11 Slika 2.15: Zaˇcetna razdelitev tabele na podtabele Algoritem deluje tako, da tabelo razdeli na veˇc podtabel (slika 2.15, desno od navpiˇcne ˇcrte), nato na vsaki podtabeli izvaja navadno vstavljanje. Na sliki 2.15 so podtabele zaznamovane z 1, 2, 3. Za poenostavitev izstopnega pogoja iz zanke ima vsaka podtabela lastnega ˇcuvaja (levo od navpiˇcne ˇcrte). Ko opravimo to fazo postopka, ga ponovimo z manjˇsim ˇstevilom podtabel in tako nadaljujemo do ˇstevila podtabel, 1. V tem pa je tudi navidezna protislovnost postopka: ˇce postopek zakljuˇcimo z navadnim vstavljanjem na celotni vhodni tabeli, zakaj je hitrejˇsi od enega samega navadnega vstavljanja? Odgovor je, da, medtem ko ima eno samo navadno vstavljanje znaˇcilnost, da v povpreˇcju pri vstavljanju prestavlja razmeroma dolga podzaporedja (program 2.1, zanka v vrsticah 33 do 35), se dolˇzina zaporedij, ki jih prestavljamo pri veˇckratnem navadnem vstavljanju, zmanjˇsa. ˇ Stevilo podtabel, na katere razdelimo vhodno tabelo v posamezni fazi, je podano kot zaporedje h1 , h2 , . . . , ht , za katerega pa velja, kot smo ˇze zapisali, ht = 1, hi+1 < hi , 1 ≤ i ≤ t − 1 Matematiˇcna analiza postopka je razmeroma zapletena, tako da se zanaˇsamo preteˇzno na empiriˇcne izkuˇsnje. Na primer, znano je, da ˇstevila hi ne smejo imeti skupnih faktorjev. Knuth [8, razdelek 5.2.1] priporoˇca zaporedje 1, 4, 13, 40, 121, . . . (v nasprotnem vrstnem redu), za katerega velja: hk−1 = 3hk + 1, ht = 1 in t = blog3 nc − 1, ali pa 1, 3, 7, 15, 31, . . . , s pravilom: hk−1 = 2hk + 1, ht = 1 in t = blog2 nc − 1(2 ). Za slednje zaporedje lahko z matematiˇcno analizo izraˇcunamo ˇcasovno zahtevnost O(n1.2 ), kar pa je ˇse vedno precej slabˇse od najboljˇsih danes znanih metod. 2.2.6 Urejanje s kopico (izboljˇ sano izbiranje) Urejanje s kopico ali drevesno urejanje (slika 2.16) izhaja iz ugotovitve, da se pri navadnem izbiranju ˇze opravljeno delo ponavlja. Namreˇc, potem ko med izvajanjem 2 Spomnimo se, da je bxc definirano kot najveˇ cje celo ˇstevilo, ki ne presega x. Podobno je dxe definirano kot najmanjˇse celo ˇstevilo, ki ni manjˇse od x. 42 POGLAVJE 2. UREJANJE 1 2 3 4 5 6 7 8 9 10 11 13 13 14 15 16 18 18 20 20 21 22 23 24 26 26 PROCEDURE HeapSort∗(VAR a:APItem); (∗ Pozor! Indeksi v tabeli so 0, 1, . . . n − 1 ∗) VAR l ,r : LONGINT ; x : I .PItem; (∗ 1 ≤ l, r ≤ n leva in desna meja podkopice ∗) PROCEDURE Sift; VAR i,j :LONGINT ; x : I .PItem; BEGIN i:=l ; j :=2∗i; x :=a[i−1]; WHILE j < r DO IF a[j −1].k < a[j ].k THEN INC (j ) END; IF x .k >= a[j −1].k THEN j :=r +1 (∗ izhod iz zanke ∗) ELSE a[i−1]:=a[j −1]; i:=j ; j :=2∗i END END; IF (j =r ) & (x .k < a[j −1].k ) THEN a[i−1]:=a[j −1]; a[j −1]:=x ELSE a[i−1]:=x END END Sift; BEGIN l :=LEN (a) DIV 2; r :=LEN (a); WHILE l >= 1 DO Sift ; DEC (l ) END; INC (l ); WHILE r > 1 DO x :=a[0]; a[0]:=a[r −1]; a[r −1]:=x ; DEC (r ); Sift END END HeapSort; Slika 2.16: Urejanje s kopico 23 2 3 17 11 4 5 15 8 9 1 3 6 10 9 13 Slika 2.17: Kopica 7 5 43 2.2. NOTRANJE UREJANJE 14 23 1 2 3 17 11 4 5 15 8 9 3 6 10 7 5 9 13 Slika 2.18: Popravljanje pokvarjene kopice jedra zunanje zanke algoritma na sliki 2.10 poiˇsˇcemo najmanjˇsi element, isti postopek ponovimo pri naslednjem izvajanju, ne glede na dejstvo, da bi bilo moˇzno del informacije, ki smo jo nabrali med predhodnim izvajanjem, izkoristiti tudi kasneje. Urejanje s kopico ohranja informacijo iz predhodnih iskanj najmanjˇsega elementa v posebni podatkovni strukturi, ki ji pravimo kopica (angl. heap). Kopica je urejeno, levo uravnoteˇzeno dvojiˇsko drevo, katerega vozliˇsˇca hranijo informacijo o kljuˇcih. Drevo je urejeno, ker velja, da je kljuˇc nekega vozliˇsˇca v ≥ od kljuˇcev vseh vozliˇsˇc w, ki pripadajo poddrevesu s korenom v (namesto ≥ vˇcasih uporabljamo urejenost ≤). Drevo je uravnoteˇzeno, ker se razliˇcne veje med seboj razlikujejo po dolˇzini kveˇcjemu za 1 in je levo uravnoteˇzeno, ker so daljˇse veje zbrane na levi strani drevesa. Primer kopice je prikazan na sliki 2.17. Bistvenega pomena za uˇcinkovitost metode je uˇcinkovita realizacija kopice s tabelariˇcno strukturo. Na sliki 2.17 so zraven vozliˇsˇc zapisani njihovi indeksi v tabeli, ki kopico predstavlja. Pravilo za indekse je: ˇ so indeksi tabele 1, 2, . . . , n, je indeks levega naslednika vozliˇsˇca z inCe deksom i enak 2i, desnega naslednika pa 2i + 1. (V programski realizaciji tabele so indeksi prestavljeni v 0, 1, . . . , n − 1.) Nadalje je pomembno dejstvo, da je potem, ko v kopici zamenjamo koren (jo “pokvarimo”), potrebno popraviti le enega izmed dveh poddreves, ki izvirata pri korenu. Na primer, na sliki 2.18 smo zamenjali vrednost 23 pri korenu z novo vrednostjo, 14. Po tem pri korenu ni veˇc izpolnjen pogoj urejenosti in je zato potrebno element 14 prestaviti. Nalogo opravimo tako, da izberemo veˇcjega od dveh naslednikov (17,11), ga dvignemo do korena, se spustimo do izpraznjenega vozliˇsˇca in operacijo ponavljamo do vozliˇsˇca, ˇcigar oba naslednika sta manjˇsa ali enaka novemu elementu (14). Na sliki je to vozliˇsˇce z elementom 15. Novi element postavimo na pravkar izpraznjeno mesto. Na sliki 2.18 kaˇzejo prekinjene ˇcrte potrebne zamenjave. Algoritem, ki uresniˇcuje opisan postopek (“pogrezanja”), je na sliki 2.16 zapisan v vrsticah 5 do 18. Je pa sestavljen tako, da ga lahko sproˇzimo tudi na poddrevesu (“podkopici”) celotne 44 POGLAVJE 2. UREJANJE 9 2 3 23 10 4 5 17 8 15 1 3 6 11 7 5 9 13 Slika 2.19: Sestavljanje kopice kopice, pri ˇcemer je poddrevo doloˇceno z indeksoma l, ki predstavlja indeks korena, in r, ki predstavlja zadnji element na skrajno desni veji poddrevesa. Zaradi dejstva, da se pogrezanje pomika od korena po neki veji navzdol (v skrajnem primeru do lista drevesa), zahteva ˇcas O(log2 n), kjer je n ˇstevilo elementov v kopici. Do sedaj nismo obravnavali, kako iz nakljuˇcno urejene tabele pridobimo kopico. Izkaˇze se, da tudi to nalogo lahko opravimo z operacijo pogrezanja. Primer je prikazan na sliki 2.19: konˇcna vozliˇsˇca drevesa obravnavamo kot poddrevesa velikosti 1, ki so v vsakem primeru ˇze kopice (oziroma “podkopice”). Zato postopek zaˇcnemo pri prvem vozliˇsˇcu, ki predstavlja koren nekega poddrevesa, ki ima velikost veˇcjo kot 1. To je v naˇsem primeru vozliˇsˇce z indeksom 4 (v sploˇsnem pa je to vozliˇsˇce z indeksom nDIV2). V naˇsem konkretnem primeru je pogoj urejenosti izpolnjen, zato nadaljujemo v smeri puˇsˇcic. Naslednje vozliˇsˇce je 10. V tem primeru algoritem za popravljanje kopice zamenja 10 in 11. Pri vozliˇsˇcu 23 je pogoj urejenosti zopet izpolnjen. Konˇcno pri vozliˇsˇcu 9 algoritem za popravljanje kopice postavi 9 v konˇcno vozliˇsˇce skrajno leve veje, prejˇsnje vrednosti pa prestavi po veji za eno mesto viˇsje. Na ta naˇcin dobimo kopico, ki je prikazana na sliki 2.17. Celoten postopek pa zaseda v algoritmu na sliki 2.16 le eno vrstico, 21. Analiza ˇ casovne zahtevnosti. Na podlagi pravkar podanega opisa in najveˇcje porabe ˇcasa algoritma pogrezanja (O(log2 n)) ni teˇzko podati ocene porabe ˇcasa algoritma na sliki 2.16. Postopek sestavljanja kopice zahteva ˇcas ≤ c n2 log2 n (pri neki konstanti c), medtem ko samo urejanje (vrstice 22 – 25) zahteva ≤ c0 n log2 n. Torej je celotna poraba ˇcasa O(n log n). 2.2.7 Urejanje s porazdelitvami (izboljˇ sane zamenjave) Pri metodi navadnih zamenjav (slika 2.11) izvajamo zamenjave na izredno neuˇcinkovit naˇcin, saj sprehod po vseh elementih tabele po vrsti in zamenjava nepravilno postavljenih sosednjih elementov le poˇcasi pripelje do ˇzelene urejenosti. Mnogo bolj 2.2. NOTRANJE UREJANJE 1 2 3 4 5 6 8 8 9 10 11 13 13 14 15 17 17 19 19 20 21 22 23 24 25 27 27 28 45 PROCEDURE Partition∗(VAR a:APItem; VAR l ,r : LONGINT ; m:LONGINT ); VAR i,j : LONGINT ; BEGIN i:=l ; j :=r ; REPEAT WHILE a[i−1].k < m DO INC (i) END; WHILE a[j −1].k > m DO DEC (j ) END; IF i <= j THEN Exch(a[i−1],a[j −1]); INC (i);DEC (j ) END UNTIL i > j ; l :=i; r :=j END Partition; PROCEDURE QuickSort∗(VAR a:APItem); PROCEDURE Sort(l ,r : LONGINT ); VAR i,j :LONGINT ; BEGIN i:=l ; j :=r ; Partition(a,i,j ,a[((l +r ) DIV 2)−1].k ); IF l < j THEN Sort(l ,j ) END; IF i < r THEN Sort(i,r ) END END Sort; BEGIN Sort(1,LEN (a)) END QuickSort; Slika 2.20: Urejanje z porazdelitvami uˇcinkovito je zamenjave izvajati na elementih, ki so na veˇcjih medsebojnih razdaljah. Opisano idejo realiziramo tako, da na vsakem koraku tabelo porazdelimo tako, da so vsi elementi s kljuˇci, ki so enaki neki mejni vrednosti ali manjˇsi od nje, razmeˇsˇceni v spodnjem delu tabele, oni, ki pa so enaki ali veˇcji, pa v zgornjem delu tabele. Na ta naˇcin nalogo lahko reˇsimo z rekurzivnim razcepom. Opisano porazdelitev je moˇzno doseˇci tako, da se z indeksom i sprehodimo od zaˇcetka tabele proti koncu, z drugim indeksom, j, pa od konca proti zaˇcetku. Indeks i se ustavi pri prvem elementu, ki je ≥ od mejne vrednosti, indeks j pa pri prvem elementu, ki je ≤ od mejne vrednosti. Nato se opravi zamenjava, postopek pa se nadaljuje do trenutka, ko se indeksa sreˇcata. Na ta naˇcin dobimo v podtabeli, ki obsega indekse od zaˇcetka do mesta sreˇcanja i in j, vrednosti, ki so enake mejni vrednosti ali manjˇse od nje, v komplementarni podtabeli pa vrednosti, ki so enake mejni vrednosti ali veˇcje od nje. Posebno vpraˇsanje je, kako izbrati mejno vrednost. Oˇcitno bi bilo zaˇzeleno, da postopek porazdelitve prvotno tabelo razdeli na dva enaka dela, vendar, ˇzal, tega ne moremo zagotoviti, ker nimamo 46 POGLAVJE 2. UREJANJE 1. 3 2 5 2. !! 3 2 5 3. 3 2 4. !! 3 2 5 i 1 5. 2 6. 3 i 1 7. 1 2 i, j 2 1 j 3 3 7 i 4 6 1 8 6 4 6 8 i 8 7 4 j 4 6 i 6 1 j 1 j 5 7 8 4 5 4 j 7 5 6 4 Slika 2.21: Primer urejanja s porazdelitvami nobene informacije o zaˇcetni urejenosti elementov. Zato so vse izbire mejne vrednosti enakovredne. V algoritmu na sliki 2.20 uporabljamo za mejno vrednost kljuˇc elementa a[(l + r)DIV 2 − 1] (bodimo pozorni, da so i, j, l in r v mejah [1, LEN (a)], medtem ko so indeksi tabele v mejah [0, LEN (a) − 1]). Primer. Na sliki 2.21 je prikazan konkreten primer urejanja s porazdelitvami. Pri prvi porazdelitvi je mejna vrednost 7 in zato se indeksa i in j ustavita, kot je to prikazano v prvi vrstici. Po opravljeni zamenjavi indeksa nadaljujeta svojo pot in se ustavita, kot je to nakazano v drugi vrstici. Tu ugotovimo, da bi pri urejanju priˇslo do napake, ˇce bi uporabili pravilo, da po ustavitvi indeksov obvezno pride do zamenjave, saj bi se 1 in 8, ki sta v drugi vrstici pravilno urejena, postavila nepravilno. Zaradi tega je pred zamenjavo potrebno preveriti pogoj i ≤ j (slika 2.20, vrstice 10 – 12) Sedaj je prva porazdelitev opravljena (tretja vrstica) in lahko loˇceno nadaljujemo z urejanjem podtabel od indeksa 1 to 6 ter od 7 do 8. Pri porazdelitvi prve podtabele je sedaj mejna vrednost 5 in se indeksa ustavita, kot je to prikazano (tretja vrstica). Po zamenjavi indeksa nadaljujeta svoji potovanji, dokler se ne ustavita, kot je to prikazano v ˇcetrti vrstici. V ˇcetrti vrstici pride do podobnega problema kot v drugi vrstici (in potrebe po preverjanju, ali sta se pred zamenjavo indeksa sreˇcala). V peti vrstici je prikazan rezultat druge porazdelitve, nato pa nadaljujemo s porazdeljevanjem leve podtabele z mejno vrednostjo 2. Po zamenjavi dobimo urejeno zaporedje (vrstica 6), vendar se postopek nadaljuje ˇse za en korak (vrstica 7). V tem primeru algoritem opravi nepotrebno zamenjavo (element 2 zamenja s samim seboj), vendar je tak algoritem v povpreˇcju hitrejˇsi, kot ˇce bi preverjali, ali velja i = j. Ostale podtabele se urejajo na podoben naˇcin, le da je za to potreben vedno samo en korak, ker so velikosti 2. 2.2. NOTRANJE UREJANJE 47 Analiza ˇ casovne zahtevnosti. Znaˇcilnost metode je, da je dobra (in to zelo dobra) le v povpreˇcnem primeru, medtem ko je njeno obnaˇsanje v najslabˇsem primeru (ki pa nastopi izredno redko) slabo. Izhodiˇsˇce za oceno ˇcasovne zahtevnosti je ˇcas, ki ga porabi postopek porazdeljevanja. Ni se teˇzko prepriˇcati, da procedura Partition (slika 2.20, vrstice 1–15) za tabelo velikosti n porabi ˇcas Θ(n). Na podlagi tega podatka ˇze vemo, da je ˇcas, ki bi ga porabilo urejanje s porazdelitvami, ˇce bi tabelo vedno razdelili na dva enaka dela, T= (n) = Θ(n log n) (2.15) (gl. nalogo 4) in glede na dejstvo, da je to po velikostnem redu enako spodnji meji za ˇcas urejanja tabele dolˇzine n (2.11), domnevamo, da je to najboljˇsi ˇcas, ki ga metoda zmore. Sedaj bomo ocenili povpreˇcni ˇcas. Povpreˇcje je priˇcakovana vrednost neke sluˇcajne spremenljivke, kjer vsaki moˇzni vrednosti pripiˇsemo neko verjetnost. V naˇsem primeru je sluˇcajna spremenljivka, ki nas zanima, ˇcas, ki ga porabi urejanje s porazdelitvami na tabeli dolˇzine n, razliˇcni moˇzni izidi pa ustrezajo razliˇcnim velikostim k leve podtabele, ki nastane kot rezultat porazdelitve (k = j − l + 1 v vrstici 22 na sliki 2.20). Najprej razmislimo, kolikˇsen je razpon vrednosti velikosti podtabel, na katere razdelimo prvotno tabelo dolˇzine n. Oˇcitno je, da nobena od dveh podtabel ni veˇcja od n − 1, kajti vsak od indeksov i in j se premakne vsaj enkrat. Druga skrajna vrednost velikosti podtabele je 0, ki ustreza primeru, ko se na primer vrednost j spusti pod vrednost l (bralec naj poskuˇsa poiskati pogoje, pod katerimi se to zgodi). Po tem razmisleku lahko naˇsetejemo dva privzetka, na podlagi katerih bomo definirali rekurenˇcno relacijo za izraˇcun ˇcasa: 1. pri vsaki porazdelitvi se tabela velikosti n razdeli na dva dela, od katerih ima eden velikost k, drugi pa n − k − 1 pri 0 ≤ k ≤ n − 1; 2. vse vrednosti k so enakoverjetne, torej je verjetnost, da je leva podtabela velikosti k enaka n1 za vse 0 ≤ k ≤ n − 1. Prvi privzetek pravzaprav ne ustreza resniˇcnosti, kajti tabela se porazdeli v privzetem sorazmerju le takrat, ko sta indeksa i in j po izhodu iz dveh zank v vrsticah 8 in 9 na sliki 2.20, enaka (iz tega sledi, da je element s tem indeksom enak mejnemu elementu). V primeru, ko je indeks tega elementa k + 1, se opravi fiktivna zamenjava elementa a[k + 1] s samim seboj in se vrednost j spusti na k, vrednost i pa dvigne na k + 2. Iz tega sledi, da je velikost leve podtabele enaka k, desne pa n − k − 1. Bolj pogost pa je izid porazdeljevanja, ko se tabela porazdeli v razmerju k : n − k. Vendar bomo kljub malenkostni napaki zapisani privzetek uporabili, ker nam omogoˇca zapis preproste rekurenˇcne relacije, ki povezuje povpreˇcni ˇcas za urejanje tabele dolˇzine n, Tave (n), z vrednostmi iste koliˇcine pri manjˇsih vrednostih parametra n: Tave (n) = 0 cn + 1 n Pn−1 i=0 pri n = 0 ali n = 1 [Tave (i) + Tave (n − i − 1)] , pri n ≥ 2, (2.16) 48 POGLAVJE 2. UREJANJE pri ˇcemer je cn ˇcas potreben za porazdelitev na levi in desni del, vsota pa je priˇcakovani ˇcas za urejanje obeh delov tabele. (gl. tudi nalogo 5.1). Drugo vrstico enaˇcbe (2.16) lahko spremenimo v naslednjo obliko: Tave (n) = cn + n−1 2X Tave (i). n i=0 (2.17) Sedaj dokaˇzemo z indukcijo po n, da pri n ≥ 2 velja Tave (n) ≤ 2(c + δ)n ln n, (2.18) kjer je δ neka majhna konstanta, ki jo bomo ocenili naknadno. Pri n = 2 sledi (2.18) neposredno iz (2.17) (2 ln 2 ≈ 1.38) za poljubno nenegativno vrednost δ. V induktivnem koraku najprej prepiˇsemo (2.17) v obliko Tave (n) ≤ cn + n−1 4(c + δ) X i ln i. n i=0 (2.19) Funkcija x ln x ima pozitiven prvi odvod na intervalu [1, n], torej lahko zapiˇsemo na podlagi (1.12), Z n n−1 X x2 1 n i ln i ≤ x ln xdx = (ln x − ) 1 2 2 1 (2.20) i=1 n2 1 1 = (ln n − ) + , 2 2 4 in ˇce vstavimo desno stran (2.20) v (2.19), konˇcno dobimo, h 2 i n 1 1 (ln n − ) + Tave (n) ≤ cn + 4(c+δ) n 2 2 4 (2.21) = 2(c + δ)n ln n − δn + (c+δ) . n δ doloˇcimo iz pogoja (c+δ) − δn ≤ 0 pri n ≥ 3, kar daje δ ≥ 8c . n Sedaj se lotimo ocene za najveˇcjo porabo ˇcasa. V tem primeru izhajamo iz najmanjˇsega ˇcasa urejanja s porazdelitvami, T= (n) [gl. (2.15)] in ˇzelimo raziskati odvisnost ˇcasa od razliˇcnih porazdelitev prvotne tabele na dve podtabeli. Vpeljemo funkcijo dveh spremenljivk T2 (x, n), 1 ≤ x ≤ n − 1, z vrednostjo, enako porabi ˇcasa pri urejanju s porazdelitvami, ˇce prvotno tabelo razdelimo v razmerju [x, n − x], podrejene tabele pa tako, da je poraba ˇcasa najmanjˇsa. Izraˇcun si bomo poenostavili tako, da bomo obe spremenljivki, x in n obravnavali kot realni spremenljivki. Ker je v tem primeru T= (x) zvezna in ima prvi odvod pri x ≥ 1, lahko zapiˇsemo, T2 (x, n) = cn + T= (x) + T= (n − x) = cn + kx ln x + k(n − x) ln(n − x), kjer je k neka konstanta in dT2 (x, n) x = ln . dx n−x (2.22) 49 2.2. NOTRANJE UREJANJE Pri x = n/2 ima odvod (2.22) vrednost 0, pri x < n/2 je negativen, pri x > n/2 ˇ pa pozitiven. Torej T2 (x, n) monotono naraˇsˇca, ko se x pribliˇzuje 1 oziroma n. Ce isti premislek uporabimo na dveh podtabelah, na katere smo razdelili prvotno tabelo, dobimo ˇse bolj strmo narˇsˇcanje ˇcasa, ko se x pribliˇzuje 1 oziroma n. Torej sklepamo, da urejanje s porazdelitvami porabi najveˇ cˇ casa v primeru, ko so vse podtabele, ki imajo velikost razliˇ cno od 1, razdeljene v razmerju [1 : m − 1], ˇ ce je m velikost vsakokratne podtabele. V tem primeru dobimo vrednost porabe ˇcasa tako, da seˇstejemo vse ˇcase, ki so potrebni za porazdeljevanje prvotne tabele in podrejenih tabel: n−1 X Tmax (n) = c i = Θ(n2 ). i=1 Poraba pomnilnega prostora. Na zaˇcetku razdelka o urejanju tabel (2.2) smo zapisali, da nas zanimajo le algoritmi, ki ne porabijo bistveno veˇc prostora, kot ga zasedajo vhodni podatki. Sedaj bomo preverili, ali urejanje s porazdelitvami ta pogoj izpolnjuje. Najprej si zastavimo vpraˇsanje, kje lahko pride v algoritmu na sliki 2.20 do dodatne porabe prostora, saj na prvi pogled ne zasledimo drugih podatkovnih struktur kot vhodne tabele a. Izkaˇze se, da je vir dodatne porabe mehanizem rekurzije, ki ga uporabljamo za evidenco ˇse neopravljenih porazdelitev. Namreˇc, ko tabelo razdelimo na dva dela, lahko neposredno nadaljujemo delo le na enem delu, podatke o drugem pa moramo shraniti. Za to sta primerna podatkovna struktura sklad in mehanizem rekurzije. Ker je dandanes mehanizem rekurzije vgrajen v praktiˇcno vse programske jezike, so njegove podrobnosti pred programerjem skrite. Vendar, ker ˇzelimo analizirati prav lastnosti tega mehanizma, bomo zapisali program za urejanje s porazdelitvami tako, da bo rekurzijo uporabljal izrecno, kar nam bo omogoˇcilo, da preverimo njene lastnosti. Program je prikazan na sliki 2.22 z nekoliko veˇc podrobnosti kot prejˇsnji programi. V vrsticah 4–12 so najprej zapisani potrebni podatkovni tipi, med katerimi opozarjamo na osnovni tip sklada StackRec, ki rabi za shranjevanje podatkov o podtabelah prvotne tabele, in sicer v obliki leve in desne meje ter na tip SortStruc, ki zdruˇzuje vse podatke, ki so potrebni pri urejanju. Vse procedure, ki so deklarirane v modulu NRQuickSort na sliki 2.22, so procedurne konstante, ki so prirejene prav tipu SortStruc. Primerek tipa dinamiˇcno zgeneriramo s klicem procedure Init (vrstice 14–17). Procedure Push, Pop in Empty v vrsticah 19–31 predstavljajo dobro znane osnovne operacije, ki pripadajo podatkovni strukturi sklada. V proceduri Sort (vrstice 33–46), ki dejansko opravlja urejanje, na zaˇcetku (vrstica 37) vstavimo v sklad podatke o celotni tabeli (leva meja je 1, desna, LEN (a)), nato vstopimo v zanko (vrstice 38–45), ki z vrha sklada jemlje podatke o podtabelah in vsakiˇc sproˇzi porazdelitev. V kakˇsnem primeru pride do najveˇcje porabe prostora za sklad? Oˇcitno takrat, ko je po vsakem izvajanju porazdelitvene procedure v vrstici 41 indeks i enak r in je 50 POGLAVJE 2. UREJANJE 1 2 4 5 5 6 7 8 9 10 11 12 14 14 15 16 17 19 19 21 21 22 24 24 26 26 27 29 29 30 31 33 33 34 36 36 37 38 39 40 41 42 43 44 45 46 48 48 MODULE NRQuickSort; IMPORT A:=ArrSort,I :=IntSort,S :=Sort; TYPE StackRec=RECORD l ,r :LONGINT END; PStack =POINTER TO Stack ; Stack =ARRAY OF StackRec; SortStruc∗=RECORD a∗:A.PAPItem; s∗:PStack ; t∗:LONGINT END; PROCEDURE (VAR o:SortStruc)Init∗(m,n:LONGINT ); BEGIN (∗ dinamiˇcno generiramo tabelo a in sklad ∗) NEW (o.a,m); NEW (o.s,n); o.t:=0 END Init; PROCEDURE (VAR o:SortStruc)Push∗(l ,r :LONGINT ); BEGIN o.s[o.t].l :=l ; o.s[o.t].r :=r ; INC (o.t) END Push; PROCEDURE (VAR o:SortStruc)Pop∗(VAR l ,r :LONGINT ); BEGIN DEC (o.t); l :=o.s[o.t].l ; r :=o.s[o.t].r END Pop; PROCEDURE (VAR o:SortStruc)Empty∗():BOOLEAN ; BEGIN RETURN o.t=0 END Empty; PROCEDURE (VAR o:SortStruc)Sort∗; VAR i,j ,l ,r : LONGINT ; BEGIN o.Push(1,LEN (o.a↑)); REPEAT o.Pop(l ,r ); REPEAT i:=l ;j :=r ; A.Partition(o.a↑,i,j ,o.a[((l +r )DIV 2)−1].k ); IF i <= r THEN o.Push(i,r ) END; r :=j UNTIL l >= r UNTIL o.Empty() END Sort; END NRQuickSort. Slika 2.22: Uporaba samostojnega sklada pri urejanju s porazdelitvami 51 2.2. NOTRANJE UREJANJE metoda n. zamenjave n. vstavljanje kopica porazdelitve n =1024 22 8 0 0 ˇcas (v stot. sek) 2048 5000 10000 93 576 2636 39 243 1103 1 4 13 0 2 5 20000 13297 6677 35 15 Slika 2.23: Rezultati urejanja razliˇcno dolgih zaporedij ˇstevil velikost tabele, na katero se nanaˇsa nov ukaz iz vrstic 42 in 43, enaka 1. V takem primeru bi algoritem dodal n ukazov (za urejanje podtabel velikosti 1) na sklad in bi torej za sklad potrebovali prostor Θ(n) (do enakega pojava pride v primeru, ko je na koncu porazdelitve, j = 1). Glede velikostnega reda se seveda niˇc ne spremeni, ˇce bi algoritem postopal nekoliko bolj pametno in ukazov za urejanje podtabel velikosti 1 ne bi zapisoval (temveˇc le take ukaze, ki se nanaˇsajo na veˇcje podtabele). Seveda je takˇsno obnaˇsanje algoritma v nasprotju s pogojem, ki smo ga omenili na zaˇcetku razdelka. Vendar se izkaˇze, da je reˇsitev tega problema presenetljivo preprosta: potrebno je le primerjati velikosti intervalov [l, j] in [i, r] ter v sklad zapisati veˇcji interval. Na ta naˇcin se velikost intervala, ki ga trenutno obdelujemo v zanki v vrsticah 40–44, najmanj prepolovi in med izvajanjem zanke ne moremo zapisati veˇc kot log2 n ukazov v sklad. Torej je potrebno zamenjati vrstic 42 in 43 z naslednjim stavkom: IF (l < j )&(j −l >=r −i) THEN o.Push(l ,j );l :=i ELSIF i < r THEN o.Push(i,r );r :=j END V tem primeru je moˇzno za velikost sklada izbrati log2 n. 2.2.8 Primerjava razliˇ cnih metod urejanja ˇ Stiri metode urejanja tabel (dve navadni in dve izboljˇsani) smo tudi praktiˇcno preizkusili. Tabela na sliki 2.23 prikazuje rezultate urejanja razliˇcno dolgih zaporedij 32bitnih ˇstevil. Za vsako dolˇzino smo nekajkrat nakljuˇcno generirali opisano zaporedje, opravili meritev dejanske porabe ˇcasa (v stotinkah sekunde) na osebnem raˇcunalniku in nato izraˇcunali povpreˇcen rezultat. Slika 2.24 pa podatke iz tabele 2.23 prikazuje grafiˇcno v dvojno logaritemskem merilu. 2.2.9 Iskanje k-tega elementa v tabeli Tehniko porazdelitve iz razdelka 2.2.7 lahko uporabimo tudi za reˇsevanje nekega dodatnega problema: podana je neurejena tabela in ˇzelimo poiskati k-ti element po velikosti. Seveda je moˇzno nalogo reˇsiti tako, da tabelo uredimo in nato vzamemo element z indeksom k. Toda ta naˇcin zahteva, kot ˇze vemo, ˇcas Θ(n log n). Zastavlja 52 POGLAVJE 2. UREJANJE 19000 10000 5000 1000 500 100 50 sek−2 ? • ........ ........... ............ ............ ........... . . . . . . . . . . . .... ............ ........... ........... ............ ............ . . . . . . . . . . .... ............ ............ ............ ............ ........... . . . . . . . . . . ........... ............ ............ ............ ............ . . . . . . . . . . . .. ............ ........... ............ ............ ...... . ....... ....... ....... ....... ....... ....... . . . . . . . . ...... ....... ....... ....... ....... ....... ....... ....... ....... . . . . . . . . . . ....... .... ....... ....... ....... ....... ....... ....... ? • ? • ? 10 • ◦ ◦ 2048 1 1024 5000 ? • ◦ 10000 4.0683 · 10−4 n log10 n ....... ....... .................... 3.324 · 10−5 n2 ◦ urejanje s kopico ? navadne zamenjave urejanje s porazdelitvami • navadno vstavljanje Slika 2.24: Grafiˇcna primerjava razliˇcnih metod urejanja 1 2 3 4 5 6 7 8 9 11 12 12 PROCEDURE Find ∗(VAR a:APItem;k : LONGINT ); VAR i,j ,l ,r : LONGINT ; BEGIN l :=1; r :=LEN (a); WHILE l <r DO i:=l ; j :=r ; Partition(a,i,j ,a[k −1].k ); IF j <k THEN l :=i ELSIF k <i THEN r :=j END END END Find ; Slika 2.25: Iskanje k-tega elementa po velikosti ◦ n 20000 2.3. ZUNANJE UREJANJE 53 se nam torej vpraˇsanje, ali je moˇzno na raˇcun bolj enostavne naloge zmanjˇsati ˇcas, ki je potreben za njeno reˇsitev. Izkaˇze se, da je nalogo moˇzno reˇsiti z algoritmom, ki je praktiˇcno istoveten algoritmu urejanja s porazdelitvami, le da moramo vzeti mejo x = a[k] ter spremeniti postopek, ki sledi porazdelitvi. Pri porazdelitvi so moˇzni trije izidi: element a[k] je bodisi manjˇsi od k-tega po velikosti, enak ali veˇcji. V prvem primeru je meja med podtabelama, ki sta rezultat porazdelitve, manjˇsa od k, v drugem je meja toˇcno pri k-tem elementu in v tretjem primeru je meja veˇcja od k. V prvem primeru nadaljujemo z reˇsevanjem iste naloge na podtabeli [j, r] (pri ˇcemer v tej tabeli iˇsˇcemo k − j + 1 element); v drugem primeru smo reˇsitev naˇsli in v tretjem primeru nadaljujemo z reˇsevanjem iste naloge na podtabeli [l, i]. Analiza porabe ˇ casa. Glede na to, da je algoritem le nekoliko predelan algoritem urejanja s porazdelitvami, je priˇcakovati, da ima podobne znaˇcilnosti. Tudi v tem primeru je najslabˇsi ˇcas Θ(n2 ), kajti, ˇce je izid porazdelitve na vsakem koraku [1, m−1] (m je velikost vsakokratne podtabele), je potrebno opraviti porazdelitev zaporedja podtabel velikosti n, n − 1, . . . , 1, za kar je potreben kvadratiˇcen ˇcas. Ko pa imamo m sreˇco, je rezultat porazdelitve na vsakem koraku pribliˇzno [ m 2 , 2 ] in moramo opraviti n n porazdelitev na zaporedju podtabel velikosti n, 2 , 4 , . . . , 1, kar zahteva ˇcas Θ(n). 2.3 Zunanje urejanje Pri razvijanju algoritmov za zunanje urejanje izhajamo iz osnovnih deklaracij, ki so prikazane na sliki 2.26. V priˇcujoˇci knjigi se nimamo namena spuˇsˇcati v podrobnosti operacij z datotekami, oziroma trakovi, zato bomo uporabljali doloˇcene procedure, ki so deklarirane drugje, jih pa uporabnik modula za sortiranje lahko priredi spremenljivkam InitRd, InitWr, Rd in Wr (vrstice 23–25). Prvi dve proceduri pripravita trak za branje ali pisanje, preostali dve pa nanj bereta ali piˇseta. 2.3.1 Navadno zlivanje Navadno zlivanje predstavlja osnovo, oziroma izhodiˇsˇce za vse algoritme zunanjega urejanja. Metoda deluje tako, da z vhodnega zaporedja bere podzaporedja dolˇzine k, za katera privzemamo, da so ˇze urejena, in jih izmeniˇcno zapisuje na dva izhodna trakova, nato pa s teh dveh trakov podzaporedja dolˇzine k zliva nazaj na vhodni trak v podzaporedja dolˇzine 2k. Zlivanje dveh (urejenih) podzaporedij pomeni sestavljanje novega urejenega podzaporedja iz elementov prvotnih podzaporedij. Na zaˇcetku je k = 1, opisani postopek pa se ponavlja, dokler ne preostane eno samo dolgo (pod)zaporedje (gl. sliko 2.27). Opisana struktura algoritma je jasno razvidna iz osnovnega bloka programa 2.2 (vrstice 53-60). Pri notranjih procedurah procedure navadnega zlivanja naj opozorimo na znaˇcilno zgradbo procedure, ki zliva dve podzaporedji v novo podzaporedje dvakratne dolˇzine (MergeRun, ki zaseda vrstice 29–44): sestavljena iz treh zank, od katerih se prva izvaja v primeru, ko sta oba trakova neprazna, oziroma ko ustrezni podzaporedji nista zakljuˇceni; druga (oziroma tretja) pa, 54 POGLAVJE 2. UREJANJE 1 2 4 5 5 6 7 8 10 10 11 12 13 14 15 16 17 18 19 20 22 23 23 24 25 26 27 MODULE FileSort; IMPORT I :=IntSort; TYPE PAL=POINTER TO AL; AL=ARRAY OF LONGINT ; PAPItem∗=POINTER TO APItem; APItem=ARRAY OF I .PItem; PSeq∗=POINTER TO Seq; Seq∗=RECORD END ; FSeqInit∗ =PROCEDURE(p:PSeq); FRdItem∗ =PROCEDURE(p:PSeq):I .PItem; FWrItem∗ =PROCEDURE(p:I .PItem;q:PSeq); FCpyRun∗ =PROCEDURE( inf ,outf :PSeq; VAR pI :I .PItem; VAR lastK :LONGINT ); FPPSort∗ =PROCEDURE(in:PSeq;VAR f :ARRAY OF PSeq):PSeq; FNoParam∗=PROCEDURE; VAR InitRd ∗,InitWr ∗:FSeqInit; Rd ∗:FRdItem; Wr ∗:FWrItem; CopyRun∗:FCpyRun; SpecInit∗:FNoParam; Slika 2.26: Osnovne deklaracije pri zunanjem urejanju vhod: 3; 7; 1; 2; 8; 6; 5; 4 po prvi porazdelitvi: po prvem zlivanju: po drugi porazdelitvi: po drugem zlivanju: 3; 1; 8; 5 3, 7; 1, 2; 6, 8; 4, 5 3, 7; 6, 8 1, 2, 3, 7; 4, 5, 6, 8 7; 2; 6; 4 1, 2; 4, 5 po tretji porazdelitvi: po tretjem zlivanju: 1, 2, 3, 7 1, 2, 3, 4, 5, 6, 7, 8 4, 5, 6, 8 (Meje med podzaporedji so nakazane s podpiˇ cji, ki pa rabijo le kot pomoˇ c bralcu; sicer pa vhodni podatki ne vsebujejo nobenega posebnega znaka, ki bi podzaporedja loˇ ceval med seboj.) Slika 2.27: Primer navadnega zlivanja 55 2.3. ZUNANJE UREJANJE ko je prvi (oziroma drugi) trak neprazen, oziroma nima zakljuˇcenega podzaporedja. Pri tem programu ni teˇzko oceniti njegovega ˇcasa delovanja: ocenimo ga s ˇcasom, ki je potreben za branje trakov, saj je to najbolj potratna operacija. Naj bo ˇstevilo podatkovnih zapisov N . Glede na to, da se dolˇzina urejenih podzaporedij po vsakem zlivanju podvoji, je oˇcitno potrebnih log2 (N ) korakov porazdelitve in zlivanja, pri ˇcemer na vsakem takem koraku trak s podatki preberemo dvakrat. Torej je ˇcas operacije navadnega zlivanja Θ(N log2 (N )). Ker smo na ta naˇcin ˇze takoj dosegli “magiˇcno” mejo Θ(N log(N )), so naprej moˇzne le ˇse izboljˇsave za konstanten faktor. Vendar pa imamo glede tega ˇse znatne zaloge, kot bomo videli kasneje. Ko razmiˇsljamo o moˇznih izboljˇsavah navadnega zlivanja, nam pride na misel nekaj idej: 1. odpraviti posebna koraka porazdelitve in zlivanja ter porazdelitev na dva trakova izvajati istoˇcasno z zlivanjem. Ker v tem primeru potrebujemo dva vhodna in dva izhodna trakova, pravimo tej metodi uravnoteˇzeno zlivanje. Uˇcinek izboljˇsave je, da ˇcas operacije prepolovimo; 2. poveˇcati hitrost z uporabo veˇc vhodnih trakov. Takemu zlivanju pravimo veˇcsmerno zlivanje. Ni se teˇzko prepriˇcati, da veˇcsmerno zlivanje z n vhodnimi trakovi zmanjˇsa ˇcas z N log2 (N ) (kar velja za dva trakova) na N logn (N ); 3. v primeru, ko znotraj vhodnega zaporedja ˇze obstajajo urejena podzaporedja, zlivati taka podzaporedja in ne podzaporedja vnaprej doloˇcene dolˇzine. Takemu zlivanju pravimo naravno zlivanje, ker izkoriˇsˇca ˇze obstojeˇco urejenost vhodnega traku. Najveˇcje urejeno podzaporedje v zaporedju, ki ga urejamo, poimenujemo ˇceta. Bodimo pozorni, da ima “najveˇcje” pomen, da zaporedja ni moˇzno podaljˇsati s prikljuˇcitvijo sosednih elementov in ne, da je tako zaporedje po dolˇzini najdaljˇse (glej sliko 2.28). Izboljˇsava, ki jo vnaˇsa naravno zlivanje, pa je odvisna od stopnje predhodne urejenosti vhodnih podatkov, oziroma od zaˇcetnega ˇstevila ˇcet. Program 2.2: Navadno zlivanje 1 2 4 4 6 6 7 9 PROCEDURE MergeSort∗(io,aux1 ,aux2 :PSeq); VAR k ,l :LONGINT ; PROCEDURE InitLoop; BEGIN InitRd (io); InitWr (aux1 ); InitWr (aux2 ) END InitLoop; se nadaljuje 56 POGLAVJE 2. UREJANJE Navadno zlivanje (nadaljevanje) 9 10 12 12 13 14 15 16 17 18 19 21 22 22 23 24 26 26 27 29 29 30 31 32 33 34 35 37 37 38 39 40 41 42 44 44 46 47 47 48 49 50 51 53 PROCEDURE Distribute; VAR p:I .PItem; PROCEDURE CopyKseq(in,out:PSeq;VAR p:I .PItem):BOOLEAN ; VAR i:LONGINT ; BEGIN i:=0; WHILE (p#NIL) & (i<k ) DO Wr (p,out); INC (i); p:=Rd (in) END; RETURN i=k END CopyKseq; BEGIN p:=Rd (io); WHILE CopyKseq(io,aux1 ,p) & CopyKseq(io,aux2 ,p) DO END END Distribute; PROCEDURE Merge(in1 ,in2 ,out:PSeq); VAR p1 ,p2 :I .PItem; PROCEDURE MergeRun ; VAR i1 ,i2 :LONGINT ; BEGIN i1 :=0;i2 :=0; WHILE (p1 #NIL)&(i1 <k )&(p2 #NIL)&(i2 <k ) DO IF p1 .k <p2 .k THEN Wr (p1 ,out);INC (i1 );p1 :=Rd (in1 ) ELSE Wr (p2 ,out);INC (i2 );p2 :=Rd (in2 ) END END; WHILE (p1 #NIL)&(i1 <k ) DO Wr (p1 ,out);INC (i1 );p1 :=Rd (in1 ) END; WHILE (p2 #NIL)&(i2 <k ) DO Wr (p2 ,out);INC (i2 );p2 :=Rd (in2 ) END END MergeRun; BEGIN InitWr (out);InitRd (in1 );InitRd (in2 ); p1 :=Rd (in1 ); p2 :=Rd (in2 ); REPEAT MergeRun; INC (l ) UNTIL (p1 = NIL) & (p2 = NIL) END Merge; se nadaljuje 57 2.3. ZUNANJE UREJANJE vhod: 7 ; 3, 6, 11; 8, 13; 9, 12; 1, 16; 10; 4, 15; 5; 2, 14; po porazdelitvi: 7, 8, 13; 1, 16; 4, 15; 2, 14; 3, 6, 11; 9, 12; 10; 5; po prvem zlivanju (in porazdelitvi): 3, 6, 7, 8, 11, 13; 4, 10, 15; 1, 9, 12, 16; 2, 5, 14; po drugem zlivanju (in porazdelitvi): 1, 3, 6, 7, 8, 9, 11, 12, 13, 16; 2, 4, 5, 10, 14, 15; po tretjem zlivanju: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16; (Meje med ˇ cetami so nakazane s podpiˇ cji, ki pa rabijo le kot pomoˇ c bralcu; sicer pa vhodni podatki ne vsebujejo nobenega posebnega znaka, ki bi loˇ ceval ˇ cete med seboj.) Slika 2.28: Primer naravnega, uravnoteˇzenega, dvosmernega zlivanja Navadno zlivanje (nadaljevanje) 53 54 55 56 57 58 59 60 BEGIN k :=1; REPEAT InitLoop; Distribute; (∗ porazdelitev ∗) l :=0; Merge(aux1 ,aux2 ,io) ; (∗ zlivanje ∗) k :=2∗k UNTIL l =1; END MergeSort; Navadno zlivanje (konec) 2.3.2 Naravno, uravnoteˇ zeno, dvosmerno zlivanje V tem razdelku bomo opisali proceduro, ki vsebuje dve od prej omenjenih treh moˇznih izboljˇsav navadnega zlivanja: odpravili bomo poseben korak porazdelitve in namesto podzaporedij dolˇzine k, zlivali ˇcete. Zlivanje ˇcet pomeni, da moramo namesto procedure CopyKseq (vrstice 12-19 v programu 2.2) uporabljati proceduro SimpleCopyRun: PROCEDURE SimpleCopyRun( inf ,outf :PSeq; VAR pI :I .PItem; VAR lastK :LONGINT ); (∗ Prepis ene ˇcete; privzetek: na zaˇcetku p # NIL ∗) BEGIN REPEAT Wr (pI ,outf ); lastK :=pI .k ; pI :=Rd (inf ) UNTIL (pI =NIL) OR (lastK >pI .k ) END SimpleCopyRun; (2.23) 58 POGLAVJE 2. UREJANJE ki pa jo uporabljamo tako, da pred uporabo programa priredimo procedurni spremenljivki CopyRun (na sliki 2.26) proceduro SimpleCopyRun (posreden doseg do procedure SimpleCopyRun uporabljamo zato, ker bomo kasneje potrebovali dve razliˇcici procedure CopyRun in jih bo primerno izbirati tako, da enkrat spremenljivki CopyRun priredimo eno proceduro, drugiˇc pa drugo). Program za naravno, uravnoteˇzeno, dvosmerno zlivanje je prikazan kot program 2.3, pri ˇcemer podrejena procedura Distribute(t1, t2, t3, t4) (vrstice 32-47) zlije in porazdeli ˇcete s trakov t1 in t2 na t3 in t4, kot rezultat pa vrne ˇstevilo zapisanih ˇcet. Program 2.3: Naravno, uravnoteˇ zeno, dvosmerno zlivanje 1 3 3 4 6 6 7 8 9 10 11 12 14 14 15 16 17 18 19 20 22 22 23 24 25 26 27 28 29 30 32 32 33 35 35 PROCEDURE NatBal2MergeSort∗(io,aux1 ,aux2 ,aux3 ,aux4 :PSeq):PSeq; PROCEDURE InitProg; VAR p:I .PItem;last:LONGINT ; BEGIN InitRd (io); p:=Rd (io); InitWr (aux1 ); InitWr (aux2 ); (∗ zaˇcetna porazdelitev ∗) REPEAT CopyRun(io,aux1 ,p,last) ; IF p#NIL THEN CopyRun(io,aux2 ,p,last) END UNTIL p=NIL END InitProg; PROCEDURE MergeRun(in1 ,in2 ,out:PSeq;VAR p1 ,p2 :I .PItem):BOOLEAN ; (∗ Zlivanje po ene ˇcete z in1 in in2 na out ∗) VAR last1 ,last2 :LONGINT ; BEGIN last1 :=MIN (LONGINT );last2 :=last1 ; WHILE (p1 #NIL)&(last1 <=p1 .k )&(p2 #NIL)&(last2 <=p2 .k ) DO IF p1 .k <p2 .k THEN Wr (p1 ,out);last1 :=p1 .k ;p1 :=Rd (in1 ) ELSE Wr (p2 ,out);last2 :=p2 .k ;p2 :=Rd (in2 ) END END; IF (p1 #NIL)&(last1 <=p1 .k ) THEN CopyRun(in1 ,out,p1 ,last1 ) END; IF (p2 #NIL)&(last2 <=p2 .k ) THEN CopyRun(in2 ,out,p2 ,last2 ) END; RETURN (p1 #NIL) OR (p2 #NIL) END MergeRun; PROCEDURE Distribute(aux1 ,aux2 ,aux3 ,aux4 :PSeq):LONGINT ; VAR l :LONGINT ; p1 ,p2 :I .PItem; BEGIN l :=0; se nadaljuje 2.3. ZUNANJE UREJANJE 59 Naravno, uravnoteˇ zeno, dvosmerno zlivanje (nadaljevanje) 36 37 38 39 40 41 42 43 45 45 46 47 49 49 51 51 52 54 54 55 InitRd (aux1 );InitRd (aux2 ); InitWr (aux3 ); InitWr (aux4 ); p1 :=Rd (aux1 ); p2 :=Rd (aux2 ); LOOP INC (l ); IF MergeRun(aux1 ,aux2 ,aux3 ,p1 ,p2 ) THEN INC (l ); IF ∼MergeRun(aux1 ,aux2 ,aux4 ,p1 ,p2 ) THEN EXIT END ELSE EXIT END END; RETURN l END Distribute; BEGIN InitProg; LOOP IF Distribute(aux1 ,aux2 ,aux3 ,aux4 )=1 THEN RETURN aux3 ELSIF Distribute(aux3 ,aux4 ,aux1 ,aux2 )=1 THEN RETURN aux1 END END END NatBal2MergeSort; Naravno, uravnoteˇ zeno, dvosmerno zlivanje (konec) Znaˇcilnosti prikazanih procedur so naslednje: 1. pri naravnem zlivanju je potrebno uporabljati spremenljivko last oziroma last1 in last2 (vrstice 4, 16) za to, da doloˇcimo konec ˇcete; 2. procedura MergeRun (vrstice 14–30) ima ˇse vedno znaˇcilno zgradbo sestavljeno iz treh zank (ˇceprav sta dve zanki pravzaprav klica posebne procedure CopyRun); 3. preusmerjanje izhoda med dvema trakovoma opravljamo z loˇcenima klicema procedure MergeRun znotraj procedure Distribute (vrstici 40, 42). 2.3.3 Naravno, uravnoteˇ zeno, veˇ csmerno zlivanje Program je prikazan kot program 2.4. Njegova poglavitna zanimivost pa je procedura, ki usteza proceduri MergeRun iz programa 2.2. Slednja namreˇc vsebuje po eno zanko za vsako neprazno mnoˇzico vhodnih trakov. V primeru, ko je ˇstevilo vhodnih trakov enako 2, je takih podmnoˇzic 3, v sploˇsnem pa 2n − 1, kjer je n ˇstevilo vhodnih trakov. Oˇcitno je torej, da moramo poiskati drugaˇcno obliko procedure MergeRun za primer, ko je n veˇcje od 2. Reˇsitev je prikazana na sliki 2.29: trakovi (oziroma kazalci nanje) so urejeni v tabelo f , ki sta ji pridruˇzeni dve spremenljivki, ner, ki je enaka ˇstevilu trakov, na katerih trenutna ˇceta ˇse ni zakljuˇcena, in nes, ki je enaka ˇstevilu trakov, ki 60 POGLAVJE 2. UREJANJE ner (a) f nes f [x] ner nes (b) f Slika 2.29: Postopek ob zakljuˇcku ˇcete oziroma traku ˇ se tabela zaˇcne z indeksom 1, sta to obenem indeksa ˇse niso do konca prebrani. Ce zadnjega traku, ki ima ˇse nedokonˇcano ˇceto, ter zadnjega neprebranega traku. Denimo sedaj, da se je na nekem traku ˇceta konˇcala (na sliki 2.29 je to trak z indeksom x). V takem primeru zamenjamo f [x] in f [ner] ter zmanjˇsamo ner za 1. Na ta naˇcin se med zlivanjem ene ˇcete vrednost ner postopno zmanjˇsuje, dokler ne postane enaka 0. Takrat je postopek zlivanja ene ˇcete konˇcan. Podobno ravnamo s spremenljivko nes. Proceduri Distribute v programu 2.3 sedaj ustreza zanka vrste REPEAT v proceduri MergeRun, ki se konˇca, ko postane spremenljivka ner enaka 0 (vrstice 49– 77 v programu 2.4). Program 2.4: Naravno, uravnoteˇ zeno, veˇ csmerno zlivanje 1 3 3 4 5 6 7 8 9 10 11 12 14 14 15 16 17 18 19 PROCEDURE NatBalMultiMergeSort∗(in:PSeq;VAR f :ARRAY OF PSeq):PSeq; VAR h, (∗ ˇstevilo vhodov in izhodov ∗) dest, (∗ indeks izhoda, na katerega piˇsemo ∗) nes, (∗ ˇstevilo nepraznih vhodov ∗) l :LONGINT ; (∗ ˇstevilo ˇcet, zapisanih v tekoˇci iteraciji ∗) pa:PAPItem;(∗ tabela kazalcev na vhodne elemente ∗) pl :PAL; (∗ tabela vrednosti kljuˇcev, ki so bili nazadnje zapisani na izhode ∗) ppa:I .PItem; ppl :LONGINT ; PROCEDURE InitProg; VAR i:LONGINT ;p:I .PItem;last:LONGINT ; BEGIN InitRd (in); p:=Rd (in); h:=LEN (f ) DIV 2; NEW (pa,h);NEW (pl ,h); se nadaljuje 61 2.3. ZUNANJE UREJANJE Naravno, uravnoteˇ zeno, veˇ csmerno zlivanje (nadaljevanje) 20 21 22 23 24 25 26 27 29 30 30 32 32 33 34 35 36 37 38 39 40 41 42 43 45 46 46 48 48 49 50 51 52 53 54 55 56 58 58 59 60 61 62 63 64 (∗ zaˇcetna porazdelitev ∗) FOR i:=1 TO h DO InitWr (f [i−1]) END; i:=h; REPEAT IF i=h THEN i:=1 ELSE INC (i) END; CopyRun(in,f [i−1],p,last) UNTIL p=NIL END InitProg; PROCEDURE InitLoop; VAR i:LONGINT ;p:PSeq; BEGIN l :=0; FOR i:=1 TO h DO InitRd (f [i−1]); InitWr (f [h+i−1]) END; i:=1;nes:=h; (∗ toˇcno ˇstevilo nepraznih trakov ∗) WHILE i<=nes DO pa[i−1]:=Rd (f [i−1]); IF pa[i−1]#NIL THEN INC (i) ELSE p:=f [i−1]; f [i−1]:=f [nes−1]; f [nes−1]:=p; DEC (nes) END END END InitLoop; PROCEDURE MergeRun ; VAR i, minx , ner (∗ ˇstevilo nedokonˇcanih vhodnih ˇcet ∗) :LONGINT ; p:PSeq; BEGIN ner :=nes; IF dest=2∗h THEN dest:=h+1 ELSE INC (dest) END; (∗ zapiˇsemo eno ˇceto ∗) REPEAT minx :=1; FOR i:=2 TO ner DO IF pa[i−1].k <pa[minx −1].k THEN minx :=i END END; Wr (pa[minx −1],f [dest−1]); pl [minx −1]:=pa[minx −1].k ; pa[minx −1]:=Rd (f [minx −1]); se nadaljuje 62 POGLAVJE 2. UREJANJE Naravno, uravnoteˇ zeno, veˇ csmerno zlivanje (nadaljevanje) 65 66 67 68 69 70 71 72 73 74 75 76 77 79 79 80 82 82 83 85 85 86 88 88 90 90 91 92 94 94 95 97 97 98 99 IF (pa[minx −1]=NIL) OR (pl [minx −1]>pa[minx −1].k ) THEN (∗ konec ˇcete ∗) p:=f [minx −1]; f [minx −1]:=f [ner −1]; f [ner −1]:=p; ppa:=pa[minx −1];pa[minx −1]:=pa[ner −1];pa[ner −1]:=ppa; ppl :=pl [minx −1];pl [minx −1]:=pl [ner −1];pl [ner −1]:=ppl ; DEC (ner ); IF pa[ner ]=NIL THEN(∗ konec traku ∗) p:=f [ner ]; f [ner ]:=f [nes−1]; f [nes−1]:=p; ppa:=pa[ner ]; pa[ner ]:=pa[nes−1];pa[nes−1]:=ppa; ppl :=pl [ner ]; pl [ner ]:=pl [nes−1];pl [nes−1]:=ppl ; DEC (nes) END END UNTIL ner =0 END MergeRun; PROCEDURE SwitchTapes; VAR i:LONGINT ;p:PSeq; BEGIN FOR i:=1 TO h DO p:=f [i−1]; f [i−1]:=f [h+i−1]; f [h+i−1]:=p END END SwitchTapes; BEGIN InitProg; dest:=h; REPEAT InitLoop; REPEAT INC (l ); MergeRun UNTIL nes=0; SwitchTapes UNTIL l =1 ; RETURN f [h] END NatBalMultiMergeSort; Naravno, uravnoteˇ zeno, veˇ csmerno zlivanje (konec) 2.3.4 Polifazno urejanje Do sedaj smo prvotni postopek navadnega zlivanja izboljˇsali na tri naˇcine: odpravili smo poseben korak porazdelitve, upoˇstevali obstojeˇco urejenost v vhodnem zaporedju in omogoˇcili uporabo veˇc vhodnih trakov. Ali je mogoˇce postopek ˇse izboljˇsati? Pri uravnoteˇzenem, naravnem veˇcsmernem zlivanju opazimo, da je izkoriˇsˇcenost izhodnih trakov nizka. Namreˇc, od n izhodnih trakov je naenkrat aktiven le eden, ostali miru- 63 2.3. ZUNANJE UREJANJE l\i 1 1 7 2 3 4 5 3 1 0 1 2 6 3 4 4 0 b 0 2 1 0 c a 2 0 1 0 l\i 0 1 2 3 4 4 2 1 0 ˇ (a) Stevilo ˇ cet na posameznih trakovih (i) v trenutkih (l), ko se eden od trakov izprazni 1 1 1 2 4 7 2 0 1 2 3 6 3 0 1 1 2 4 4 0 0 0 0 0 (b) Isti primer potem, ko smo vrstice zasukali tako, da je trak z najveˇ cˇ cetami na prvem mestu in jih uredili nasprotno kot v 2.30a Slika 2.30: Primer polifaznega urejanja s ˇstirimi trakovi jejo in ˇcakajo, da na njih pride vrsta. Zdi se nam, da bi bil postopek najhitrejˇsi, ˇce bi lahko od celotnega ˇstevila n trakov uporabljali n − 1 kot vhodne trakove in le enega kot izhodnega. Tako zasnovano urejanje, ki mu pravimo polifazno urejanje, bomo prikazali na primeru na sliki 2.30a, kjer uporabljamo ˇstiri trakove. Pri trakovih prikazujemo le ˇstevilo ˇcet na posameznem traku, ker identiteta posameznega zapisa v tem primeru ni pomembna. Osnovna ideja je, da vedno uporabljamo tri trakove kot vhodne in enega kot izhodnega. Ko se neki vhodni trak izprazni, postane takoj izhodni trak in dotedanji izhodni trak postane vhodni. V prvi vrstici na sliki 2.30a smo trakove uredili po padajoˇcem ˇstevilu ˇcet. Oˇcitno lahko zlivamo na trak ˇstevilka 4 do trenutka, ko se trak ˇstevilka 3 izprazni (ko ˇstevilo ˇcet na njem doseˇze vrednost 0). Do tega trenutka smo s trakov 1-3 prebrali po 4 ˇcete in prav toliko zapisali na trak ˇstevilka 4. V vrstici 2 se to vidi po tem, da smo od vrednosti v stolpcih 1-3 odˇsteli 4 in prav toliko dodali stolpcu 4. Sedaj postane trak 3 izhodni trak in postopek nadaljujemo. V naslednji fazi na trak 3 zapiˇsemo 2 ˇceti, po ˇcemer se izprazni trak 2 in takoj postane izhodni. V naslednji fazi zapiˇsemo na trak 2 1 ˇceto, kar ima za posledico, da se trak 1 izprazni, na vseh ostalih trakovih pa je prisotna ena sama ˇceta. Konˇcno zlijemo s trakov 2–4 na trak 1 po eno ˇceto in na traku 1 dobimo eno samo ˇceto. Iz opisanega sledi, da je osnovna relacija med ˇstevili ˇcet na posameznih vhodnih trakovih in izhodnem traku, a=b+c (2.24) [gl. sliko 2.30a]. Ko analiziramo polifazno urejanje, ugotovimo, da je bistveno odvisno od konkreˇ bi tnih ˇstevil, ki jih izberemo kot zaˇcetna ˇstevila ˇcet na posameznih trakovih. Ce ta ˇstevila izbrali nakljuˇcno, postopka nikakor ne bi uspeˇsno konˇcali. Kot primer je potrebno le pomisliti na izbiro, ko je na vseh trakovih enako ˇstevilo ˇcet. V takem primeru bi po zlivanju dobili en trak z nekim ˇstevilom (daljˇsih) ˇcet in n − 1 trakov ˇ bi hoteli postopek nadaljevati, bi morali ponoviti zaˇcetno pos ˇstevilom ˇcet, 0. Ce ˇ hoˇcemo izpeljati pravilo, razdeljevanje ˇcet, kar pa je seveda ˇcasovno potratno. Ce 64 POGLAVJE 2. UREJANJE po katerem se ravnajo ˇstevila ˇcet na posameznih trakovih, zato da postopek poteka najuˇcinkoviteje, moramo tabelo na sliki 2.30a nekoliko preurediti: vsako naslednjo vrstico kroˇzno zasukamo za eno mesto v desno in na ta naˇcin vse niˇcelne elemente poravnamo v en stolpec, nato pa ˇse vrstice preuredimo v nasprotnem vrstnem redu ˇ ˇstevilo ˇcet na traku i v vrstici kot na sliki 2.30a. Na ta naˇcin dobimo sliko 2.30b. Ce l l na sliki 2.30b zaznamujemo z ai , se relacija (2.24) spremeni v al+1 = ali+1 + al1 = al1 + ali+1 . i (2.25) Na podlagi pravkar zapisane rekurenˇcne relacije in zaˇcetnega podatka a01 = 1, a0i = 0 pri i > 1 je moˇzno za poljubno ˇstevilo trakov n sestaviti tabelo, ki je podobna tabeli na sliki 2.30b. Shema celotnega postopka polifaznega urejanja je, da nek vhodni trak z elementi, ki jih ˇzelimo urediti, porazdelimo na n−1 trakov (kjer je n ˇstevilo trakov), in sicer tako kot narekuje vrstica l tabele (ki je podobna tabeli 2.30b pri izbranem n), pri ˇcemer l izberemo kot indeks prve vrstice, v kateri je vsota elementov veˇcja ali enaka ˇstevilu vhodnih ˇcet. Preden pa se zaˇcnemo ukvarjati s podrobnostmi realizacije algoritma je ˇ relacijo (2.25) uporabimo veˇckrat, dobimo dobro, da dobimo obˇcutek za ˇstevila ali . Ce za al1 naslednjo relacijo: al1 = al−1 + al−2 + . . . + al−n+1 , 1 1 1 (2.26) ki pa moˇcno spominja na definicijo dobro znanih Fibonaccijevih ˇstevil fl = fl−1 + fl−2 (2.27) (koliˇcina fl ustreza al1 ). Zato bomo ˇstevilom al1 v enaˇcbi (2.26) rekli Fibonaccijeva ˇstevila reda n − 2 [red Fibonaccijevega ˇstevila je za ena manjˇsi kot ˇstevilo ˇclenov na desni strani (2.26)]. Glede na to, da navadna Fibonaccijeva ˇstevila (reda 1) naraˇsˇcajo “pribliˇzno kot” 2l (gl. nalogo 10), lahko sklepamo, da Fibonaccijeva ˇstevila reda n naraˇsˇcajo “pribliˇzno kot” (n + 1)l . Preden pa pravkar opisano idejo o zgradbi programa za polifazno urejanje spremenimo v delujoˇci program, je potrebno razreˇsiti dva drobna problema: 1. kako doloˇciti indeks vrstice l v tabeli 2.30b, ki ga je potrebno uporabiti in 2. glede na to, da smo pravkar ugotovili, da je postopek polifaznega urejanja kritiˇcno odvisen od ˇstevila ˇcet na posameznih trakovih, kaj storiti, ko vsota ˇstevil v vrstici l ni enaka ˇstevilu vhodnih ˇcet. Na prvi pogled bi kazalo, da je za reˇsitev problema 1. zgoraj potrebno prebrati ves vhod in preˇsteti ˇcete. Vendar se izkaˇze, da se temu lahko izognemo. Zaˇcnemo s privzetkom, da je ˇzelena vrednost l enaka 1, nato preberemo ˇstevilo ˇcet, ki je enako vsoti ˇstevil v vrstici l = 1 (na primer, v tabeli na sliki 2.30b je vsota ˇstevil v vrstici ˇ je po koncu te faze vhodni trak l = 1 enaka 3) in jih porazdelimo po trakovih. Ce prazen, smo postopek konˇcali, sicer spremenimo privzetek tako, da poveˇcamo l za 1 in v novi fazi postopka preberemo ˇstevilo ˇcet, ki je enako razliki med vsotama ˇstevil v vrsticah l+1 in l, ter te ˇcete porazdelimo skladno z vrednostmi v posameznih stolpcih. Tako nadaljujemo, dokler na koncu neke faze trak ni prazen. 2.3. ZUNANJE UREJANJE 65 Reˇsitev problema 2. zgoraj je tudi razmeroma preprosta: ko se vhodni trak izprazni preden smo prebrali ˇstevilo ˇcet, ki je enako vsoti ˇstevil v neki vrstici, zapiˇsemo mankajoˇce ˇcete v obliki “navideznih” ˇcet, ki so prisotne samo zato, da se postopek izide. Takoj pa, ko smo sprejeli odloˇcitev o navideznih ˇcetah, se postavi vpraˇsanje, kako jih porazdeliti in 8 seveda, kako jih predstavljati. Zadevo bomo prika7 6 zali na primeru s slike 2.30b, ko je ˇstevilo trakov 4. 5 Denimo, da izvajamo postopek porazdeljevanja in 4 smo konˇcali porazdeljevanje ˇcet, ki ustrezajo l = 3, 3 ne da bi vhodni trak izpraznili. Nato poveˇcamo l 2 1 na 4 in nadaljujemo z branjem. Po treh ˇcetah ugo0 tovimo, da je vhodni trak prazen. Kako porazdeliti 0 1 2 3 4 5 prebrane tri ˇcete in preostalih 5 navideznih ˇcet? Ali je primerno vse tri resniˇcne ˇcete zapisati na trak 1 Slika 2.31: Porazdelitev praznih ali pa kako drugaˇce? Izkaˇze se, da je koristen nasle- ˇcet dnji premislek: postopek je najuˇcinkovitejˇsi takrat, ko se dolˇzina ˇcet pri zlivanju najhitreje poveˇcuje. To pa doseˇzemo takrat, ko pri zlivanju ne zlivamo navideznih in resniˇcnih ˇcet. Zato navidezne ˇcete ˇcimbolj enakomerno porazdelimo po trakovih in jih kasneje zlivamo med seboj, preostale, resniˇcne ˇcete pa porazdelimo tako, kot predpisuje tabela na sliki 2.30b. Postopek enakomernega porazdeljevanja navideznih ˇcet prikazuje slika 2.31: viˇsina grafa pri traku i je ˇstevilo ˇcet, ki bi jih morali zapisati na trak i, da doseˇzemo ˇstevilo ali (pri izbranem l). Enakomerno porazdeljenost doseˇzemo, ˇce navidezne ˇcete (belo obarvano) pustimo “na dnu” grafa, medtem ko resniˇcne ˇcete (sivo) pustimo na vrhu. V programu postopamo tako, da zaˇcnemo s celotno povrˇsino grafa pobarvano belo, nato pa se sprehajamo po vodoravnih trakovih povrˇsine in vsakiˇc, ko preberemo neko ˇceto, spremenimo eno enoto povrˇsine v sivo (zaradi tega se opisan naˇcin porazˇ se sredi postopka deljevanja ˇcet imenuje “vodoravna strategija porazdeljevanja”). Ce vhodni trak izprazni, predstavlja preostala bela povrˇsina navidezne ˇcete. Iz opisa postopka sledi, da so navidezne ˇcete porazdeljene po trakovih pribliˇzno enakomerno, kar pomeni, da v postopku zlivanja lahko zlivamo bodisi samo navidezne ˇcete ali pa samo resniˇcne ˇcete. Obenem se tudi ponuja enostavna reˇsitev problema predstavitve navideznih ˇcet. Namreˇc pri porazdeljevanju potrebujemo ˇstevec, katerega vrednost je ˇstevilo manjkajoˇcih ˇcet do ˇstevila ali . Ko se vhodni trak izprazni, pa je prav to ˇstevilo potrebno ˇstevilo navideznih ˇcet. Torej: navidezne ˇcete predstavljamo z vrednostjo nekega ˇstevca, ki je prirejen traku. 66 POGLAVJE 2. UREJANJE Program 2.5: Polifazno urejanje 1 2 3 4 5 6 7 8 9 10 11 13 13 14 16 16 18 18 19 20 21 22 23 24 25 26 28 28 29 30 31 32 33 34 35 36 37 38 40 40 41 43 43 44 PROCEDURE PolyphaseSort∗(in:PSeq;VAR f :ARRAY OF PSeq):PSeq; VAR j , (∗ indeks tekoˇcega traku pri porazdeljevanju ∗) k , (∗ ˇstevilo nepraznih ˇcet ∗) l , (∗ raven (nivo) porazdeljevanja ∗) n, (∗ ˇstevilo trakov ∗) z :LONGINT ;(∗ ˇstevilo ˇcet, ki jih zlivamo na tekoˇci ravni ∗) pa:PAPItem; (∗ “okno” na vhod ∗) a, (∗ ˇstevila ˇcet na trakovih na ravni l ∗) d , (∗ ˇstevila praznih ˇcet na trakovih na ravni l ∗) last, (∗ zadnji kljuˇc, ki je bil zapisan na posamezen trak ∗) t:PAL; (∗ indeksi trakov z nepraznimi ˇcetami ∗) PROCEDURE Select; VAR i,z :LONGINT ; BEGIN IF d [j ] < d [j +1] THEN INC (j ) ELSE IF d [j ]=0 THEN INC (l );z :=a[1]; FOR i:=1 TO n−1 DO d [i]:=z +a[i+1]−a[i]; a[i]:=z +a[i+1] END; END; j :=1 END; DEC (d [j ]) END Select; PROCEDURE InitProg; VAR i:LONGINT ; BEGIN n:=LEN (f ); NEW (a,n+1);NEW (d ,n+1);NEW (last,n+1); NEW (pa,n+1);NEW (t,n+1); FOR i:=1 TO n−1 DO a[i]:=1;d [i]:=1;InitWr (f [i−1]) END ; l :=1;j :=1;a[n]:=0;d [n]:=0 END InitProg; PROCEDURE Distribute; VAR p:I .PItem;i:LONGINT ; BEGIN SpecInit; (∗ pri navadnem polifaznem urejanju, brez uˇcinka ∗) InitRd (in);p:=Rd (in); se nadaljuje 67 2.3. ZUNANJE UREJANJE Polifazno urejanje (nadaljevanje) 45 46 47 48 49 50 51 53 54 54 55 56 58 58 59 61 61 62 63 64 66 67 67 69 69 70 72 72 73 74 75 76 77 78 79 80 81 82 83 85 85 86 88 REPEAT Select; CopyRun(in,f [j −1],p,last[j ]) UNTIL (p=NIL) OR (j =n−1); WHILE p#NIL DO Select; IF last[j ]>p.k THEN CopyRun(in,f [j −1],p,last[j ]) ELSE CopyRun(in,f [j −1],p,last[j ]); IF p#NIL THEN CopyRun(in,f [j −1],p,last[j ]) ELSE INC (d [j ]) END END END; FOR i:=1 TO n−1 DO InitRd (f [i−1]); pa[i]:=Rd (f [i−1]) END END Distribute; PROCEDURE InitInnerLoop; VAR i:LONGINT ; BEGIN k :=0; FOR i:=1 TO n−1 DO IF d [i]>0 THEN DEC (d [i]) ELSE INC (k ); t[k ]:=i END END END InitInnerLoop; PROCEDURE MergeNonEmptyRuns; VAR i,mx :LONGINT ; BEGIN REPEAT i:=1; mx :=1 ; WHILE i < k DO INC (i); IF pa[t[i]].k < pa[t[mx ]].k THEN mx :=i END END; (∗ t[mx ] vsebuje najmanjˇsi element; zapiˇsemo ga na f [n−1] ∗) Wr (pa[t[mx ]],f [n−1]);last[t[mx ]]:=pa[t[mx ]].k ; pa[t[mx ]]:=Rd (f [t[mx ]−1]); IF (pa[t[mx ]]=NIL) OR ( last[t[mx ]] > pa[t[mx ]].k ) THEN (∗ konec ˇcete na tem traku ∗) t[mx ]:=t[k ]; DEC (k ) END UNTIL k =0 END MergeNonEmptyRuns; se nadaljuje 68 POGLAVJE 2. UREJANJE Polifazno urejanje (nadaljevanje) 88 89 91 91 92 93 94 95 96 97 98 99 100 101 102 104 104 105 106 107 108 109 110 111 112 113 115 115 116 117 118 119 PROCEDURE TermLoop; VAR i,dn:LONGINT ;ff :PSeq;p:I .PItem; BEGIN (∗ Zasuk trakov ∗) InitRd (f [t[n−1]]);pa[n]:=Rd (f [n−1]); ff :=f [n−1]; dn:=d [n]; z :=a[n−1];p:=pa[n]; FOR i:=n TO 2 BY −1 DO f [i−1]:=f [i−2]; d [i]:=d [i−1]; a[i]:=a[i−1]−z ; pa[i]:=pa[i−1] END; f [0]:=ff ; d [1]:=dn; a[1]:=z ; pa[1]:=p; DEC (l ) END TermLoop; BEGIN InitProg; IF p # NIL THEN Distribute; REPEAT (∗ f [0]. . . f [n−2] zlivamo na f [n−1] ∗) z :=a[n−1]; d [n]:=0; InitWr (f [n−1]); REPEAT (∗ z ˇcet zapiˇsemo na trak f [n−1] ∗) InitInnerLoop; IF k =0 THEN INC (d [n]) ELSE MergeNonEmptyRuns END; z :=z −1 UNTIL z =0; TermLoop UNTIL l =0; END; (∗ Izhodno zaporedje je na f [0] ∗) RETURN f [0] END PolyphaseSort; Polifazno urejanje (konec) Celotni program polifaznega urejanja je prikazan kot program 2.5, opisani postopek zaˇcetnega porazdeljevanja ˇcet realizira procedura Distribute (vrstice 40–56), “vodoravno strategijo” pa procedura Select, ki izbira naslednji izhodni trak j (vrstice 13–26). Slednjo proceduro aktiviramo pred vsakim prepisovanjem ˇcete z vhodnega traku na izhodni trak. Koliˇcina d[j] je preostalo ˇstevilo navideznih ˇcet na traku j in ji ob spremembi vrednosti l priredimo zaˇcetno vrednost alj − ajl−1 . Pri porazdeljevanju ˇcet opazimo ˇse neko zanimivost: kadarkoli porazdeljujemo ˇcete na veˇc izhodnih trakov, obstaja moˇznost, da se zadnja ˇceta na nekem izhodnem traku zlije s tekoˇco ˇceto v eno samo ˇceto. Pri dvo in veˇcsmernem zlivanju nas to ne skrbi preveˇc, ker je postopek tako zasnovan, da ni obˇcutljiv na razlike v ˇstevilu ˇcet na posameznih trakovih. Pri 2.3. ZUNANJE UREJANJE 69 polifaznem urejanju pa je postopek kritiˇcno odvisen od ˇstevila ˇcet na nekem traku in zato si ne moremo privoˇsˇciti razlike med dejanskim ˇstevilom in ali . Zato v zanki v vrsticah 39–46procedure Distribute, programa 2.5 vedno preverimo, ali se je stara ˇceta na izhodu zlila s trenutno ˇceto in v tem primeru z vhoda prepiˇsemo dodatno ˇceto. Pred to zanko opazimo posebno zanko v vrsticah 45–46, ki opravi zaˇcetno prirejanje spremenljivki last, katere vrednost je nazadnje zapisani kljuˇc na nekem traku. Sicer pa ima osnovni blok programa 2.5 dokaj preprosto zgradbo: po zaˇcetnih prirejanjih in porazdeljevanju ˇcet se izvajata vgnezdeni zanki v vrsticah 107–115. Zunanja pravzaprav “odvije” postopek porazdeljevanja in pri vsakem izvajanju jedra zanke zmanjˇsa l za ena, notranja pa pri vsakem izvajanju jedra zanke na izhodni trak zapiˇse eno ˇceto. Procedura TermLoop natanˇcno izvaja postopek, ki smo ga opisali v zvezi s tabelo na sliki 2.30b: stolpce tabel d, a in f zasuka za eno mesto v desno ter zmanjˇsa l. 2.3.5 Polifazno urejanje s predurejanjem Pri polifaznem urejanju imamo ˇse eno moˇznost za pohitritev postopka: trajanje postopka je odvisno od potrebnega ˇstevila l, ki pa je odvisno od zaˇcetnega ˇstevila ˇcet. ˇ to ˇstevilo zmanjˇsamo, je moˇzno postopek pohitriti. Ena moˇznost za to se nam Ce ponuja, ˇce ima raˇcunalnik neizkoriˇsˇcen notranji pomnilnik. V tem primeru lahko uporabimo neko metodo notranjega urejanja (tabel) za to, da poveˇcamo dolˇzino ˇcet (in na ta naˇcin zmanjˇsamo njihovo ˇstevilo). Postopek bomo izpeljali med zaˇcetnim porazdeljevanjem ˇcet preprosto tako, da bomo spremenljivki CopyRun priredili novo proceduro, ki podaljˇsa ˇcete tako, da jih nekoliko preuredi, uporabljajoˇc v ta namen kopico, pri ˇcemer je urejenost kopice sedaj nasprotna od urejenosti kopice iz razdelka 2.2.6: sedaj je element pri korenu drevesa manjˇsi ali enak od elementov v poddrevesih. Delovanje procedure prikaˇzemo na primeru, ko imamo na voljo kopico velikosti 12. Denimo, da imamo na vhodnem ˇ nato 12 vhodnih podatkov traku zaporedje kljuˇcev, ki so prikazani na sliki 2.32a. Ce preberemo v prazna mesta tabele, ki predstavlja kopico, in nato tabelo uredimo v kopico (pozor na spremenjeno urejenost kopice), dobimo kopico, ki je prikazana na sliki 2.32b. Nato zapiˇsemo koren kopice na izhodni trak (ˇcigar indeks dobimo s klicem procedure Select), preberemo naslednji element z vhoda in nadaljujemo v odvisnosti od razmerja med kljuˇcem naslednjega vhodnega elementa in kljuˇcem pravkar zapisanega elementa: ˇ je kljuˇc zapisanega elementa manjˇsi ali enak od kljuˇca naslednjega elementa 1. Ce na vhodu, slednji element pripada isti ˇceti kot pravkar zapisani element in ga zapiˇsemo v koren kopice ter sproˇzimo proceduro Sift (kopico popravimo); ˇ je kljuˇc zapisanega elementa veˇcji od kljuˇca naslednjega elementa, pripada 2. Ce naslednji element naslednji ˇceti. V tem primeru kopico zmanjˇsamo za en element tako, da zadnji element zapiˇsemo v koren ter sproˇzimo Sift, naslednji element na vhodu pa zapiˇsemo v prejˇsnji konec kopice; 70 POGLAVJE 2. UREJANJE 55,87,93; 17,21; 4,37,44,45; 19,100; 7,13,22; 2,57,88,101; 11,19,27; 9; 3; 1,25,58,80; 70,110; 29 (meje med ˇcetami so nakazane z znakom ;) (a) Vhodni podatki 17 4 22 19 17 44 87 7 55 19 45 44 37 87 21 100 93 21 45 (b) Kopica pred prvim izpisom na izhod 93 100 55 37 2 (c) Kopica se prviˇ c zmanjˇsa 29 70 58 9 80 25 27 19 1 11 3 2 (d) Kopica v celoti izpisana; druga pripravljena na urejanje Slika 2.32: Polifazno urejanje s predurejanjem 3. Zapisovanje in branje ponovimo. Na sliki 2.32c je prikazano stanje, ko nastopi prvo zmanjˇsanje kopice (po branju elementa 2), na sliki 2.32d pa trenutek, ko smo prvotno kopico izpraznili in je druga kopica pripravljena za urejanje in kasnejˇse izpise. Celotni postopek se nekoliko zaplete, ker je sestavljen iz nekaj faz, med katerimi je obnaˇsanje algoritma razliˇcno. Prvo fazo predstavlja branje elementov iz vhoda v tabelo ter sestavljanje kopice. Nato sledi faza, ko je vhodni trak neprazen. Med to fazo elemente prepisujemo na izhod do trenutka, ko se tekoˇca kopica izprazni, oziroma tekoˇca ˇceta konˇca (ta faza se lahko ponovi veˇckrat). Vsakiˇc, ko se kopica izprazni, moramo ˇse sproˇziti urejanje elementov naslednje ˇcete v kopico. Naslednja faza nastopi, ko se vhod izprazni in na izhodni trak prepiˇsemo ostanek tekoˇce ˇcete, nato pa ˇse elemente zadnje ˇcete prestavimo na zaˇcetek tabele ter jih uredimo v kopico. Zadnjo fazo predstavlja izpis zadnje ˇcete. Teˇzava pri realizaciji opisanega postopka je v tem, da moramo v proceduro CopyRun vgraditi spomin o tem, v kateri fazi je postopek. Najprimerneje je, da novo 71 2.3. ZUNANJE UREJANJE proceduro realiziramo kot tim. samostojno proceduro (angl. coroutine), ki se razlikuje od navadne procedure po tem, da si zapomni svoje stanje in ko se program vanjo vrne, se izvajanje nadaljuje od mesta, kjer jo je program nazadnje zapustil. Program je prikazan kot progam 2.6, sledi pa nekaj pripomb o njegovem delovanju. Program 2.6: Polifazno urejanje s predurejanjem 1 2 4 5 5 7 8 8 10 10 11 12 13 14 15 16 17 18 19 20 22 22 23 24 26 26 27 28 30 30 31 32 33 34 35 36 38 38 39 MODULE HeapPS ; IMPORT I :=IntSort,F :=FileSort; CONST N =8192; TYPE FCrCoroutine∗=PROCEDURE(P :PROCEDURE); VAR Rd ∗:F .FRdItem; Wr ∗:F .FWrItem; InitRd ∗,InitWr ∗:F .FSeqInit; FinalSwitch∗,SwitchCoroutine∗:F .FNoParam; CrCoroutine∗:FCrCoroutine; in,out:F .PSeq; dummy:I .Item; p:I .PItem; last: LONGINT ; Sort:F .FPPSort; PROCEDURE CopyRunCoroutine; (∗ Prepisovanje ˇcet, ki smo jih predhodno podaljˇsali tako, da smo jih spustili skozi kopico; privzetek: vhod je neprazen ∗) VAR m, (∗ velikost ostanka kopice, ko se vhod izprazni ∗) n: LONGINT ;(∗ zaˇcetna velikost kopice ∗) a:F .PAPItem; PROCEDURE Sift(l ,r : LONGINT ); VAR i,j :LONGINT ; x : I .PItem; BEGIN i:=l ; j :=2∗i+1; x :=a[i]; WHILE j < r DO IF a[j ].k > a[j +1].k THEN INC (j ) END; IF x .k <= a[j ].k THEN j :=r +1 (∗ Izhod iz zanke ∗) ELSE a[i]:=a[j ]; i:=j ; j :=2∗i+1 END END; IF (j =r ) & (x .k > a[j ].k ) THEN se nadaljuje 72 POGLAVJE 2. UREJANJE Polifazno urejanje s predurejanjem (nadaljevanje) 40 41 43 43 45 45 46 48 48 49 50 52 52 53 55 55 56 57 58 59 60 61 62 64 64 66 66 67 68 69 70 71 72 73 75 75 76 78 79 79 80 81 82 83 84 a[i]:=a[j ]; a[j ]:=x ELSE a[i]:=x END END Sift; PROCEDURE HeapOrder (n:LONGINT ); VAR l : LONGINT ; BEGIN l :=m DIV 2; WHILE l >= 1 DO DEC (l ) ; Sift(l ,m−1) END; (∗ Kopico uredimo ∗) END HeapOrder ; PROCEDURE MoveAndOrder ; VAR i,lim: LONGINT ; BEGIN (∗ Neurejene elemente prestavimo ∗) IF m>0 THEN IF m > n−m THEN lim:=n−m ELSE lim:=m END; FOR i:=0 TO lim−1 DO a[i]:=a[n−i−1] END END; n:=n−m (∗ velikost zadnje ˇcete ∗) HeapOrder (n) END MoveAndOrder ; PROCEDURE Init; BEGIN NEW (a,N ); n:=0;InitRd (in);p:=Rd (in); REPEAT (∗ Kopico napolnimo ∗) a[n]:=p;INC (n);p:=Rd (in) UNTIL (p=NIL) OR (n=N ); m:=n; HeapOrder (m) END Init; PROCEDURE NonEmptyInput; VAR l : LONGINT ; pitem:I .PItem; BEGIN REPEAT (∗ Zaˇcetek naslednje ˇcete ∗) WHILE (p # NIL) & (m>0) DO Wr (a[0],out);last:=a[0].k ; IF p.k >=last THEN a[0]↑:=p↑; Sift(0,m−1) ELSE DEC (m); a[0]:=a[m]; a[m]:=p END; se nadaljuje 73 2.3. ZUNANJE UREJANJE Polifazno urejanje s predurejanjem (nadaljevanje) 85 86 87 88 89 90 91 92 94 94 95 97 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 113 113 115 115 117 117 118 119 120 121 122 124 124 125 126 127 128 129 p:=Rd (in) END; IF m=0 THEN m:=n; HeapOrder (m); pitem:=p;p:=a[0]; SwitchCoroutine; p:=pitem END UNTIL p=NIL END NonEmptyInput; PROCEDURE NextToLastRun; VAR rm: LONGINT ; pitem:I .PItem; BEGIN rm:=m; (∗ Konec vhoda, pri ˇcemer je morebiti ena ˇceta ostala nedokonˇcana ∗) IF rm > 0 THEN REPEAT (∗ Ostanek ˇcete zapiˇsemo ∗) Wr (a[0],out);last:=a[0].k ; DEC (rm);a[0]:=a[rm];Sift(0,rm−1) UNTIL rm = 0; MoveAndOrder ; pitem:=p;p:=a[0]; SwitchCoroutine; p:=pitem ELSE MoveAndOrder END END NextToLastRun; PROCEDURE LastRun; BEGIN WHILE n > 0 DO Wr (a[0],out);last:=a[0].k ; DEC (n);a[0]:=a[n];Sift(0,n−1) END; p:=NIL END LastRun; BEGIN SwitchCoroutine; Init; NonEmptyInput; NextToLastRun; LastRun; FinalSwitch se nadaljuje 74 POGLAVJE 2. UREJANJE Polifazno urejanje s predurejanjem (nadaljevanje) 130 132 132 133 134 136 136 137 138 139 141 141 142 143 145 145 146 147 148 END CopyRunCoroutine; PROCEDURE CopyRun(inf ,outf :F .PSeq;VAR pI :I .PItem; VAR lastK :LONGINT ); (∗ Prepis ene ˇcete; privzetek: na zaˇcetku p # NIL ∗) BEGIN in:=inf ;out:=outf ;p:=pI ;last:=lastK ; SwitchCoroutine; pI :=p;lastK :=last END CopyRun; PROCEDURE MakeCoroutine; BEGIN CrCoroutine(CopyRunCoroutine) END MakeCoroutine; BEGIN F .CopyRun:=CopyRun; F .SpecInit:=MakeCoroutine; Sort:=F .PolyphaseSort END HeapPS . Polifazno urejanje s predurejanjem (konec) Mehanizem samostojnih procedur smo vgradili v program v obliki procedurne spremenljivke CrCoroutine, ki samostojno proceduro pripravi za izvajanje, ter dveh procedurnih spremenljivk, SwitchCoroutine ter FinalSwitch, ki program prestavita iz ene samostojne procedure v drugo (vrstici 14-15). V naravo procedur, ki so prirejene tem spremenljivkam, se niti ne spuˇsˇcamo, ker to presega okvir dela. Program, ki ga gradimo, je sestavljen iz dveh samostojnih procedur, iz glavnega programa in iz samostojne procedure, ki realizira prepisovanje ˇcet (CopyRunCoroutine). 1. Modul HeapPS inicializiramo tako, da procedurnim spremenljivkam CopyRun in SpecInit iz modula FileSort (slika 2.26) ter Sort is modula HeapPS priredimo vrednosti (vrstice 145–147). Nato je moˇzno polifazno urejanje s predurejanjem sproˇziti s klicem procedurne spremenljvike Sort, ki pripada tipu FPPSort iz modula FileSort (slika 2.26). V tem sestavku se ne spuˇsˇcamo v naravo procedur, ki so prirejene procedurnim spremenljivkam CrCoroutine, SwitchCoroutine ter FinalSwitch; 2. Ob klicu procedurne spremenljvike Sort se najprej izvaja procedura SpecInit (program 2.5, vrstica 35), ki ima za posledico izvajanje procedure MakeCoroutine iz programa 2.6 (vrstice 141–143). Slednja procedura poˇzene samostojno proceduro, tako da pripravi proceduro CopyRunCoroutine za izvajanje in se nato takoj vrne v glavni program; 3. glavni program nadaljuje z izvajanjem, kot smo to opisali v razdelku o polifaznem urejanju, v samostojno proceduro, ki jo sedaj opisujemo, pa se vrne ob 2.4. POVZETEK OSNOVNIH POJMOV 75 klicu CopyRun (vrstice 132–139). Ko pride do tega klica, se opravijo prirejanja spremenljivkam in, out, p in last (vhodni trak, izhodni trak, kazalec na element, ki ga preberemo, in kljuˇc nazadnje prebanega elementa), nato pa se pokliˇce SwitchCoroutine, ki prestavi program iz samostojne procedure, ki jo predstavlja glavni program, v tisto, ki opravi prepisovanje ˇcet; 4. Procedura CopyRunCoroutine se priˇcne s klicem Init (vrstice 64–73), ki dinamiˇcno generira tabelo a za kopico, jo napolni z elementi, ki jih prebere z vhoda (moramo upoˇstevati tudi moˇznost, da se vhod konˇca, preden je cela tabela popisana) ter na koncu sestavi kopico (vrstica 72); 5. Sledi klic procedure NonEmptyInput (vrstice 75–95), katere jedro je zanka, ki se izvaja do trenutka, ko se vhod izprazni (mora pa biti tako zasnovana, da deluje pravilno tudi, ko je vhod ˇze prazen ob vstopu). Zanka v vrsticah 80–86, v svojem jedru zapiˇse koren kopice na izhodni trak, prebere naslednji element z vhoda ter njegov kljuˇc primerja s kljuˇcem pravkar zapisanega elementa (vrstica ˇ se 82). Zanka se zakljuˇci, ko se bodisi vhod ali trenutna kopica izprazni. Ce kopica izprazni, je to znak, da se je trenutna ˇceta konˇcala in je potrebno sestaviti novo kopico in zamenjati samostojno proceduro (vrstica 91); 6. Sledi klic procedure NextToLastRun, ki zapiˇse preostale elemente trenutne kopice na izhod, nato prestavi elemente zadnje ˇcete na zaˇcetek tabele, jih preuredi v kopico ter opravi zamenjavo samostojne procedure (vrstice 107–109); 7. Elemente zadnje ˇcete nato izpiˇse procedura LastRun; 8. Konˇcno se pokliˇce procedure FinalSwitch, ki program dokonˇcno prestavi v osnovno samostojno proceduro. 2.4 Povzetek osnovnih pojmov 1. Naloga urejanja 2. Kriteriji za razvrˇsˇcanje nalog urejanja 3. Operacije, s katerimi ocenjujemo porabo ˇcasa pri urejanju 4. Urejanje tabel in osnovni privzetek glede porabe prostora 5. Navadne metode urejanja. Znaˇcilna zanˇcna invarianta. Ocena porabe ˇcasa 6. Spodnja meja za ˇcas urejanja tabel dolˇzine n. Uporabljeni raˇcunalniˇski model. Mnoˇzica vhodnih podatkov. Drevo sledi 7. Shellovo urejanje 8. Urejanje s kopico in ocenjevanje porabe ˇcasa 9. Urejanje s porazdelitvami. Ocena povpreˇcne in najveˇcje porabe ˇcasa. Ocena porabe prostora 10. Iskanje k-tega elementa ter ocena povpreˇcne in najveˇcje porabe ˇcasa 76 POGLAVJE 2. UREJANJE 11. Navadno zlivanje datotek. Ocena porabe ˇcasa. Moˇzne poti izboljˇsave navadnega zlivanja 12. Naravno zlivanje 13. Osnovni problem veˇcsmernega zlivanja in njegova reˇsitev 14. Polifazno zlivanje; osnovna rekurenˇcna relacija za potrebno zaˇcetno ˇstevilo ˇcet na posameznih takovih; problem, ko celotno ˇstevilo ˇcet ni enako vsoti potrebnih zaˇcetnih ˇstevil ˇcet na posameznih trakovih; problem zlivanja dveh ˇcet v eno; “vodoravna” strategija porazdeljevanja ˇcet; 15. Polifazno zlivanje s predurejanjem. Zakaj predurejanje. Programska reˇsitev, ki uporablja samostojne procedure. 2.5 Naloge 1. Izpeljite oceno za ˇcas, ki ga porabi algoritem na sliki 2.9 pri izvajanju zanke v vrsticah od 14 do 16. 2. Izpeljite oceno za ˇstevilo zamenjav, ki jih opravi algoritem navadnih zamenjav na sliki 2.11. Ocenite najmanjˇse, najveˇcje in povpreˇcno ˇstevilo zamenjav. 3. Dokaˇzite, da je z naborom operacij na sliki 2.13 moˇzno realizirati urejanje poljubne tabele dolˇzine n. (Napotek: izhajajte iz ugotovitve, da je naloga urejanja enakovredna sestavljanju neke permutacije vhodnih podatkov.) 4. Izraˇcunajte porabo ˇcasa pri urejanju s porazdelitvami, ˇce privzamemo, da je poraba ˇcasa za porazdelitev enaka cn in da vsaka porazdelitev tabelo razdeli toˇcno na dve tabeli enakih velikosti (napotek: uporabite izrek 1.3). 5. V razdelku o ˇcasu, ki ga porabi urejanje s porazdelitvami, smo dokazali relacijo (2.18), vendar le za ceno sorazmerno visokega poveˇcanja konstante c. Poiˇsˇcite tak nastavek za vrednost Tave (n), ki ohrani vrednost konstantnega faktorja c pri n ln n (napotek: ˇclenu cn log n priˇstejte nek ˇclen, ki se po integraciji v (6) uniˇci z 41 ). 6. Izpeljava relacije (2.16) ni povsem prepriˇcljiva, ker smo izhajali iz nekoliko nenatanˇcnega privzetka, da se tabela velikosti n vedno razdeli v razmerju k : n − k − 1 pri 0 ≤ k ≤ n − 1. Do iste relacije pridemo z nekoliko spremenjenim opisom porazdeljevanja, ko tabelo razdelimo na tri dele, in sicer na levi del, ki vsebuje i elementov, ki so manjˇsi ali enaki mejnemu elementu, na element, ki je enak mejnemu elementu in na n − i − 1 elementov, ki so veˇcji ali enaki mejnemu elementu. Naloga: sestavite algoritem, ki realizira takˇsno porazdelitev. Kaj menite o njegovi uˇcinkovitosti v primerjavi s porazdelitvenim algoritmom na sliki 2.19? 7. Obravnavali smo vpraˇsanje zmanjˇsevanja porabe prostora pri urejanju s porazdelitvami. Prikazali smo reˇsitev, ki uporablja poseben sklad za shranjevanje novih zahtev po porazdelitvi. Pokaˇzite, da je moˇzno tudi prvotno proceduro Sort na sliki 2.20 spremeniti v obliko, ko ne pride do pretirane porabe prostora. 8. Za neko metodo urejanja pravimo, da je stabilna, v primeru, ko ne more priti do zamenjave medsebojnega poloˇzaja dveh zapisov z istim kljuˇcem. Za vse metode notranjega urejanja, ki smo jih predstavili (navadno vstavljanje, dvojiˇsko vstavljanje, navadno izbiranje, navadne zamenjave, izmeniˇcne zamenjave, Shellovo urejanje, urejanje s kopico 77 2.5. NALOGE i1 j1 i2 j2 Slika 2.33: Urejanje tabel z uravnoteˇzenim zlivanjem in urejanje s porazdelitvami), ugotovite, katere so stabilne in podajte utemeljitev vaˇse sodbe. 9. Shemo naravnega, uravnoteˇzenega zlivanja lahko uporabimo tudi za urejanje tabel, kot to prikazuje sl. 2.33: dve vhodni tabeli sta prikazani na levi, pri ˇcemer sta i1 in j1 indeksa trenutnih elementov vhodnih ˇcet, i2 in j2 pa indeksa elementov izhodnih ˇcet. i1 in j1 zlivamo v i2 , naslednjo ˇceto pa na j2 . (a) Sestavite podroben program za opisani postopek; (b) zakaj se ta postopek ni uveljavil v praksi, ˇceprav je izredno hiter? 10. Na podlagi rekurenˇcne relacije 2.27 izpeljite velikost Fibonaccijevih stevil prvega reda (napotek: uporabite nastavek fi = cxi ). 78 POGLAVJE 2. UREJANJE Poglavje 3 ALGORITMI Z REKURZIVNIM RAZCEPOM 3.1 Uvod Algoritme, ki uporabljajo rekurzivni razcep prvotnega problema na manjˇse podprobleme, smo omenili ˇze v uvodnem poglavju (gl. razdelek 1.4.2), kasneje pa smo se z njimi sreˇcali tudi pri urejanju tabel (metoda urejanja s porazdelitvami, razdelek 2.2.7). Sedaj bomo prikazali ˇse dva dodatna primera: nek algoritem za mnoˇzenje matrik in dva za raˇcunanje diskretne Fourierjeve transformacije, pri ˇcemer slednji gotovo spada med najkoristnejˇse algoritme, kar jih poznamo. V tem poglavju uporabljamo doloˇcene pojme iz linearne in sploˇsne algebre in v kolikor pojasnila v tem besedilu ne zadostujejo, lahko bralec poiˇsˇce dodatne informacije v delu Vidav [15, poglavje 1]. 3.2 Mnoˇ zenje matrik Podani sta dve matriki, A in B, s komponentami Aij in Bij pri Pn1 ≤ i, j ≤ n. Njun produkt, C = A · B, je definiran na obiˇcajen naˇcin kot Cij = k=1 Aik Bkj . Zastavimo si vpraˇsanje, kolikˇsno je minimalno ˇstevilo aritmetiˇcnih operacij, potrebnih za izraˇcun vseh komponent C? Iz zgodovinskih razlogov, pa tudi na podlagi teoretiˇcnih premislekov, je primerno kot prvi pribliˇzek upoˇstevati le operacije mnoˇzenja1 . Torej, kolikˇsno je minimalno ˇstevilo mnoˇzenj, ki je potrebno za izraˇcun komponent matriˇcnega produkta? 1 Zgodovinski razlog je, da sta vˇ casih na raˇ cunalnikih mnoˇ zenje in deljenje zahtevala mnogo veˇ c ˇ casa kot seˇstevanje in odˇstevanje (danes to veˇ c ni res); teoretiˇ cni premislek pa je, da ima v algebraiˇ cnih izpeljavah mnoˇ zenje posebno mesto in da je laˇ ze ocenjevati ˇstevilo mnoˇ zenj kot ˇstevilo seˇstevanj. 79 80 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM Najpreprostejˇsi algoritem za mnoˇzenje matrik seveda izhaja neposredno iz definicije. Zanj se brez teˇzav prepriˇcamo, da porabi n3 mnoˇzenj in je potemtakem klasiˇcen primer raˇcunsko potratnega algoritma. 3.2.1 Metoda S. Winograda Prvi, ki je omenjeni problem raziskoval in priˇsel do doloˇcenih rezultatov, je bil S. Winograd l. 1967. Njegova metoda deluje, ko je n sodo ˇstevilo2 , sloni pa na uporabi naslednjih koliˇcin: πi,k,j ρi,k σk,j = = = = (Ai,2k−1 + B2k,j )(B2k−1,j + Ai,2k ) Ai,2k−1 B2k−1,j + Ai,2k−1 Ai,2k + B2k,j B2k−1,j + B2k,j Ai,2k , Ai,2k−1 Ai,2k , B2k,j B2k−1,j , (3.1) pri 1 ≤ i, j ≤ n in 1 ≤ k ≤ n2 . Vsako od zapisanih koliˇcin lahko izraˇcunamo z enim samim mnoˇzenjem. Komponento Ci,j matriˇcnega produkta lahko nato izrazimo kot n Ci,j = 2 X (πi,k,j − ρi,k − σk,j ). (3.2) k=1 Naj nπ , nρ in nσ predstavljajo ˇstevila elementov posamezne vrste. Potem je ˇstevilo mnoˇzenj za izraˇcun vseh komponent matriˇcnega produkta enako nπ + nρ + nσ . (3.3) Ker oˇcitno velja nπ = 21 n3 , nρ = nσ = 12 n2 , smo z metodo Winograda zmanjˇsali potrebno ˇstevilo mnoˇzenj pribliˇzno na polovico. Bodimo pozorni na to, da pri izraˇcunih komponent produkta uporabljamo koliˇcine ρi,k in σk,j enako pogosto kot πi,k,j , vendar so koliˇcine πi,k,j pri razliˇcnih komponentah razliˇcne, medtem ko se koliˇcine ρi,k in σk,j pri razliˇcnih komponentah ponavljajo. Bistveno za delovanje algoritma je dejstvo, da je mnoˇzenje navadnih ˇstevil komutativno (xy = yx). Namreˇc, ˇce to ne bi veljalo, ne bi pri komponenti Ci,j iz koliˇcine πi,k,j mogli dobiti ˇclena Ai,2k B2k,j . ˇ upoˇstevamo, da za Kolikˇsno pa je ˇstevilo operacij seˇstevanja in odˇstevanja? Ce izraˇcun ene koliˇcine πi,k,j porabimo 2 seˇstevanji, nato, da pri izraˇcunu komponent C P n2 P n2 raˇcunamo vsote k=1 ρi,k ter k=1 σk,j , ki zahtevajo n2 −1 seˇstevanj, vseh takih vsot pa je 2n, in konˇcno, da pri izraˇcunu Ci,j porabimo ˇse 2 dodatni seˇstevanji, dobimo, 1 n 2 · n3 + 2n( − 1) + 2n2 = n3 + 3n2 − 2n, 2 2 kar pa je veˇc kot zahteva klasiˇcna metoda. Zaradi tega in drugih okoliˇsˇcin, ki zmanjˇsujejo hitrost metode v praksi, se metoda ni uveljavila. 2 V kolikor n ni sodo, obravnavamo A in B kot n0 × n0 matriki pri n0 = n + 1, pri ˇ cemer so vsi elementi v zadnji vrstici in zadnjem stolpcu enaki 0. ˇ 3.2. MNOZENJE MATRIK A11 A12 A21 A22 A31 A32 A41 A42 A13 A23 A33 A43 81 A14 B11 B12 A24 B21 B22 A34 B31 B32 A44 B41 B42 B13 B23 B33 B43 B14 B24 B34 B44 Slika 3.1: Delitev problema mnoˇzenja matrik pri n = 4 3.2.2 Metoda V. Strassena V. Strassen je pribliˇzno istoˇcasno z Winogradom iznaˇsel neko novo metodo za mnoˇzenje matrik, ki tudi po velikostnem redu porabi manj mnoˇzenj od klasiˇcne metode. Metoda sloni na neki posebni metodi mnoˇzenja 2 × 2 matrik, ki ne uporablja lastnosti komutativnosti ˇstevil. Zaradi tega v identitetah, ki predstavljajo osnovo za metodo, ˇstevila lahko zamenjamo s podmatrikami obeh matriˇcnih faktorjev in doseˇzemo rekurzivni razcep prvotne naloge. ˇ sta A in B dve 2 × 2 matriki, lahko Strassenove produkte zapiˇsemo takole: Ce π11 π12 π13 π14 = (A11 + A22 )(B11 + B22 ) = (A12 − A22 )(B21 + B22 ) = (A11 + A12 )B22 = A22 (B11 − B21 ) π21 π22 π23 π24 = (A22 + A11 )(B22 + B11 ) = (A21 − A11 )(B12 + B11 ) = (A22 + A21 )B11 = A11 (B22 − B12 ). (3.4) Vseh skupaj je produktov na seznamu (3.4) 8, vendar sta π11 in π21 enaka, kar pomeni, da jih je 7. Lahko ugotovimo, da dobimo produkte π21 , π22 , π23 , π24 iz π11 , π12 , π13 , π14 tako, da povsod indeks 1 zamenjamo z 2 in nasprotno, 2 z 1. Ni teˇzko ugotoviti, da velja C11 C12 C21 C22 = = = = +π11 +π12 −π13 +π13 −π14 −π24 −π14 +π21 +π22 +π23 −π23 (3.5) −π24 Identitete (3.5) ne potrebujejo privzetka komutativnosti mnoˇzenja elementov Ai,j in Bi,j . Zato lahko npr. pri n = 4 koliˇcine A11 , A12 , A21 , A22 in B11 , B12 , B21 , B22 zamenjamo s podmatrikami, kot je to prikazano na sliki 3.1. Pri izraˇcunu porabe ˇcasa Strassenove metode uporabljamo izrek 1.3. V naˇsem primeru smo velikost problema razpolovili (c = 2), za reˇsitev prvotnega problema pa je potrebno reˇsiti sedem podobnih problemov poloviˇcne velikosti (a = 7). Cena za delitev problema in za kasnejˇse sestavljanje reˇsitve je velikostnega reda Θ(n2 ) (torej r = 2), ker je vsak element matrik potrebno vsaj enkrat uporabiti v nekem izraˇcunu. V tem primeru velja a > cr , zato dobimo T (n) = Θ(nlogc a ) = Θ(n2.80735 ). (3.6) 82 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM Glede na rezultat (3.6) bi priˇcakovali, da se bo Strassenov algoritem moˇcno uveˇ pa zaradi njegove rekurzivne narave pride do ljavil v praktiˇcnem raˇcunanju. Zal prevelike porabe prostora, kar ga naredi neprimernega za delo z velikimi matrikami. Klasiˇcni algoritem ima tudi to prednost, da uporablja zelo preproste operacije — nekako bi lahko rekli, da je “homogen”. Zato je primeren za realizacijo na velikih (“vektorskih”) raˇcunalnikih. Zaradi teh in podobnih razlogov se klasiˇcni algoritem ˇse danes uporablja tam, kjer je potrebno mnoˇziti velike matrike. Dodaten razlog, zakaj se uˇcinkovitejˇsi algoritmi za mnoˇzenje matrik v praksi niso uveljavili, tudi tam, kjer bi to lahko priˇcakovali, je, da je do danes prav zaradi ˇcasovne potratnosti klasiˇcnega algoritma nastala vrsta matematiˇcnih metod, ki doseˇzejo isti ali podoben uˇcinek brez uporabe matriˇcnega mnoˇzenja. 3.3 3.3.1 Diskretna Fourierjeva transformacija in algoritmi zanjo Uvod Diskretna Fourierjeva transformacija spada med pomembnejˇsa orodja uporabne matematike, sodobni algoritmi zanjo so pa tudi primeri elegantnih in uˇcinkovitih raˇcunalniˇskih algoritmov. V tem sestavku bomo na kratko opisali diskretno Fourierjevo transformacijo, ali s kratico DFT, njeno uporabo pri raˇcunanju konvolucije polinomov in algoritme zanjo. DFT je opisana v zelo obseˇzni literaturi. Na tem mestu lahko omenimo Aho, Hopcroft in Ullman [1, 7. poglavje] ali Kozak [10, §7.7]. 3.3.2 Diskretna Fourierjeva transformacija Pri definiciji diskretne Fourierjeve transformacije izhajamo iz n-razseˇznostnega vektorskega prostora nad nekim obsegom K(3) Prostor oznaˇcujemo z VK . Pri K privzamemo, da 2π ei n vsebuje nek poseben element ω, ki mu pravimo n-ti primitivni koren enote zaradi naslednjih lastnosti: ei0 2π ei n n = = ω n = 1, (3.7) ω i 6= 1, pri 1 ≤ i ≤ n − 1. (3.8) 1 in Slika 3.2: Klasiˇcni primer [Elementu ω pravimo koren enote zaradi lastnosti n-tega primitivnega korena (3.7) in primitivni koren enote zaradi (3.8).] Takoj enote lahko ugotovimo, da sta (3.7) in (3.8) enakovredni 3 Spomnimo se, da je obseg matematiˇ cna struktura kolobarja, v katerem imata tako seˇstevanje kot mnoˇ zenje ustrezni inverzni operaciji. V nekaterih virih se od mnoˇ zenja zahteva, da je komutativno, v drugih te zahteve ni. Za naˇse potrebe lahko privzamemo, da je mnoˇ zenje komutativno. 3.3 DISKRETNA FOURIERJEVA TRANSFORMACIJA 83 naslednji lastnosti: n−1 X ω pi = i=0 n, 0, pri p = n; pri 1 ≤ p ≤ n − 1. (3.9) Dejansko: naj veljata (3.7) in (3.8). Veljavnost (3.9) pri p = n je oˇcitna. Naj bo sedaj 1 ≤ p ≤ n − 1 in zapiˇsimo: 1 − ω pn = (1 − ω p ) = 0. Pn−1 i=0 ω pi (3.10) Ker na desni strani prve vrstice (3.10) prvi faktor ni enak niˇc [na podlagi (3.8)], mora biti drugi faktor enak niˇc in torej (3.9) velja. Nasprotno pa, naj ne veljata (3.7) in (3.8). Pravzaprav (3.7) velja, ker nas zanimajo le koreni enote, torej naj koren enote ni primitiven [ne velja (3.8)]. V tem primeru eksistira nek 1 ≤ p ≤ n − 1, pri katerem velja ω p = 1. Tedaj pa pri tej vrednosti p ne more veljati druga vrstica desne strani (3.9), in torej (3.9) ne velja. 2 3.1 Primer Klasiˇcen primer n-tega primitivnega korena enote je kompleksno ˇstevilo 2π ei n (gl. sliko 3.2). 3.2 Definicija Diskretna Fourierjeva transformacija stopnje n prostora VK je linearna transformacija tega prostora z matriko F in elementi Fij = ω ij , pri 0 ≤ i, j ≤ n − 1. Ponavadi je n znano in vnaprej doloˇceno tako, da stopnje transformacije niti ne omenjamo. Osnovna lastnost DFT je, da ima inverz v primeru, ko je izpolnjen naslednji pogoj: ˇ obseg k vsebuje element 3.3 Trditev Ce −1 1 −ij Fij = n ω . 1 n, ima F inverz z matriko F −1 in elementi Dokaz. Pri sprejetem privzetku o elementu n1 obsega je element v vrstici i in stolpcu j produkta F · F −1 enak n−1 1 X (i−j)h ω , n h=0 ta vsota pa je na podlagi (3.9) enaka 1 δij = 0 pri i = j; sicer. 2 Koliˇcini δij ponavadi pravimo Kroneckerjev delta simbol. 84 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM 3.4 Pripomba Pri eksponentu i·j elementa ω ij ugotovimo, da je pomemben le ostanek po deljenju z n zaradi relacije ω n = 1. Pogoj o eksistenci elementa n1 najpogosteje opiˇsemo s pogojem, da ima K karakteristiko, ki je razliˇcna od n, pri ˇcemer je karakteristika obsega K tisti najmanjˇsi mnogokratnik 1, ki je enak 0. Na primer, pri ˇstevilih ˇ takega mnogokratnika ni, definiramo karakteristiko kot ∞ po modulu 3 je to 3. Ce (npr. pri racionalnih ˇstevilih). 2 3.5 Primer Pri n = 2 je n-ti primitivni koren enote enak -1 in sta matrika DFT in njen inverz, 1 1 1 1 1 1 = F F = ; F −1 = 1 −1 1 −1 2 2 ˇ pri (seveda pa v sploˇsnem ne velja F −1 = n1 F ). Ce koren enote z ω, sta matrika DFT in njen inverz, 1 1 1 1 1 1 1 ω ω 2 ω 3 −1 1 ;F = F = 1 ω2 1 ω2 4 1 3 2 1 1 1 ω ω ω ˇ upoˇstevamo, da velja ω 2 = ω −2 = −1, ω −1 = ω 3 Ce 1 1 1 1 1 ω −1 −ω ; F −1 = 1 F = 1 −1 1 −1 4 1 −ω −1 ω n = 4 oznaˇcimo n-ti primitivni 1 1 ω −1 ω −2 ω −3 ω −2 1 ω −2 1 ω −3 . ω −2 ω −1 = −ω ter ω −3 = ω 1 , dobimo 1 1 1 1 1 −ω −1 ω . 1 −1 1 −1 1 ω −1 −ω Primer lahko zakljuˇcimo z izraˇcunom podobe nekega vektorja. Naj bo n = 4 in x = [1, 1, 2, 2] t , pri ˇcemer x t oznaˇcuje transpozicijo vektorja x. Na podlagi pravkar zapisane vrednosti F lahko izraˇcunamo F x kot [6, −1 − ω, 0, −1 + ω] t . Naj ˇse pripomnimo, da je v primeru n = 4 in ko je K obseg kompleksnih ˇstevil, ω enako “imaginarnemu” ˇstevilu i (glej primer 3.1). 2 3.3.3 Interpretacija DFT s polinomi V mnogih aplikacijah je primerna interpretacija DFT s polinomi: baza VK je sestavljena iz polinomov xi , pri 0 ≤ i ≤ n − 1. Ker imamo le n baznih elementov, je moˇzno z elementi prostora predstavljati le polinome stopnje n − 1: n−1 X i=0 ai xi . 3.3 DISKRETNA FOURIERJEVA TRANSFORMACIJA 85 Lahko si pa tudi mislimo, da predstavljamo vse polinome (ne glede na stopnjo), vendar izenaˇcimo dva polinoma, ki se razlikujeta le po koeficientih pri stopnjah ≥ n. V tem primeru pravimo, da sta polinoma enaka po modulu xn . Povedano drugaˇce, dva polinoma sta enaka po modulu xn takrat, ko sta ostanka po deljenju z xn ista. Torej so elementi prostora VK polinomi ene spremenljivke po modulu xn s koeficienti iz K. Glede na to, da polinome ne le seˇstevamo, temveˇc tudi mnoˇzimo, imamo pravzaprav opravka s kolobarjem. Pri polinomih uporabljamo razliˇcne predstavitve, med katerimi sta najpomembnejˇsi koeficientna predstavitev in pa vrednostna predstavitev. Prva predstavitev ustreza pravkar omenjeni bazi za prostor polinomov po modulu xn : v tem primeru je i-ta komponenta polinoma p(x) koeficient pri baznem elementu xi . Druga predstavitev pa je povezana z n elementi, c0 , c1 , . . . , cn−1 (ki pripadajo K), polinom p(x) pa predstavljamo z n-terko vrednosti p(c0 ), p(c1 ), . . . , p(cn−1 ) (gl. nalogi 2 in 3). V naˇsem primeru je posebno zanimiv nabor elementov ci = ω i pri 0 ≤ i ≤ n − 1. Ni se teˇzko prepriˇcati v to, da predstavlja DFT transformacijo, ki preslika koeficientno predstavitev nekega polinoma v vrednostno predstavitev v toˇckah ω i , pri 0 ≤ i ≤ n−1. Resniˇcno: n−1 X p(ω i ) = aj ω i·j , j=0 kar predstavlja i-to komponento F ([a0 , a1 , . . . , an−1 ]) (gl. tudi nalogo 4). 3.3.4 Konvolucija polinomov Za hip bomo obravnavali sploˇsne polinome (ne po modulu xn ). Vsak polinom si lahko predstavljamo kot element vektorskega prostora neskonˇcne razseˇznosti, katerega bazni elementi so xi , i ≥ 0, ki pa ima le konˇcno mnogo neniˇcelnih komponent. 3.6 Definicija Konvolucija polinomov p(x) in q(x) je koeficientna predstavitev njuˇ sta vektorja koeficientov polinomov p(x) in q(x), a in nega produkta p(x) · q(x). Ce b, po vrsti, oznaˇcujemo vektor koeficientov produkta p(x) · q(x) z a ◦ b. Ko imamo dva polinoma stopnje n − 1, p(x) = Pn−1 i=0 ai xi , q(x) = Pn−1 j=0 bj xj , je njun produkt, p(x) · q(x) = 2n−2 X n−1 X i=0 aj bi−j xi , j=0 pri ˇcemer koliˇcine bi z indeksom i, ki leˇzijo izven intervala [0, n − 1], izenaˇcimo z 0. Torej, ko sta podana dva vektorja s komponentami a = [a0 , a1 , . . . , an−1 ] b = [b0 , b1 , . . . , bn−1 ] , 86 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM je njuna konvolucija c = a ◦ b vektor z najveˇc 2n − 1 neniˇcelnimi komponentami. i-ta komponenta ci pa je enaka ci = n−1 X aj bi−j , 0 ≤ i ≤ 2n − 2. j=0 ˇ Teˇzava pri raˇcunanju konvolucije je, da je razmeroma potratna operacija. Ce upoˇstevamo samo mnoˇzenja, zahteva raˇcunanje i-te komponente i + 1 mnoˇzenj, pri 0 ≤ i ≤ n − 1 in 2n − i − 1 pri n ≤ i ≤ 2n − 2, kar pomeni, da za vseh 2n − 2 komponent potrebujemo n2 mnoˇzenj (gl. nalogo 1). Izkaˇze pa se, da obstaja pot, ki zahteva manj operacij mnoˇzenja (in tudi operacij nasploh). Ideja je v tem, da najprej ovrednotimo oba faktorja, p(x) in q(x), pri potencah 2n-tega primitivnega korena enote, za kar uporabimo diskretno Fourierjevo transformacijo stopnje 2n, nato tako pridobljene vektorje F (p(x)) in F (q(x)) zmnoˇzimo po komponentah ter konˇcno uporabimo inverzno DFT na vektorju, ki je sestavljen iz produktov istoleˇznih komponent. Ideja sloni na dejstvu, da je vrednost produkta dveh polinomov v poljubni toˇcki, produkt ˇ sta a in b vektorja koeficientov p(x) in q(x), lahko vrednosti faktorjev v isti toˇcki. Ce zapiˇsemo, a ◦ b = F −1 (F (a) ∗ F (b)), kjer simbol ∗ oznaˇcuje vektor, ki ga dobimo iz dveh faktorjev tako, da zmnoˇzimo istoleˇzne komponente4 . Na prvi pogled je teˇzava v tem, da sta faktorja polinoma stopnje n − 1, rezultat pa polinom stopnje 2n − 2. Ponavadi postopamo tako, da oba vektorja faktorjev podaljˇsamo do dolˇzine 2n (z dodajanjem niˇcelnih komponent) in tudi rezultat obravnavamo kot vektor dolˇzine 2n (namesto 2n − 1). Razlog je v tem, da je vedno ugodno, ˇce je n potenca ˇstevila 2. To, kar smo doslej opisali, lahko strnemo v naslednji izrek: 3.7 Izrek (o konvoluciji). Podana sta dva polinoma stopnje n − 1: p(x) = n−1 X ai xi in q(x) = i=0 n−1 X bj xj . j=0 Naj bosta a = [a0 , a1 , . . . , an−1 , 0, . . . , 0] t in b = [b0 , b1 , . . . , bn−1 , 0, . . . , 0] t vektorja dolˇzine 2n, pri ˇcemer predstavlja t simbol za transponiranje, vektorja F (a) = [a00 , a01 , . . . , a02n−1 ] t in F (b) = [b00 , b01 , . . . , b02n−1 ] t pa sta ustrezni DFT transformiranki. Potem velja a ◦ b = F −1 (F (a) ∗ F (b)). 4 Naslednje dejstvo navajamo kot zanimivost: spomnimo se, da je Kroneckerjev produkt neke m×n matrike A in neke m0 × n0 matrike B, mm0 × nn0 matrika C = A ⊗ B z elementi Cii0 ,jj 0 = Aij Bi0 j 0 . Zato lahko predstavimo operacijo ∗ takole: levi faktor si predstavljamo kot neko n × 1 matriko, desni pa kot neko 1 × n matriko; potem je a ∗ b enako diagonali matrike a ⊗ b. 3.3 87 DISKRETNA FOURIERJEVA TRANSFORMACIJA Dokaz. Ker velja ai = bi = 0, pri n ≤ i ≤ 2n − 1, lahko zapiˇsemo Pn−1 Pn−1 a0l = j=0 aj ω lj , b0l = k=0 bk ω lk , pri 0 ≤ l ≤ 2n − 1. Torej velja a0l b0l = n−1 X n−1 X aj bk ω l(j+k) . (3.11) j=0 k=0 Zapiˇsimo sedaj a ◦ b = [c0 , c1 , . . . , c2n−1 ] t ter F (a ◦ b) = [c00 , c01 , . . . , c02n−1 ] t . Ker velja cp = P2n−1 j=0 aj bp−j , lahko zapiˇsemo c0l = 2n−1 X 2n−1 X aj bp−j ω lp . p=0 j=0 ˇ zamenjamo vrstni red pri vsoti ter p − j s k, dobimo Ce c0l = 2n−1 X 2n−1−j X j=0 aj bk ω l(j+k) . k=−j Glede na to, da indeksa j in k lahko omejimo na interval [0, n − 1], lahko v drugi vsoti spremenimo k = −j v k = 0 in 2n − 1 − j v n − 1, v prvi vsoti pa 2n − 1 v n − 1. Zato je c0l enako a0l b0l , kot smo to zapisali v (3.11). 2 3.8 Primer Dana sta polinoma p(x) = 2x + 1 in q(x) = −2x + 1 (ki ju piˇsemo na obiˇcajen naˇcin, po padajoˇcih potencah x) in ˇzelimo izraˇcunati njuno konvolucijo na pravkar opisan naˇcin. Seveda jo je v tem primeru moˇzno izraˇcunati tudi neposredno kot −4x2 + 1. Najprej koeficiente polinomov predstavimo z vektorjema dolˇzine 4: p = [1, 2, 0, 0] t , q = [1, −2, 0, 0] t (bodimo pozorni, da so sedaj koeficienti razvrˇsˇceni po naraˇsˇcajoˇcih potencah x), ki ju nato zmnoˇzimo z leve z matriko DFT pri n = 4, ki smo jo predstavili v primeru 3.5: F (p) = F · p = [3, 1 + 2ω, −1, 1 − 2ω] t , F (q) = F · q = [−1, 1 − 2ω, 3, 1 + 2ω] t . Naslednji korak je izraˇcun vektorja F (p) ∗ F (q) = [−3, 5, −3, 5] , 88 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM na katerem nato uporabimo inverzno DFT (gl. ponovno primer 3.5): F −1 (F (p) ∗ F (q)) = 1 [4, 0, −16, 0] , 4 kar se ujema z neposrednim izraˇcunom. 2 V nekaterih aplikacijah uporabljamo tim. “ovito konvolucijo”. 3.9 Definicija Dana sta dva vektorja a = [a0 , a1 , . . . , an−1 ] t , b = [b0 , b1 , . . . , bn−1 ] t . Potem je pozitivna ovita konvolucija a in b vektor c = [c0 , c1 , . . . , cn−1 ] t s komponentami i n−1 X X ci = aj bi−j + aj bn+i−j . j=0 j=i+1 Pozitivno ovito konvolucijo a in b oznaˇcujemo z a2b. Na podoben naˇcin definiramo negativno ovito konvolucijo a in b kot vektor d = [d0 , d1 , . . . , dn−1 ] t s komponentami di = i X j=0 aj bi−j − n−1 X aj bn+i−j . j=i+1 Negativno ovito konvolucijo a in b oznaˇcujemo z a3b. 3.10 Primer Naj bo n = 4 in naj bosta dana dva vektorja dolˇzine 4, a = [a0 , a1 , a2 , a3 ] , b = [b0 , b1 , b2 , b3 ] . V tem primeru ima njuna pozitivna (negativna) ovita konvolucija komponente c0 c1 c2 c3 = a0 b0 ± a1 b3 ± a2 b2 ± a3 b1 = a0 b1 + a1 b0 ± a2 b3 ± a3 b2 = a0 b2 + a1 b1 + a2 b0 ± a3 b3 = a0 b3 + a1 b2 + a2 b1 + a3 b0 , pri ˇcemer za pozitivno ovito konvolucijo izbiramo znak +, za negativno pa znak − (takrat ko je izbira nakazana). 2 3.11 Izrek Dana sta dva vektorja a = [a0 , a1 , . . . , an−1 ] t , b = [b0 , b1 , . . . , bn−1 ] t . 3.3 89 DISKRETNA FOURIERJEVA TRANSFORMACIJA Naj bo ω n-ti primitivni koren enote, za ψ pa naj velja ψ 2 = ω (torej je ψ 2n-ti primitivni koren enote). Naj ima n multiplikativni inverz. Potem velja a2b = F −1 (F (a) ∗ F (b)) in ˆ a3b = F −1 (F (ˆ a) ∗ F (b)), kjer velja za poljuben vektor c = [c0 , c1 , . . . , cn−1 ] t , ˆ = [c0 , ψc1 , . . . , ψ n−1 cn−1 ] t . c Dokaz izreka je podoben dokazu izreka 3.7, ˇce upoˇstevamo ψ n = −1. 3.3.5 Rekurzivni algoritem za DFT V tem razdelku bomo opisali osnovno idejo, ki pripelje do najenostavnejˇsega algoritma za DFT. Kot osnovo uporabljamo interpretacijo DFT s polinomi, ki smo jo opisali v razdelku 3.3.3 in na podlagi katere so komponente podobe vektorja Pn−1 a = [a0 , a1 , . . . , an−1 ], p(ω 0 ), p(ω 1 ), . . . , p(ω n−1 ) , kjer je p polinom p(x) = i=0 ai xi . Izhajamo iz dveh preprostih lastnosti n-tega primitivnega korena enote. Naj bo n = 2r. Potem velja (ω j+r )2 = ω 2j ω 2r = ω 2j (3.12) in ω j+r = −ω j . (3.13) V veljavnost (3.13) se prepriˇcamo na podlagi relacije (3.8). Namreˇc, razliko levega in desnega dela (3.12) razcepimo in dobimo (ω j+r + ω j )(ω j+r − ω j ) = 0. Ker desni faktor leve strani ne more biti 0 [na podlagi (3.8)], velja torej (3.13). Sedaj pa naj bo podan polinom p(x) z vektorjem koeficientov a. Torej p(x) = a0 + a1 x + . . . + an−1 xn−1 . Vpeljali bomo dva pomoˇzna polinoma, p1 (x) in p2 (x), stopnje r − 1: p1 (x) = a1 + a3 x + . . . + a2i+1 xi + . . . + an−1 xr−1 , p2 (x) = a0 + a2 x + . . . + a2i xi + . . . + an−2 xr−1 . (3.14) Torej ima p1 (x) koeficiente lihih potenc xi polinoma p(x), p2 (x) pa sodih potenc. Nato lahko zapiˇsemo p(x) = xp1 (x2 ) + p2 (x2 ). (3.15) 90 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM Ko iˇsˇcemo F (a), oziroma vrednosti p(ω k ) pri 0 ≤ k ≤ n − 1, lahko uporabimo relacijo (3.15). Imamo torej p(ω k ) p(ω k+r ) = ω k p1 (ω 2k ) + p2 (ω 2k ), = ω k+r p1 (ω 2(k+r) ) + p2 (ω 2(k+r) ) = −ω k p1 (ω 2k ) + p2 (ω 2k ), (3.16) pri 0 ≤ k ≤ r − 1. Leve strani (3.16) lahko neposredno izraˇcunamo s algoritmom, ki uporablja rekurzivni razcep: problem smo razbili na dva podproblema poloviˇcne velikosti. Podproblema sta izraˇcuna pi (ω 2k ), pri 0 ≤ k ≤ r − 1 in i = 1, 2. Bodimo pozorni na to, da je ω 2 primitivni r-ti koren enote in sta torej podproblema identiˇcna prvotnemu problemu, razen da smo n prepolovili. Relacija (3.13) nam ˇse omogoˇca, da dobimo eno polovico vrednosti p(ω k ) praktiˇcno zastonj (izraˇcunamo p(ω k ), pri 0 ≤ k ≤ r − 1, p(ω k+r ) pa dobimo skoraj brez dodatnega raˇcunanja, ˇce v desni strani prve vrstice 3.16 negiramo prvi ˇclen). 3.12 Primer Opisano metodo bomo prikazali na majhnem primeru. Podan je polinom x3 + 2x2 + 2x + 1 in ˇzelimo izraˇcunati DFT vektorja njegovih koeficientov, [1, 2, 2, 1]. V tem primeru sta p1 (x) = x + 2 in p2 (x) = 2x + 1. Lahko bi p1 in p2 ˇse naprej razcepili in dobili ˇstiri polinome stopnje 0, vendar bomo p1 (ω 0 ), p1 (ω 2 ), p2 (ω 0 ) in p2 (ω 2 ) kar neposredno izraˇcunali z matriko DFT pri n = 2 (gl. primer 3.5). p1 (ω 0 ) p1 (ω 2 ) p2 (ω 0 ) p2 (ω 2 ) = = 1 1 1 −1 1 1 1 −1 2 1 1 2 = = 3 1 3 −1 . Nato za izraˇcun komponent transformiranega vektorja uporabimo enaˇcbe (3.16): p(ω 0 ) = p(ω 1 ) = p(ω 2 ) = p(ω 3 ) = ω 0 p1 (ω 0 ) + p2 (ω 0 ) = 3 + 3 = 6 ω 1 p1 (ω 2 ) + p2 (ω 2 ) = −1 + ω ω 2 p1 (ω 0 ) + p2 (ω 0 ) = 3 − 3 = 0 ω 3 p1 (ω 2 ) + p2 (ω 2 ) = −1 − ω in dobimo isti rezultat kot ˇce [1, 2, 2, 1] neposredno zmnoˇzimo z leve z matriko DFT za n = 4. 2 ˇ Cas, ki ga porabi algoritem, ki uporablja opisani izraˇcun, ocenimo na podlagi izreka 1.3. Problem smo razcepili na dva podproblema (c = 2), ki ju moramo oba reˇsiti (a = 2) in ker moramo za vsak element p(ω i ), 0 ≤ i ≤ n − 1, izraˇcunati le vsoto dveh ˇclenov, je r = 1. Iz tega izpeljemo porabo ˇcasa Θ(n log n) (gl. tudi nalogo 5 za nekatere podrobnosti). 3.3 DISKRETNA FOURIERJEVA TRANSFORMACIJA 3.3.6 91 Iterativni algoritem za DFT Pri zasnovi algoritmov se poskuˇsamo izogibati rekurziji, ˇce je to le mogoˇce, ker je njena realizacija ˇcasovno potratna. Izkaˇze se, da je to moˇzno storiti tudi v primeru DFT. Podobno kot pri rekurzivnem algoritmu iz razdelka 3.3.5 izhajamo iz pojmovanja DFT kot seznama vrednosti polinoma p(x) po modulu xn v toˇckah x = ω 0 , ω 1 , . . . , ω n−1 , pri ˇcemer so komponente vektorja, ki ga transformiramo, koeficienti p(x) pri stopnjah x. Pri sestavljanju iterativnega algoritma se opiramo na dve trditvi. Prva je 3.13 Trditev Naj bo p(x) polinom nad izbranim obsegom K in naj bo c ∈ K. V tem primeru velja p(c) = p(x) mod (x − c). (3.17) Dokaz. Relacija (3.17) sledi iz tega, ker je x − c polinom prve stopnje in je zato p(x) mod (x − c) polinom stopnje 0 (konstanta) c0 z lastnostjo p(x) = q(x) · (x − c) + c0 , (3.18) kjer je q(x) nek drug polinom, ki ima stopnjo za eno manjˇso od stopnje p(x). Oˇcitno je pri x = c prvi ˇclen desne strani (3.18) enak 0 in je p(c) enako c0 . 2 Druga trditev, ki jo potrebujemo, je 3.14 Trditev Naj bodo p(x), s(x), t(x) in u(x) polinomi nad izbranim obsegom K in naj velja s(x) = t(x) · u(x). V tem primeru velja p(x) mod t(x) = (p(x) mod s(x)) mod t(x). (3.19) Dokaz. Relacija (3.19) sledi iz definicije p(x) mod s(x) = rs (x), kjer je rs (x) polinom, ki ima stopnjo manjˇso ali enako stopnji s(x) ter lastnost, p(x) = qs (x) · s(x) + rs (x), (3.20) pri ˇcemer je qs (x) nek drug polinom, ki zagotavlja enakost (3.20). Sedaj v levi strani (3.19) zamenjajmo p(x) z desno stranjo (3.20): (qs (x) · s(x) + rs (x)) mod t(x) = = (qs (x) · t(x) · u(x) + rs (x)) mod t(x) rs (x)) mod t(x). (3.21) Leva stran prve vrstice (3.21) je enaka desni strani druge vrstice zato, ker velja qs (x) · t(x) · u(x) mod t(x) = 0. 2 Relacija (3.19) omogoˇca, da uredimo vmesne delitelje v izraˇcunu p(x) mod (x−ω k ), pri 0 ≤ k ≤ n − 1 v drevesno strukturo, s ˇcimer doseˇzemo, da izraˇcunov ni potrebno ponavljati. Zadevo bomo najprej prikazali na primeru n = 4 in ˇsele nato podali opis sploˇsnega algoritma. 92 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM x4 − 1 x2 − 1 x − ω0 = x−1 x2 + 1 x − ω2 = x+1 x − ω3 = x+ω x − ω1 Slika 3.3: Drevesna urejenost deliteljev med izraˇcunom DFT pri n = 4. x3 + 2x2 + 2x + 1 3x + 3 6 x−1 0 −1 + ω −1 − ω Slika 3.4: Ostanki po deljenju polinoma x3 + 2x2 + 2x + 1 z delitelji na sliki 3.3. 3.15 Primer Slika 3.3 prikazuje delitelje za primer n = 4. Mimogrede: neposredna podrejenost dveh deliteljev nekemu tretjemu delitelju pomeni, da je produkt prvih ˇ je podan polinom p(x) = a0 + a1 x + a2 x2 + a3 x3 , je potrebno dveh enak tretjemu. Ce izraˇcunati vrednosti p(x) mod (x−1), p(x) mod (x+1), p(x) mod (x−ω) in p(x) mod (x + ω). Na podlagi relacije (3.19) bomo na primer pri izraˇcunu p(x) mod (x − ω) najprej poiskali ostanek po deljenju p(x) z x4 − 1 (oˇcitno je to kar p(x)), nato bomo delili ta ostanek z x2 + 1 in poiskali ostanek, in konˇcno bomo slednji ostanek delili z x − ω. Opisano zaporedje izraˇcunov je na sliki 3.3 nakazano s ˇcrtkano potjo med korenom in konˇcnim vozliˇsˇcem x − ω. Bodimo pozorni na to, da v spodnji vrstici deliteljev na sliki 3.3 eksponenti ω niso urejeni v vrstnem redu 0, 1, 2, 3. Metodo lahko prikaˇzemo na primeru 3.12, ki smo ga ˇze uporabili kot primer za rekurzivno metodo. Izraˇcun ostankov je prikazan na sliki 3.4 in lahko ugotovimo, da 3.3 93 DISKRETNA FOURIERJEVA TRANSFORMACIJA d03 (x) d02 (x) d01 (x) d00 (x) d10 (x) d42 (x) d21 (x) d20 (x) d30 (x) d41 (x) d40 (x) d50 (x) d61 (x) d60 (x) d70 (x) Slika 3.5: Drevesna urejenost deliteljev med izraˇcunom DFT pri n = 8. d03 (x) = x8 − ω 0 d02 (x) = x4 − ω 0 d42 (x) = x4 − ω 4 2 0 2 4 2 d01 (x) = x − ω d21 (x) = x − ω d41 (x) = x − ω 2 d61 (x) = x2 − ω 6 d00 (x) = d10 (x) = d20 (x) = d30 (x) = d40 (x) = d50 (x) = d60 (x) = d70 (x) = x − ω0 x − ω4 x − ω2 x − ω6 x − ω1 x − ω5 x − ω3 x − ω7 Slika 3.6: Vrednosti delitevljev pri n = 8. so ostanki v spodnji vrstici istovetni z vrednostmi, ki smo jih dobili v primeru 3.12. 2 Sedaj se pa lotimo sploˇsnega algoritma. Obravnavali bomo primer n = 2k , pri k ≥ 0. Delitelje uredimo v drevesno strukturo, podobno kot na sliki 3.3. Oznaˇcevanje deliteljev, kakor tudi njihove dejanske vrednosti za primer k = 3 so prikazani na slikah 3.5 in 3.6. Iz slik 3.3 in 3.5 ugotovimo, da ima drevo deliteljev k + 1 vodoravnih vrstic, ki so oˇstevilˇcene od 0 do k, in sicer od spodnje vrstice do korena drevesa. Pri delitelju z oznako dih predstavlja indeks h ˇstevilko vrstice (“viˇsino”), za prve indekse i pri doloˇceni viˇsini h pa uporabljamo oˇstevilˇcenje, ki se zaˇcenja z 0 in uporablja prirastek 2h . Lahko postavimo vpraˇsanje, zakaj i preprosto ne ustreza mestu delitelja v svoji vrstici, vendar za enkrat nanj ˇse ne bomo odgovorili, naj zadostuje, da povemo, da ima tudi ˇstevilo, ki predstavlja mesto delitelja svojo vlogo, ki jo bomo razloˇzili v kratkem. Pri ˇstevilih, ki oznaˇcujejo eksponente oziroma indekse, je n izenaˇceno z 0 (ker predstavljamo polinome po modulu xn !), zato za njih zadostuje dvojiˇska 94 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM i }| z { 0 ... 0 l h bitov k bitov Slika 3.7: Dvojiˇska predstavitev indeksa i delitelja dih . rev4 (19) = rev4 (1011B) = 1101B = 21 Slika 3.8: Funkcija rev (dvojiˇska predstavitev je oznaˇcena z “B”). predstavitev dolˇzine k (k bitov)5 . Pri taki predstavitvi in na podlagi pravkar zapisane razlage predstavlja indeks i delitelja dih ˇstevilo, ki ima zadnjih h bitov enakih 0 (gl. sliko 3.7), z drugimi besedami, i = i0 · 2h , pri nekem i0 v intervalu [0, . . . , 2k−h − 1]. Delitelji so definirani kot, h dih = x2 − ω s , (3.22) pri ˇcemer je s podobno kot i neko ˇstevilo z dvojiˇsko predstavitvijo, ki ima na desni h niˇcelnih bitov, torej je s = l · 2h . V takem primeru dobimo naslednjo povezavo med delitelji v dveh sosednih vrsticah drevesa deliteljev: h+1 x2 h+1 − ω l·2 h h h h = (x2 − ω l·2 )(x2 + ω l·2 ) h h h h n = (x2 − ω l·2 )(x2 − ω l·2 + 2 ). (3.23) Preden nadaljujemo bomo zapisali ˇse neko definicijo, ki jo bomo potrebovali. 3.16 Definicija Naj bosta k in n pozitivni celi ˇstevili. Z revk (n) bomo oznaˇcevali ˇstevilo z dvojiˇsko predstavitvijo dolˇzine k, ki je enako zrcalni podobi dvojiˇske predstavitve n (gl. sliko 3.8). V primeru, ko je dolˇzina dvojiˇske predstavitve n veˇc kot k, pred zrcaljenjem odveˇcne bite odreˇzemo, v primeru, ko pa je manj kot k, dodamo ustrezno ˇstevilo niˇcelnih bitov. h h Sedaj se bomo poglobili v urejenost deliteljev x2 − ω l·2 v vrstici h. Relacija h+1 h+1 (3.23) nam razodeva, da se delitelj iz viˇsje vrstice, x2 − ω l·2 , razcepi na dva delitelja, pri katerih dobimo ustrezna eksponenta ω tako, da l · 2h+1 pomaknemo za 5 Seveda pa to ne velja za koeficiente a polinomov, ki jih predstavljamo kot obiˇ cajna ˇstevila, ne i glede na vrednost k. 3.3 DISKRETNA FOURIERJEVA TRANSFORMACIJA 95 h + 1 bitov z }| { 0 ... ... 0 l 0 l 1 l 0 ... 0 | 0 ... 0 {z } h bitov Slika 3.9: Odvisnost indeksov deliteljev v niˇzji vrstici drevesa od indeksa delitelja v viˇsji vrstici. eno dvojiˇsko mesto v desno in nato enkrat v prvo dvojiˇsko mesto z leve zapiˇsemo 0, drugiˇc pa 1 (gl. sliko 3.9). Na podlagi te ugotovitve lahko dokaˇzemo naslednjo trditev, 3.17 Trditev Naj bo, h h x2 − ω li ·2 , i = 0, 1, . . . , 2k−h − 1, delitelj na mestu i z leve v vrstici h. V tem primeru je li · 2h = revk (i). Oˇcitno velja tudi i = revk (li · 2h ). Preden se lotimo dokaza, naj opozorimo, da v pravkar zapisani trditvi uporabljamo mesto delitelja v vrstici in ne njegovega indeksa, kot je bil opisan prej. Dokaz. Za zgornjo vrstico drevesa je trditev oˇcitna, saj vsebuje le en delitelj z vrednostjo l0 = 0. Sedaj denimo, da trditev velja za delitelje v vrstici h + 1 in naj bo h+1 h+1 x2 − ω l·2 nek delitelj iz te vrstice. Njegovo mesto je revk (l · 2h+1 ) (na podlagi predpostavke), mesti njegovih naslednikov v niˇzji vrstici pa sta 2 · revk (l · 2h+1 ) in ˇ 2 · revk (l · 2h+1 ) + 1 (ker je deliteljev v niˇzji vrstici dvakrat toliko kot v viˇsji). Ce h 0 00 sta koeficienta pri 2 dveh naslednikov l in l , sledi iz (3.23) oziroma pripombe pred trditvijo 3.17, da je revk (l0 2h ) = 2 · revk (l · 2h+1 ) in revk (l00 2h ) = 2 · revk (l · 2h+1 ) + 1. Slednji dve enaˇcbi veljata zato, ker se desni pomik koliˇcine l · 2h+1 prevede v levi pomik (in s tem mnoˇzenje z 2) koliˇcine revk (l · 2h+1 ). 2 96 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM 3.18 Primer Naj bo n = 23 = 8. V tem primeru so delitelji v spodnji vrstici drevesa (h = 0), d00 (x) = (x − ω 0 ), d40 (x) = (x − ω 1 ), 4 d10 (x) = (x − ω ), d50 (x) = (x − ω 5 , 2 d20 (x) = (x − ω ), d60 (x) = (x − ω 3 ), 6 d30 (x) = (x − ω ), d70 (x) = (x − ω 7 ). Torej je zaporedje eksponentov ω, 0, 4, 2, 6, 1, 5, 3, 7, kar je enako, rev3 (0), rev3 (1), rev3 (2), rev3 (3), rev3 (4), rev3 (5), rev3 (6), rev3 (7). 2 Pri eksponentu ω, l · 2h , delitelja dih bodimo pozorni na dejstvo, da v primeru, ko ne bi zrcalili zaporednega mesta delitelja temveˇc njegov indeks i (ki ima desnih h bitov enakih 0), bi dobili l in ne l · 2h . Do sedaj smo za eksponent ω potrebovali lastnost, da je mnogokratnik 2h , zato da smo izpeljali rekurzivno relacijo (3.23), odslej pa nas njegov razcep v obliki l · 2h ne bo zanimal in bo zadostovalo, da je njegova vrednost v zrcalni povezavi z mestom delitelja v svoji vrstici. h Glede na to, da imajo vsi delitelji dij preprosto obliko x2 − ω s , se tudi izraˇcun P2h+1 −1 ostankov ustrezno poenostavi. Ko je p(x) = j=0 aj xj , lahko zapiˇsemo p(x) mod (x 2h s − ω ) = r(x) = h 2X −1 (aj + ω s aj+2h )xj . (3.24) j=0 Relacijo (3.24) preverimo tako, da desno stran vstavimo namesto r(x) v h 2X −1 h p(x) = aj+2h xj (x2 − ω s ) + r(x). j=0 Popoln iterativni algoritem za DFT je prikazan na sliki 3.10. V prikazanem algoritmu so vhodni podatki v tabeli a, rezultat pa v b. Bodimo pozorni na operacijo zrcaljenja dvojiˇske predstavitve l v vrstici 8, ki je potrebna zaradi trditve 3.17. Naˇsa zadnja pripomba o algoritmu na sliki 3.10 se bo nanaˇsala na njegovo porabo prostora. Koeficienti polinoma r0,k (ostanek pri korenu drevesa) so kar vhodni podatki a. Nato pa bi se na prvi pogled zdelo, da v vsaki vrstici drevesa potrebujemo eno tabelo, ki vsebuje koeficiente ri,h+1 , ter dve, ki vsebujeta koeficiente rl,h in rl+2h ,h . Ker pa je koeficientov ri,h+1 natanko toliko kot koeficientov rl,h in rl+2h ,h skupaj, je skupna dolˇzina izhodnih tabel enaka dolˇzini vhodne tabele. Nadalje, ˇce smo pozorni ugotovimo, da koeficiente rl,h in rl+2h ,h pravzaprav lahko spravimo v prostor, ki so ga prej zasedali koeficienti ri,h+1 . Namreˇc, ˇce v vrsticah 9 in 10 izraˇcun koeficientov rl,h 3.3 DISKRETNA FOURIERJEVA TRANSFORMACIJA 1 3 3 4 5 6 (∗ n = 2k ∗) BEGIN Pn−1 (∗ Naj bo r0,k = j=0 aj xj ∗) FOR h:=k −1 BY −1 TO 0 DO i:=0 ; WHILE i < n DO P2h+1 −1 7 8 (∗ Naj bo ri,h+1 = s:= revk (i DIV 2h ) ; 9 ri,h := 10 11 13 13 14 16 97 P2h −1 j=0 j=0 aj xj ∗) (aj + ω s aj+2h )xj ; P2h −1 ri+2h ,h := (aj + ω s+n/2 aj+2h )xj ; j=0 (h+1) i := i + 2 END END ; FOR i:=0 TO n−1 DO brevk (i) := ri,0 END END Slika 3.10: Iterativni algoritem za DFT in rl+2h ,h izvajamo istoˇcasno, lahko koeficiente rl,h zapisujemo na mesta koeficientov aj ostanka ri,h+1 pri 0 ≤ j < 2h , koeficiente rl+2h ,h pa na mesta koeficientov aj+2h . Iz tega sledi, da potrebujemo le eno tabelo koeficientov med celotnim izraˇcunom. Sedaj postane oˇcitna tudi indeksacija deliteljev dih : i predstavlja zaˇcetni indeks koeficientov ostanka ri,h v tabeli koeficientov a. Ker ima ri,h 2h koeficientov, naraˇsˇca i s takim prirastkom. Lahko tudi ugotovimo, da posebna izhodna tabela b ni potrebna, kajti zaradi lastnosti revk (revk (i)) = i, lahko izraˇcun v vrstici 14 izpeljemo kot eno samo zaporedje zamenjav dveh vrednosti, ki ga lahko opravimo z eno zaˇcasno spremenljivko (za podrobnosti gl. nalogo 6). Do sedaj smo zanemarili neko navidez pomembno vpraˇsanje: opisali smo dva uˇcinkovita algoritma za DFT, vendar nobenega za inverzno transformacijo! Glede na to, da inverzno transformacijo potrebujemo enako pogosto kot DFT, bi kazalo, da problema sploh nismo reˇsili. Vendar je na sreˇco moˇzno iz algoritma za DFT brez teˇzav pridobiti algoritem za inverzno DFT na podlagi ugotovitve, da je v primeru, ko je ω n-ti primitivni koren enote, tudi ω −1 n-ti primitivni koren enote (preverjanje tega prepuˇsˇcamo bralcu za vajo). Na podlagi tega je moˇzno inverzno matriko DFT predstaviti v obliki 1 −ij 1 F −1 = ω = (ω −1 )ij . n n Matrika na desni (brez faktorja n1 ) seveda predstavlja matriko DFT glede na nov n-ti primitivni koren enote ω −1 . To pa pomeni, da je moˇzno inverzno DFT izraˇcunati s poljubnim algoritmom za DFT po zamenjavi ω z ω −1 in z naknadnim mnoˇzenjem vseh komponent rezultata z n1 . 98 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM Na koncu bi bilo potrebno ˇse preveriti, da iterativni algoritem na sliki 3.10 zahteva O(n2 log n) operacij. Na ta naˇcin bi se prepriˇcali, da je po velikostnem redu enako uˇcinkovit kot rekurzivni algoritem, ker pa ne uporablja rekurzije, lahko upamo, da je dejansko boljˇsi (in tudi je). Vendar podrobno izpeljavo tega dejstva izpuˇsˇcamo, ker brez teˇzav sledi iz zapisa algoritma na sliki 3.10 (vaja). 3.4 Povzetek osnovnih pojmov 1. Klasiˇcni algoritem za mnoˇzenje matrik in njegova poraba ˇcasa 2. Winogradov algoritem in zakaj ga ni moˇzno uporabiti rekurzivno 3. Strassenov algoritem in ocena njegove porabe ˇcasa 4. n-ti primitivni koren enote in njegove lastnosti 5. Matrika diskretne Fourierjeve transformacije in njene lastnosti 6. Koeficientna in vrednostna predstavitvi polinoma 7. Polinomi po modulu xn 8. Povezava med DFT in polinomi 9. Konvolucija polinomov ter izrek o konvoluciji 10. Pozitivna in negativna ovita konvolucija ter izrek o pozitivni in negativni oviti konvoluciji 11. Rekurzivni algoritem za DFT in lastnosti n-tega primitivnega korena enote, ki omogoˇcajo njegovo delovanje 12. Iterativni algoritem za DFT (povezava med raˇcunanjem vrednosti polinoma in raˇcunanjem ostanka polinoma po deljenju z linearnim polinomom; naˇcelo postopnega ali iteriranega raˇcunanja ostankov; urejanje deliteljev pri raˇcunanju ostankov v dvojiˇsko drevo; operator zrcaljenja) 13. Algoritem za inverzno DFT. 3.5 Naloge 1. Preverite izraˇcun, da definicija konvolucije dveh polinomov stopnje n − 1 vsebuje n2 mnoˇzenj. 2. Opiˇsite matriko koordinatne transformacije, ki spremeni koeficientno predstavitev polinoma p(x) stopnje n−1 v vrednostno predstavitev pri vrednostih x = c0 , c1 , . . . , cn−1 . 3. Kakˇsen pogoj mora biti izpolnjen, da je predstavitev polinomov po modulu xn z vrednostmi pri c0 , c1 , . . . , cn−1 enakovredna predstavitvi s koeficienti pri xi ? Razmislite o pomenu pojma “enakovredna”. 4. Bazo prostora polinomov po modulu xn pri koeficientni predstavitvi tvorijo polinomi xi , 0 ≤ i ≤ n − 1. Kateri polinomi pa predstavljajo bazo prostora po opravljeni DFT? 99 3.5. NALOGE 5. Pri realizaciji rekurzivnega algoritma za DFT se sreˇcamo z naslednjim problemom: pri prvem poskusu ugotovimo, da je potrebno uporabiti poleg prostora za vektor koeficientov vhodnega polinoma ˇse dodaten prostor za koeficiente polinomov, ki nastanejo ˇ je prvotni vektor dolˇzine n, potrebujemo 2n dodatnih celic pri rekurzivnih klicih. Ce prostora (naloga: preverite to trditev). Razmislite o realizaciji rekurzivnega algoritma, ki ne bi potreboval dodatnega prostora (kot smo to dosegli pri algoritmu za urejanje s porazdelitvami). Napotek: osnovna teˇzava je, da so pri koliˇcinah, ki predstavljajo rezultat rekurzivnih klicev, p2 (ω 0 ), p1 (ω 0 ), . . . , p2 (ω 2i ), p1 (ω 2i ), . . . , p2 (ω 2(r−1) ), p1 (ω 2(r−1) ) in rezultatu DFT b0 , b1 , . . . , br , br+1 , . . . , bn−1 , bi in br+i , pri 0 ≤ i ≤ r − 1 odvisni od p2 (ω 2i ) in p1 (ω 2i ), kar prepreˇcuje, da za prvi in drugi vektor uporabimo isti prostor. Moˇzna reˇsitev je, da rezultata rekurzivnih klicev preuredimo v vrstni red p2 (ω 0 ), . . . , p2 (ω 2i ), . . . , p2 (ω 2(r−1) ), p1 (ω 0 ), . . . , p1 (ω 2i ), . . . , p1 (ω 2(r−1) ). Po tem je moˇzno prostor, ki sta ga zasedala p2 (ω 2i ) in p1 (ω 2i ), uporabiti za bi in br+i . Najugodneje je preurejanje izvajati ˇze na prvotnih koeficientih polinoma p. Koliko ˇcasa porabi opisani postopek? 6. Na koncu iterativnega algoritma za DFT smo morali opraviti zamenjave koeficientov v izhodni tabeli tako, da smo vsak koeficient zamenjali s koeficientom, ki ima “zrcalni” indeks. Zapisano je, da za to operacijo ni potrebno imeti posebne tabele izhodnih koeficientov. Vendar bi se na prvi pogled zdelo, da potrebujemo vsaj en bit informacije na koeficient, ki nam pri obiskovanju koeficientov pove, ali smo koeficient ˇze zrcalili. Predlagajte algoritem, ki odpravi potrebo tudi po tem dodatnem prostoru. 100 POGLAVJE 3. ALGORITMI Z REKURZIVNIM RAZCEPOM Poglavje 4 ˇ ˇ POZRE SNI ALGORITMI 4.1 Uvod V tem poglavju se bomo zaˇceli ukvarjati z optimizacijskimi problemi, kot pravimo nalogi iskanja najboljˇse reˇsitve v neki mnoˇzici reˇsitev. Optimizacijski problemi se pojavljajo v ˇstevilnih razliˇcicah, ki pogojujejo tudi razliˇcne pristope k njihovemu reˇsevanju. V najpreprostejˇsi razliˇcici naloge imamo mnoˇzico “objektov”, ki jim znamo prirediti “ceno”, oziroma vrednost, in iˇsˇcemo takega, ki ima najmanjˇso (ali najveˇcjo) ceno, oziroma vrednost1 . Pogosto uporabljamo pri izbiri predmetov poleg kriterija vrednosti ˇse kakˇsen drug kriterij, ki mu preprosto pravimo kriterij “dopustnosti”. Sploˇsno zgradbo takega problema predstavlja kar navadno, linearno iskanje, kot ga prikazuje slika 4.1. Ponavadi je osnovna teˇzava pri podobnih problemih velikost n in je potrebno vloˇziti precej napora, da spravimo obseg raˇcunanja v znosne meje. V skrajnem primeru je n neskonˇcno in je potrebno z matematiˇcno analizo izraˇcun optimuma prevesti na iskanje po konˇcni mnoˇzici. Vˇcasih pa imamo opravka z zmerno velikostjo n, vendar je naloga poiskati neko podmnoˇzico elementov ui , 1 ≤ i ≤ n, ki ima ˇzelene, optimalne lastnosti (gl. sliko 4.2). V takem primeru naloga pogosto postane neobvladljiva tudi pri zmerno velikem n (in kljub temu, da je program na sliki 4.2 praktiˇcno identiˇcen s programom na sliki 4.1), zato ker je ˇstevilo podmnoˇzic eksponentno odvisno od n. V nekaterih primerih pa je moˇzno nalogo na sliki 4.2 moˇcno poenostaviti in izpeljati izbiro elementov optimalne podmnoˇzice tako, da jih izbiramo enega za drugim, pri ˇcemer vsak element obravnavamo le enkrat. To je moˇzno, kadar je vrednost neke podmnoˇzice preprosto vsota vrednosti njenih elementov in ko izbira nekega elementa ne more postaviti pod vpraˇsaj izbire njegovih predhodnikov (izbire elementov so neodvisne). V tem primeru je moˇzno elemente ovrednotiti pred postopkom sestavljanja optimalne podmnoˇzice in jih vkljuˇcevati v optimalno podmnoˇzico na podlagi njihove vrednosti. Ker opisan algoritem najprej “pohrusta” element z najveˇcjo vrednostjo 1 Izraz cena je primeren, ko iˇsˇ cemo najmanjˇso koliˇ cino, vrednost pa, ko iˇsˇ cemo najveˇ cjo. 101 102 ˇ SNI ˇ ALGORITMI POGLAVJE 4. POZRE VAR maxvr :INTEGER; x ,maxelx :index ; (∗ tip, ki mu pripadajo indeksi elementov ∗) BEGIN (∗ U je mnoˇzica elementov u1 , u2 , . . . , un ∗) maxelx :=1; maxvr :=vrednost(u1 ); FOR x :=2 TO n DO IF dopustno(ux ) & (vrednost(ux ) > maxvr ) THEN maxvr :=vrednost(ux ); maxelx :=x END END (∗ rezultat je element z indeksom maxelx in vrednostjo maxvr ∗) END Slika 4.1: Osnovna oblika algoritma za reˇsevanje optimizacijskega problema VAR maxvr :INTEGER; (∗ U je mnoˇzica elementov u1 , u2 , . . . , un ∗) (∗ podmnoˇzico Ax ⊂ U predstavljamo s karakteristiˇcno funkcijo x, ki pa jo lahko predstavimo kot ˇstevilsko vrednost, ˇcigar dvojiˇska predstavitev nakazuje, kateri elementi so vkljuˇceni v podmnoˇzico ∗) x ,mnx :INTEGER; (∗ indeksa trenutne podmnoˇzice in maksimalne podmnoˇzice ∗) BEGIN mnx :=0; maxvr :=vrednost(Amnx ); FOR x :=1 TO 2n − 1 DO IF dopustno(Ax ) & (vrednost(Ax ) > maxvr ) THEN maxvr :=vrednost(Ax ); mnx :=(x ) END END (∗ rezultat je Amnx z vrednostjo maxvr ∗) END Slika 4.2: Iskanje optimalne podmnoˇzice ˇ SNEGA ˇ 4.2. OSNOVNA ZGRADBA POZRE ALGORITMA 103 TYPE Element = . . . VAR maxvr :INTEGER; s:SetOfElement; VAR a: ARRAY OF Element; BEGIN (∗ U je mnoˇzica elementov u1 , u2 , . . . , un ∗) (∗ Elemente u1 , u2 , . . . , un uredimo po nenaraˇsˇcajoˇci vrednosti in tako urejeno zaporedje priredimo tabeli a ∗) maxvr :=0; s:=∅; FOR x :=1 TO n DO IF dopustno(s∪{a[x]}) THEN s:=s∪{a[x]} ; INC (maxvr ,Vrednost(a[x ])) END END (∗ rezultat je mnoˇzica s z vrednostjo maxvr ∗) END Slika 4.3: Osnovna oblika poˇzreˇsnega algoritma (najbolj “slasten” element), mu pravimo, da je “poˇzreˇsen”. 4.2 Osnovna zgradba poˇ zreˇ snega algoritma Osnovna zgradba poˇzreˇsnega algoritma je prikazana na sl. 4.3. Iz slike je razvidno, da pri takem naˇcinu reˇsevanja ne gre za zapleten program in je torej morebitna teˇzavnost reˇsevanja problema s poˇzreˇsnim algoritmom kveˇcjemu v iskanju primernega poˇzreˇsnega vrstega reda in pa v dokazovanju, da poˇzreˇsna metoda resniˇcno poiˇsˇce reˇsitev. V preostanku poglavja bomo prikazali nekaj zgledov, kako se prepriˇcamo v pravilnost poˇzreˇsnega reˇsevanja problemov. 4.3 Razvrˇ sˇ canje zapisov na magnetnem traku Imamo n zapisov, ki jih ˇzelimo zapisati na magnetni trak tako, da jih lahko kasneje po potrebi preberemo. Naprava za magnetni trak ima lastnost, da pred vsako zahtevo po branju zapisa trak previje na zaˇcetek, nato ˇzeleni zapis poiˇsˇce tako, da vsebino traku bere od zaˇcetka proti koncu, dokler ne prebere iskanega. Trak se premika s konstantno hitrostjo in zato je ˇcas za celotno operacijo sorazmeren dolˇzini traku do ˇ je verjetnost zahteve po branju i-tega zapisa enaka pri vseh konca iskanega zapisa. Ce i, 1 ≤ i ≤ n, katero zaporedje zapisov na traku zagotavlja najmanjˇsi povpreˇcni ˇcas branja enega zapisa. Dolˇzina zapisa z indeksom i je li . Naj bo zaporedje zapisov na traku i1 , i2 , . . . , in . (4.1) ˇ SNI ˇ ALGORITMI POGLAVJE 4. POZRE 104 ˇ Cas, ki ga porabimo za branje j-tega zapisa je potemtakem, tj = c · j X lik , k=1 kjer je c koeficient proporcionalnosti med ˇcasom in dolˇzino. Povpreˇcni ˇcas za branje vseh zapisov pri privzetku, da je njihovo branje enakoverjetno, pa je n t= n j 1X c XX tj = lik . n j=1 n j=1 (4.2) k=1 Naloga je torej, poiskati zaporedje (4.1), ki zagotavlja minimalno vrednost koliˇcine (4.2). ˇ Ceprav je naloga trivialna, predstavlja njena reˇsitev prikaz osnovne tehnike reˇsevanja sorodnih problemov. Naloga je namreˇc trivialna zato, ker iz pogojev vidimo, da se zapisi pri zaˇcetku traku veˇckrat berejo od zapisov pri koncu in je torej smiselno postaviti krajˇse zapise na zaˇcetek. Torej na podlagi tega premisleka uganemo, da je pravilna reˇsitev razvrstitev zapisov v nepadajoˇcem zaporedju njihove dolˇzine. V tem primeru je predikat dopustno na sliki 4.3 neodvisen od x in vedno resniˇcen. Sedaj dokaˇzimo, da v takem primeru poˇzreˇsno zaporedje predstavlja pravilno reˇsitev. Naj bo I = i1 , i2 , . . . , in , poˇzreˇsno zaporedje, torej zaporedje, za katerega velja li1 ≤ li2 ≤ . . . ≤ lin , pri ˇcemer imamo dodaten pogoj, da v primeru dveh zapisov ij in ij+1 enakih dolˇzin velja ij < ij+1 . Sedaj denimo, da to zaporedje ni pravilna reˇsitev. Z drugimi besedami, obstaja neko drugo zaporedje, J = j1 , j2 , . . . , jn , ki je optimalno. Glede na to, da sta I in J razliˇcna, obstaja prvi indeks (ko gremo od zaˇcetka proti koncu), kjer se razlikujeta. Naj bo to indeks h. Torej velja ik = jk , 1 ≤ k < h, in ih 6= jh . In ker se v obeh zaporedjih pojavljajo vsi indeksi, gotovo obstaja t´ ako k > h, da je jk = ih . Sedaj J spremenimo v J 0 tako, da zamenjamo poloˇzaja jh in jk , nato pa na podlagi (4.2) izraˇcunamo t(J) − t(J 0 ): c(k − h) (ljh − ljk ). n Glede na to, da velja ljk = lih ≤ ljh , ugotovimo, da velja t(J) − t(J 0 ) ≥ 0. V ˇ primeru vrednosti 0 je J 0 enakovredna reˇsitev, vendar bolj podobna reˇsitvi I. Ce postopek nadaljujemo z J 0 namesto J, dobimo na koncu v primeru, ko je razlika med povpreˇcnima ˇcasoma zaporedij vedno 0, zaporedje, ki je identiˇcno z I, kar je v nasprotju s privzetkom, da I ni pravilna reˇsitev. Ko pa velja t(J) − t(J 0 ) > 0, smo dobili strogo boljˇso reˇsitev od J, kar je v protislovju s privzetkom, da je J optimalna reˇsitev. t(J) − t(J 0 ) = ˇ 4.4. RAZVRSˇCANJE POSLOV V DELAVNICI Z ENIM STROJEM • reˇsitev 1,2 1,3 1,4 2,3 3,4 1 2 3 4 zaporedje 2,1 1,3 ali 3,1 4,1 2,3 4,3 1 2 3 4 105 vrednost 110 115 127 25 42 100 100 15 27 Slika 4.4: Reˇsitev problema razvrˇsˇcanja poslov iz primera 4.1 4.4 Razvrˇ sˇ canje poslov v delavnici z enim strojem Neka delavnica ima en sam stroj, ki obdeluje posle enega za drugim in za vsak posel ˇ imamo n poslov, tako da je i-temu prirejena vrednost vi in rok porabi enoto ˇcasa. Ce izgotovitve ti , se postavlja problem poiskati podmnoˇzico poslov in vrstni red njihovega izvajanja, s katerima doseˇzemo najveˇcjo skupno vrednost izgotovljenih poslov pri pogoju, da so vsi posli iz podmnoˇzice dokonˇcani do svojega roka. 4.1 Primer Imamo naslednje podatke: n = 4 v = h100, 10, 15, 27i t = h2, 1, 2, 1i , kjer je v vektor vrednosti poslov, t pa vektor roka izgotovitve. Slika 4.4 prikazuje vse moˇzne reˇsitve ter med njimi optimalno (gl. tudi nalogo 1). 2 Najprej vpeljemo nekaj pojmov: zaporedje poslov hi1 , i2 , . . . , ik i je dopustno, ko se posli lahko obdelujejo v takem zaporedju, ne da bi katerikoli zamujal. Podmnoˇzica poslov {i1 , i2 , . . . , ik } je dopustna v primeru, ko za posle iz podmnoˇzice obstaja neko dopustno zaporedje. Sedaj dokaˇzemo, da ni potrebno obravnavati vseh moˇznih zaporedij poslov iz neke podmnoˇzice, temveˇc je pri preverjanju dopustnosti podmnoˇzice potrebno preveriti le eno samo zaporedje. ˇ je zaporedje izvajanja poslov 4.2 Trditev Ce J = hj1 , j2 , . . . , jk i, dopustno, je dopustno tudi poˇzreˇsno zaporedje I = hi1 , i2 , . . . , ik i, ki ima lastnost, da so posli urejeni po nepadajoˇcem roku izgotovitve: ti1 ≤ ti2 ≤ . . . ≤ tik , v primeru enakega roka pa po naraˇsˇcajoˇcem indeksu. 106 ˇ SNI ˇ ALGORITMI POGLAVJE 4. POZRE Dokaz. Trditev dokaˇzemo podobno, kot smo dokazali optimalnost poˇzreˇsnega zaporedja iz razdelka 4.3: naj bo I poˇzreˇsno zaporedje in denimo, da ni dopustno. J je dopustno zaporedje, ki pa se razlikuje od I. V tem primeru obstaja nek prvi indeks h (od leve proti desni), pri katerem se zaporedji razlikujeta. Ker je I poˇzreˇsno zaporedje, velja tih ≤ tjh in ker obe zaporedji vsebujeta iste elemente, je posel z indeksom ih tudi v zaporedju J, na primer na k-tem mestu, pri ˇcemer velja k > h in seveda jk = ih . Ko v J zamenjamo mesti poslov jh in jk , dobimo zaporedje J 0 , ki je bolj podobno I in prav tako dopustno. Namreˇc jk = ih lahko obdelamo na h-tem mestu, ker vsak posel lahko prestavimo naprej; po drugi strani pa lahko jh obdelamo na k-tem mestu zato, ker velja tih = tjk ≤ tjh (ˇce je bilo moˇzno obdelati jk na k-tem mestu, je gotovo moˇzno obdelati na tem mestu tudi jh , ki ima enak ali kasnejˇsi rok). Z veˇckratno uporabo opisanega postopka pridemo do dopustnega zaporedja, ki je identiˇcno z I. 2 Naslednji izrek pa utemeljuje uporabo poˇzreˇsne metode. 4.3 Izrek Optimalno zaporedje izvajanja poslov je poˇzreˇsno zaporedje I = hi1 , i2 , . . . , ik i, kjer na koraku j izberemo posel z najveˇcjo vrednostjo med preostalimi posli, pod pogojem, da po prikljuˇcitvi novega posla mnoˇzica izbranih poslov ostane dopustna. Dokaz. Naj trditev iz izreka ne drˇzi in naj bo I zaporedje poslov na podlagi poˇzreˇsnega kriterija, kot je opisano v trditvi, J = hj1 , j2 , . . . , jr i, pa domnevno razliˇcno, optimalno zaporedje. Najprej obe zaporedji uredimo po padajoˇci vrednosti, v primeru enakih vrednosti pa po naraˇsˇcajoˇcem indeksu: vi1 ≥ vi2 ≥ . . . ≥ vik , vj1 ≥ vj2 ≥ . . . ≥ vjr . Takoj ugotovimo, da I ni stroga podmnoˇzica J (I ⊂ J in I 6= J), ker bi v takem primeru tudi poˇzreˇsna metoda nadaljevala z izbiranjem elementov. Torej eksistira indeks h ≤ min(k, r), tako da velja iq = jq , q = 1, 2, . . . , h − 1 in ih 6= jh . Poˇzreˇsna metoda izbira posle po padajoˇci vrednosti, v primeru enakih vrednosti pa po naraˇsˇcajoˇcem indeksu. Zato velja vih ≥ vjh . V primeru vih > vjh ugotovimo, da ih ∈ / J (ker smo zaporedje J naknadno uredili po padajoˇci vrednosti oziroma, v primeru enakih vrednosti, po naraˇsˇcajoˇcem indeksu); pri vih = vjh pa prav tako velja ih ∈ / J, ker poˇzreˇsna metoda v takem primeru posle izbira na podlagi naraˇsˇcajoˇcega indeksa, J pa smo na tak naˇcin uredili naknadno. 107 4.5. POVZETEK OSNOVNIH POJMOV Ker je J dopustna reˇsitev, lahko posle, ki jo sestavljajo, izvrˇsimo v vrstnem redu nepadajoˇcega roka izgotovitve. Naj bo R podmnoˇzica poslov iz J, ki vsebuje zaˇcetne posle iz pravkar opisanega zaporedja izvajanja, do vkljuˇcno posla, ki se izvaja na koraku tih . Ker vsak posel traja en korak, velja | R | = min(r, tih ). Toda | R | < tih ni moˇzno (ker bi potem lahko vkljuˇcili ˇse posel ih ); torej velja | R | = t ih . Ker so posli do h-tega prisotni v obeh reˇsitvah, obstaja v R posel jh0 , pri h0 > h. Sedaj sestavimo mnoˇzico, J 0 = J − {jh0 } + {ih }. Mnoˇzica J 0 je dopustna. Ker velja vih ≥ vjh ≥ vjh0 , velja tudi X i∈J 0 vi ≥ X vi . (4.3) i∈J Torej smo priˇsli do reˇsitve, ki je na podlagi (4.3) bodisi boljˇsa od J, kar je v nasprotju s privzetkom o optimalnosti J, ali prav tako optimalna in bolj podobna poˇzreˇsni reˇsitvi (ima daljˇsi skupni zaˇcetek). 2 Za nalogo razvrˇsˇcanja poslov v delavnici z enim strojem prikazuje slika 4.5 preprost algoritem, ki uporablja urejanje z navadnim vstavljanjem. Reˇsitev E vsebuje posle, ki so na podlagi trditve 4.2 urejeni po naraˇsˇcajoˇcem roku izgotovitve. Zanka v vrsticah 33–39 obravnava predmete enega za drugim in za vsak predmet preverja, ali ga je dopustno vstaviti v obstojeˇco reˇsitev. To je moˇzno takrat, ko imajo vsi predmeti s kasnejˇsim rokom kot je rok predmeta i, ˇse toliko zaloge pri svojem roku izgotovitve, da jih lahko v reˇsitvi prestavimo za eno mesto viˇsje. 4.5 Povzetek osnovnih pojmov 1. Opis optimizacijskega problema 2. Opis poˇzreˇsne metode 3. Osnovna teˇzava pri realizaciji poˇzreˇsne metode 4. Znaˇcilen naˇcin dokazovanja pravilnosti poˇzreˇsne metode na primeru razvrˇsˇcanja zapisov na magnetnem traku 5. Naloga razporejanja poslov v delavnici z enim strojem. ˇ SNI ˇ ALGORITMI POGLAVJE 4. POZRE 108 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 19 20 21 22 24 24 25 26 27 28 29 31 31 32 33 34 35 36 37 38 39 41 41 (∗ ˇstevilo poslov je n in imajo indekse 1, 2, . . . , n. Podatki o poslih so zapisani v tabeli AElt, urejeni pa so po nenaraˇsˇcajoˇcih cenah; AElt[i].t je skrajni rok izgotovitve posla i, AElt[i].v pa je njegova vrednost. E[r] je indeks posla v optimalni reˇsitvi, ki se izvaja na koraku r ∗) TYPE Elt=RECORD v ,t:INTEGER (∗ opis posla ∗) END; VAR (∗ posli urejeni po nenaraˇsˇcajoˇci vrednosti ∗) AElt : ARRAY n OF Elt ; maxvr : INTEGER; (∗ reˇsitev urejena po naraˇsˇcajoˇcem roku izgotovitve ∗) E : ARRAY n+1 OF INTEGER; i, (∗ tekoˇci indeks tabele AElt ∗) j , (∗ pomoˇzna spremenljivka ∗) k , (∗ tekoˇci indeks reˇsitve E ∗) r , (∗ meja znotraj E , do katere elementov ne prestavljamo ∗) : INTEGER; PROCEDURE Dopustno(i,k :INTEGER; VAR r :INTEGER):BOOLEAN ; (∗ posel i je moˇzno dodati reˇsitvi velikosti k ; r je mesto, nad katerega vstavimo nov posel ∗) BEGIN r :=k ; (∗ poiˇsˇcemo mesto posla i ∗) WHILE (E [r ].t > AElt[i].t) & (E [r ].t # r ) DO DEC (r ) END; (∗ preverimo dopustnost ∗) RETURN (E [r ].t≤AElt[i].t) & (AElt[i].t > r ) END Dopustno ; BEGIN AElt[0].t:=0;E [0]:=0; (∗ ˇcuvaj ∗) maxvr :=0; k :=1; E [1]:=1; FOR i:=2 TO n DO IF Dopustno(i,k ,r ) THEN (∗ elemente reˇsitve prestavimo za eno mesto ∗) FOR j :=k TO r +1 BY −1 DO E [j +1]:=E [j ] END; (∗ i postavimo na svoje mesto v reˇsitvi, poveˇcamo k in maxvr ∗) E [r +1]:=i; INC (k ); INC (maxvr ,AElt[k ].v ) END; END END. Slika 4.5: Podrobnosti programa za razvrˇsˇcanje poslov 4.6. NALOGE 4.6 109 Naloge 1. Pri primeru 4.1 opravite natanˇcno analizo in dokaˇzite, da slika 4.4 prikazuje vse moˇzne reˇsitve. 2. (Cormen, Leiserson in Rivest) Potnik se pelje po avtocesti od mesta A do B in ˇzeli potovanje opraviti s ˇcimmanj postanki za polnjenje goriva. Ima tudi karto z razdaljami ˇ velja privzetek, da v potnikovo posodo za gorivo gre do vseh avtocestnih servisov. Ce veˇc goriva kot je potrebno za najveˇcjo razdaljo med sosednjima servisoma, sestavite uˇcinkovit algoritem za opisano nalogo in dokaˇzite, da je pravilen. ˇ 3. Podanih je n predmetov z vrednostmi v1 , v2 , . . . , vn . Zelimo poiskati podmnoˇzico m < n predmetov, ki ima najveˇcjo skupno vrednost. Dokaˇzite, da reˇsitev lahko poiˇsˇcemo s poˇzreˇsno metodo. Napotek: posnemajte metodo iz razdelka 4.3. 4. Problem 0-1 nahrbtnika je definiran z n predmeti, ki imajo vrednost ci in prostornino vi pri 1 ≤ i ≤ n, ter ˇstevilom V , naloga pa je poiskati n ˇstevil xi ∈ {0, 1}, 1 ≤ i ≤ n, tako da je Pn xi vi ≤ V, Pi=1 n x c i=1 i i pa doseˇze najveˇcjo vrednost. Ta problem bomo obravnavali kasneje. V primeru pa, ko dovolimo, da ima eno ˇstevilo xk , 1 ≤ k ≤ n, poljubno vrednost v intervalu [0, 1] govorimo o poenostavljenem problemu nahrbtnika. Slednji problem je moˇzno reˇsiti s poˇzreˇsno metodo, tako da za poˇzreˇsni kriterij izberemo koliˇcino ci /vi (“specifiˇcno vrednost”). Dokaˇzite pravilnost te metode. Uporabite podoben pristop, kot smo ga izbrali pri razvrˇsˇcanju zapisov na magnetnem traku. 110 ˇ SNI ˇ ALGORITMI POGLAVJE 4. POZRE Poglavje 5 PRETOKI IN LINEARNO PROGRAMIRANJE 5.1 Uvod V tem poglavju nadaljujemo z metodami za reˇsevanje optimizacijskih problemov. V predhodnem smo prikazali probleme, ki jih je mogoˇce reˇsevati na najpreprostejˇsi moˇzen naˇcin, sedaj pa nadaljujemo s takimi, ki zahtevajo nekoliko bolj premiˇsljen pristop. Pravzaprav se bomo ukvarjali s problemi, ki imajo neko posebno matematiˇcno znaˇcilnost, ki omogoˇca uˇcinkovito reˇsevanje. Gre za probleme, ki spadajo v podroˇcje linearnega programiranja, oziroma za probleme, ki predstavljajo poenostavljeno obliko problemov tega razreda. 5.2 Problem maksimalnega pretoka skozi omreˇ zje Poenostavljena oblika problema linearnega programiranja, ki jo bomo predstavili na zaˇcetku, je problem maksimalnega pretoka skozi omreˇzje. 5.2.1 2 2 3 Definicija problema 4 3 1 1 1 6 Podan je graf z vozliˇsˇci 1, 2, . . . , n, vsaki povezavi hi, ji 1 pa je prirejeno nenegativno ˇstevilo cij , ki mu pravimo 2 2 ˇ si povezave predstakapaciteta (gl. sliko 5.1). Ce 5 3 2 vljamo kot cevi, po katerih se pretaka neka tekoˇcina, je kapaciteta najveˇcja koliˇcina tekoˇcine, ki lahko v Slika 5.1: Graf s kapacitetami enoti ˇcasa preteˇce po povezavi v stabilnih razmerah. povezav Z drugimi besedami, kapaciteta je najveˇcji pretok po povezavi. Smer pretoka je v smeri povezave, torej od i do j. 111 112 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE Sedaj opiˇsemo problem maksimalnega pretoka od nekega vhodnega vozliˇsˇca, ki mu pravimo izvor , do nekega izhodnega vozliˇsˇca, ki mu pravimo ponor (denimo, da sta to vozliˇsˇci 1 in n, po vrsti). Naj xij predstavlja pretok skozi povezavo hi, ji. Tedaj oˇcitno velja, 0 ≤ xij ≤ cij . (5.1) Poleg tega pogoja velja pri vseh vozliˇsˇcih, razen izvora in ponora, ˇse zakon o ohranjanju pretoka. Z drugimi besedami, kar priteˇce v neko vozliˇsˇce i, mora iz njega tudi izteˇci: X X xji = xij . (5.2) j j ˇ upoˇstevamo ˇse izvor in ponor, dobimo relacijo, Ce −v, i = 1 X X 0, i = 6 1, n xji − xij = j j v, i = n. (5.3) ˇ je x = (xij ) nabor vrednosti, ki zadoˇsˇcajo pogojema (5.1) in (5.3) in ˇce je v(x) Ce koliˇcina na desni strani 5.3, pravimo, da je v celotni pretok med izvorom in ponorom. Ko ne bo nevarnosti za nesporazum, bomo z besedo pretok poimenovali tudi nabor ˇ ima v najveˇcjo vrednost med vsemi nabori, ki izpolnjujejo zapisane pogoje, je x. Ce v maksimalen pretok. V tem razdelku bomo spoznali nekaj lastnosti maksimalnega pretoka, ki nam omogoˇcajo, da ga na uˇcinkovit naˇcin izraˇcunamo. Naj bo P neusmerjena pot od izvora do ponora, kar pomeni, da lahko vsebuje tako povezave, ki so usmerjene od izvora do ponora, kakor tudi take, ki so nasprotno usmerjene. Na primer, na sliki 5.1 je primer take poti 1–2–4–5–6 in na njej so povezave prve vrste 1–2, 2–4 in 5–6, povezava 4–5 pa je druge vrste. Na kratko bomo rekli povezavi hi, ji prve vrste (torej, ko je usmerjena od i proti j), da je pozitivna, povezavi ˇ so podani pretoki po vseh povezavah x = (xij ), ki druge vrste pa, da je negativna. Ce zadoˇsˇcajo pogojema (5.1) in (5.3) in neka pot P od izvora do ponora, pravimo, da je P zasiˇcena v primeru, ko bodisi za neko pozitivno povezavo na poti velja, xij = cij , bodisi za neko negativno povezavo, xij = 0. Na primer na sliki 5.2 predstavlja slika 5.2b zasiˇceno pot, 5.2c pa nezasiˇceno pot. Lahko bi tudi rekli, da je neka pozitivna povezava hi, ji zasiˇcena, ko velja xij = cij , negativna pa, ko velja xij = 0. V tem primeru lahko opiˇsemo zasiˇceno pot kot tako, na kateri je vsaj ena povezava zasiˇcena. Na sliki 5.2a je prikazan nek moˇzen pretok skozi graf. Prvo ˇstevilo ob povezavi hi, ji predstavlja njeno kapaciteto cij , drugo pa pretok xij . Ni se teˇzko prepriˇcati, da koliˇcine xij zadoˇsˇcajo pogojema (5.1) in (5.3) in da je celoten pretok med izvorom in ponorom enak 3. Na sliki 5.2c je prikazana nezasiˇcena pot od izvora do ponora pri pretoku na sliki 5.2a. Pretok po prikazani poti lahko poveˇcamo za eno enoto tako, da ga v vsaki pozitivni povezavi poveˇcamo za eno enoto in zmanjˇsamo prav za toliko v vsaki negativni povezavi. Kot posledico dobimo poveˇcan pretok z vrednostjo 4, ki je prikazan na sl. 5.2d. Ni se teˇzko prepriˇcati, da tudi spremenjeni pretok zadoˇsˇca pogojema (5.1) in (5.3). ˇ 5.2. PROBLEM MAKSIMALNEGA PRETOKA SKOZI OMREZJE 2 2,1 4 3,1 3,2 1,0 1 1,1 6 1,0 2,2 3 2,2 2 3,2 1,0 1 2,1 6 1,0 5 3 (b) Zasiˇ cena pot 2 4 2,2 4 3,2 3,1 1 4 2 3,1 (a) Zaˇ cetni pretok 2,1 113 1,1 6 2,1 1 1,0 6 1,0 2,2 3 5 (c) Nezasiˇ cena pot 3,2 1,0 2,2 2,2 5 (d) Poveˇ can pretok Slika 5.2: Postopek veˇcanja nekega zaˇcetnega pretoka. Pri povezavah pomeni prvo ˇstevilo kapaciteto, drugo pa vrednost pretoka Sedaj definirajmo pojem prereza med izvorom in ponorom. To je par kompleˇ hoˇcemo mentarnih podmnoˇzic vozliˇsˇc hS, T i, pri ˇcemer velja 1 ∈ S ter n ∈ T . Ce poudariti izvor in ponor, zapiˇsemo, da je hS, T i (1, n)-prerez. Kapaciteto prereza hS, T i definiramo kot X c(S, T ) = cij , (5.4) i∈S,j∈T torej je to vsota kapacitet vseh povezav med S in T , ki so usmerjene od S proti T . Vrednost poljubnega (celotnega) pretoka med izvorom in ponorom ima neko lastnost, ki je sicer precej oˇcitna, a jo bomo opisali s posebnim izrekom: 5.1 Izrek (o zgornji meji za vrednost pretoka) G = hV, Ei naj bo graf z uteˇzenimi ˇ povezavami, pri ˇcemer uteˇzi predstavljajo kapacitete. Naj bo 1 izvor, n pa ponor. Ce je x = (xij ) nabor pretokov po povezavah, ki zadoˇsˇca pogojema (5.1) in (5.3), v pa vrednost celotnega pretoka med izvorom in ponorom, velja v ≤ c(S, T ), (5.5) kjer je c(S, T ) kapaciteta poljubnega (1, n)-prereza hS, T i. Dokaz. Izrek dokaˇzemo tako, da seˇstejemo vse enaˇcbe (5.3), ki so prirejene vozliˇsˇcem 114 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE i ∈ S. V tem primeru dobimo, P P P v = ( j xij − j xji ) Pi∈S P = (x − xji ) + i∈S,j∈T (xij − xji ) Pi,j∈S ij = i∈S,j∈T (xij − xji ). (5.6) Vrednost desne strani tretje vrstice (5.6) je enaka vrednosti desne strani druge vrstice zato, ker je prvi ˇclen druge vrstice enak 0. Namreˇc, v tem ˇclenu se vsaka koliˇcina xij , pri i, j ∈ S, pojavlja dvakrat, enkrat s pozitivnim predznakom, drugiˇc z negativnim (pozitivni predznak se pojavi pri ciljnem vozliˇsˇcu povezave hi, ji, negativni pa pri izhodiˇsˇcnem vozliˇsˇcu). Tretja vrstica torej pravi, da je vrednost v celotnega pretoka enaka razliki vsot vseh pretokov skozi povezave, ki vodijo iz S v T in onih, ki so nasprotno usmerjene1 . Ker pa velja xij ≤ cij ter xji ≥ 0, imamo, X v≤ cij = c(S, T ). (5.7) i∈S,j∈T Torej celotni pretok od izvora do ponora ne presega kapacitete poljubnega prereza (med izvorom in ponorom). 2 Pravkar zapisani izrek 5.1 ima preprosto, a pomembno posledico: 5.2 Posledica Naj bodo definicije iste kot v zapisu izreka 5.1. V primeru, ko pri (5.5) velja enakost, je pretok v maksimalen. Na primer v primeru poveˇcanega pretoka na sl. 5.2d dejansko obstaja (1,6)-prerez s kapaciteto, ki je enaka vrednosti pretoka. Primer takega prereza je h{1, 2}, {3, 4, 5, 6}i. Posledica 5.2 tedaj zagotavlja, da je pretok na sliki 5.2d maksimalen. Sedaj bomo zapisali in dokazali tri osnovne izreke o pretokih, ki jih bomo kasneje uporabili pri sestavljanju algoritma za izraˇcun maksimalnega pretoka. 5.3 Izrek (o zasiˇ cenih poteh) Nek pretok je maksimalen natanko tedaj, ko so vse poti od izvora do ponora zasiˇcene. Dokaz. V primeru, ko obstaja nezasiˇcena pot od izvora do ponora, pretok oˇcitno ni maksimalen, ker lahko pretok po njej poveˇcamo tako, kot smo to storili v primeru na sliki 5.2a. Po drugi strani pa naj bo x nek pretok, ki ima vse poti zasiˇcene. Naj bo S mnoˇzica vozliˇsˇc, ki vsebuje izvor ter vozliˇsˇca, do katerih pelje nezasiˇcena pot iz izvora, T pa naj bo komplementarna mnoˇzica. Na podlagi definicije nezasiˇcene poti sklepamo, da pri vseh i ∈ S in j ∈ T velja xij = cij ter xji = 0 (ker bi v nasprotnem primeru tudi j pripadalo mnoˇzici S). Iz tega izpeljemo vrednost pretoka X v= cij , i∈S,j∈T 1 Naˇ celoma je moˇ zno, da sta med i in j dve, nasprotno usmerjeni povezavi, sicer pa je tista vrednost xij oziroma xji , ki ustreza neobstojeˇ ci povezavi, enaka 0. ˇ 5.2. PROBLEM MAKSIMALNEGA PRETOKA SKOZI OMREZJE 115 kar predstavlja kapaciteto prereza hS, T i. Na podlagi posledice 5.2 sklepamo, da je pretok maksimalen. 2 Pomen izreka je, da nam ponuja kriterij za presojo, kdaj smo dosegli maksimalen pretok. 5.4 Izrek (o celoˇ stevilˇ cnosti maksimalnega pretoka) V primeru, ko so vse kapacitete cela ˇstevila, obstaja tudi celoˇstevilˇcen maksimalen pretok. Dokaz. Naj bodo vse kapacite celoˇstevilˇcne in naj bo zaˇcetni pretok x0 . V primeru, ko v(x0 ) ni maksimalen, obstaja nezasiˇcena pot, kar ima za posledico eksistenco ˇ v(x1 ) ˇse vedno ni maksiceloˇstevilˇcnega pretoka x1 , tako da velja v(x1 ) > v(x0 ). Ce malen, imamo zopet nezasiˇceno pot itn. Glede na to, da je vrednost vsakega pretoka najmanj za ena veˇcja od vrednosti predhodnega, na koncu pridemo do celoˇstevilˇcnega pretoka, ki ima vse poti zasiˇcene in je torej maksimalen. 2 Pomen pravkar zapisanega izreka je v tem, da zagotavlja, da lahko pridemo do reˇsitve v konˇcnem ˇstevilu korakov, pri ˇcemer je en korak sestavljen iz iskanja nezasiˇcene poti ter njenega zasiˇcenja. 5.5 Izrek (o max pretoku pri min prerezu) Vrednost maksimalnega pretoka je enaka minimalni kapaciteti nekega (1, n)-prereza. Dokaz. V primeru, ko je pretok enak minimalni kapaciteti nekega prereza, je gotovo maksimalen na podlagi posledice 5.2. Ko pa imamo opravka z maksimalnim pretokom, lahko tako kot v dokazu izreka 5.3 poiˇsˇcemo prerez s kapaciteto, ki je enaka pretoku. Ta prerez pa ima oˇcitno minimalno kapaciteto, ker je pretok manjˇsi ali enak od kapacitet vseh prerezov. 2 Zapisani izrek ni toliko pomemben za praktiˇcno raˇcunanje kot za teoretiˇcno razlago problema, ker ga uvrˇsˇca v druˇzino problemov, pri katerih poznamo podobne, tim. izreke vrste min-max. V naˇsem primeru ga preprosto predstavljamo kot zanimivost. 5.2.2 Algoritem za maksimalni pretok ˇ Ceprav je moˇzno algoritem za maksimalni pretok realizirati tako, da na vsakem koraku poiˇsˇce najveˇcji moˇzen prirastek pritoka, se bomo v tem delu zadovoljili z algoritmom, ki nima te lastnosti. Algoritem je prikazan na sliki 5.3. V postopku vozliˇsˇca opremimo z oznakami, ki imajo (pri vozliˇsˇcu j) obliko (i+ , δj ) ali (i− , δj ). (i+ , δj ) pomeni, da obstaja nezasiˇcena pot od izvora do vozliˇsˇca j, po kateri lahko pretok poveˇcamo za prirastek δj in je (pozitivna) povezava hi, ji zadnja na tej poti. (i− , δj ) pa pomeni, da je zadnja povezava na poti od izvora do vozliˇsˇca j povezava hj, ii (in je torej negativna). Na zaˇcetku je le izvor (vozliˇsˇce 1) oznaˇcen s posebno oznako (−, ∞), ki se med celotnim postopkom ne spreminja, nato oznaˇcujemo ostala vozliˇsˇca na en izmed dveh naˇcinov: 116 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE Vhod: Seznam kapacitet cij povezav grafa G = hV, Ei, pri ˇ cemer je V = {1, 2, . . . , n}. Privzetek je, da je vozliˇsˇ ce 1 izvor, vozliˇsˇ ce n pa ponor ter da so vse kapacitete celoˇstevilˇ cne. Izhod: Maksimalna vrednost celotnega pretoka od izvora do ponora ter vrednosti pretokov xij po posameznih povezavah, pri 1 ≤ i, j ≤ n, i 6= j Postopek: 1. (Zaˇ cetek) Zaˇ cnemo z nekim moˇ znim celoˇstevilˇ cnim pretokom x, v skrajnem primeru takim, pri katerem so pretoki skozi vse povezave enaki niˇ c. Viru (vozliˇsˇ cu 1) priredimo stalno oznako (−, ∞), ostala vozliˇsˇ ca nimajo oznak. 2. (Oznaˇ cevanje in obiskovanje) (a) Poiˇsˇ cemo neko oznaˇ ceno, a neobiskano vozliˇsˇ ce i; ˇ ce takega vozliˇsˇ ca ni, nadaljujemo s korakom 4; (b) Pri vsaki povezavi hi, ji, za katero velja xij < cij in je j neoznaˇ ceno, priredimo vozliˇsˇ cu j oznako (i+ , δj ), tako da je δj = min{δi , cij − xij }. Pri vsaki povezavi hj, ii, pri kateri velja xij > 0 in je j neoznaˇ ceno, priredimo vozliˇsˇ cu j oznako (i− , δj ), tako da je δj = min{δi , xji }. (c) V primeru, ko je ponor (vozliˇsˇ ce n) oznaˇ cen, nadaljujemo s korakom 3, sicer ponovimo korak 2 ; 3. (Veˇ canje pretoka) Nezasiˇ ceno pot odkrijemo tako, da zaˇ cnemo pri ponoru in uporabljamo prve komponente oznak. Na primer, prva komponenta oznake pri ponoru nam odkrije predzadnje vozliˇsˇ ce na poti, prva komponenta oznake le-tega nam odkrije predpredzadnje vozliˇsˇ ce itn. Pretok poveˇ camo tako, da poveˇ camo ali zmanjˇsamo pretok skozi povezave na nezasiˇ ceni poti, odvisno od znaka prve komponente oznake. Nadaljevanje pri koraku 2. 4. (Doloˇ canje prereza z minimalno kapaciteto) V tem primeru je trenutni pretok maksimalen. Prerez z minimalno kapaciteto hS, T i dobimo tako, da postavimo vsa oznaˇ cena vozliˇsˇ ca v S, neoznaˇ cena pa v T . S tem smo izraˇ cun zakljuˇ cili. Slika 5.3: Algoritem za maksimalni pretok 5.3. LINEARNO PROGRAMIRANJE 117 ˇ je vozliˇsˇce i oznaˇceno in obstaja povezava hi, ji, kjer velja xij < cij , lahko Ce nezaznamovano vozliˇsˇce j opremimo z oznako (i+ , δj ), pri ˇcemer je δj = min{δi , cij − xij }. ˇ je vozliˇsˇce i oznaˇceno in obstaja povezava hj, ii, kjer velja xji > 0, lahko Ce nezaznamovano vozliˇsˇce j opremimo z oznako (i− , δj ), pri ˇcemer je δj = min{δi , xji }. V primeru, ko procedura zaznamuje ponor n, smo odkrili nezasiˇceno pot in je ˇ se procedura ustavi, ne da bi oznaˇcila moˇzno vrednost pretoka poveˇcati za δn . Ce ponor, so vse poti zasiˇcene. V tem primeru je moˇzno dobiti prerez z minimalno kapaciteto hS, T i tako, da v S postavimo vsa vozliˇsˇca, ki so oznaˇcena, v T pa ostala vozliˇsˇca. Zaznamovano vozliˇsˇce je bodisi “obiskano” ali “neobiskano”. Vozliˇsˇce obiˇsˇcemo tako, da preiˇsˇcemo vse povezave, ki izhajajo iz vozliˇsˇca ali prihajajo vanj in opremimo vozliˇsˇca, ki se dotikajo teh povezav, z oznakami v primeru, ko je to mogoˇce in ko oznak ˇse nimajo. Pri algoritmu moramo najprej dokazati njegovo pravilnost, ki je podana s pogojem, da kadar se algoritem ustavi po koraku 4, je vrednost pretoka v maksimalna. Dejansko: ko pridemo do koraka 4 po koraku 2a, ponor ni oznaˇcen in predstavlja mnoˇzica oznaˇcenih vozliˇsˇc (ter ustrezna komplementarna mnoˇzica) prerez, iz katerega vodijo same zasiˇcene povezave (kajti ˇce neka povezava ne bi bila zasiˇcena, bi se postopek oznaˇcevanja nadaljeval). Torej je v tem primeru celotni pretok enak kapaciteti tega prereza in nam posledica 5.2 zagotavlja, da je pretok maksimalen. Potrebno ˇstevilo operacij algoritma lahko ocenimo takole: naj bo m ˇstevilo povezav. Vsega skupaj moramo pri vsakem iskanju nezasiˇcene poti 2m-krat pregledati povezave ter nato morebiti ˇse opraviti oznaˇcevanje2 . V primeru, ko so vse kapacitete celoˇstevilˇcne, moramo ponoviti iskanje nezasiˇcene poti najveˇc v-krat, kjer je v vrednost maksimalnega pretoka. Torej je ˇstevilo operacij O(mv). 5.3 5.3.1 Linearno programiranje Uvod Pri problemu maksimalnega pretoka skozi graf smo omenili, da je le poseben primer ˇsirˇsega razreda problemov, ki jim pravimo problemi linearnega programiranja. Slednji razred problemov je izredno pomemben iz dveh razlogov: prviˇc, mnogi praktiˇcni problemi se dajo prevesti na obliko linearnega programiranja in drugiˇc, za ta razred problemov poznamo uˇcinkovit algoritem. V tem delu se bomo seznanili z nekaterimi osnovnimi pojmi ter v osnovnih obrisih spoznali simpleksni algoritem. Izhajamo iz ndimenzionalnega vektorskega prostora nad obsegom K, za katerega pa ne privzemamo 2 V najslabˇ sem primeru je potrebno vsako povezavo obravnavati dvakrat: enkrat ob obisku njenega izvora, drugiˇ c pa ob obisku njenega ponora. 118 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE kakih posebnih lastnosti (kot smo to storili, na primer, v razdelku 3.3 o diskretni Fourierjevi transformaciji). Prostor bomo oznaˇcevali z Vn . Torej so njegovi elementi n-terke x elementov K. Problem linearnega programiranja opiˇsemo z m × n matriko A, s stolpˇcnim vektorjem b dolˇzine m ter vrstiˇcnim vektorjem c dolˇzine n, naloga pa je poiskati x ∈ Vn , z lastnostma A · x ≤ b in c · x je maksimalno. A · x predstavlja produkt matrike A in stolpˇcnega vektorja x, c · x pa skalarni produkt c in x(3 ). Funkciji f = c · x pravimo tudi ciljna funkcija problema. Linearno programiranje obravnava zelo obseˇzna literatura. Naj omenimo le dve deli: Vidav [15, 2. poglavje, str. 135] in Kemeny [7, 7. poglavje, str. 314]. Priˇcujoˇci sestavek se ne drˇzi strogo nobenega od citiranih del, predvsem pa se ne dotikamo ˇstevilnih vpraˇsanj, ki so pomembna za praktiˇcno uporabo linearnega programiranja. Na primer, ne omenjamo zveze med osnovnim in dualnim problemom in se ne spuˇsˇcamo v primer, ko problem degenerira. 5.6 Primer Primerov problemov linearnega programiranja je brez ˇstevila, tipiˇcen pa je naslednji: imamo n izdelkov, vsak izmed katerih zahteva na enoto izdelka doloˇceno koliˇcino vsake od m surovin, prinaˇsa pa tudi na enoto izdelka doloˇcen dobiˇcek. Vsaka surovina je na razpolago v omejeni koliˇcini, postavlja pa se vpraˇsanje, koliko kosov vsakega izdelka izdelati, da bo dobiˇcek najveˇcji. Na primer, neki izdelovalec kartonskih ˇskatel izdeluje dve vrsti ˇskatel, majhne in velike. Vsaka majhna ˇskatla porabi 0.5m2 kartona in prinaˇsa 1 SIT dobiˇcka, vsaka velika ˇskatla pa porabi 0.75m2 kartona in ˇ je na razpolago 100m2 kartona, koliko ˇskatel vsake vrste prinaˇsa 2 SIT dobiˇcka. Ce naj izdela, da bo dobiˇcek ˇcim veˇcji? Uporabljamo tudi privzeti pogoj, da je koliˇcina obeh vrst ˇskatel nenegativna. Koliˇcino majhnih in velikih ˇskatel oznaˇcujemo z x1 in x2 , po vrsti. Torej lahko zapiˇsemo: −x1 0.5x1 −x2 +0.75x2 ≤ 0 ≤ 0 ≤ 100, (5.8) pri ˇcemer mora biti x1 + 2x2 (5.9) maksimalno. V matriˇcnem zapisu lahko zapiˇsemo problem takole: # " # " 0 −1 0 0 0.5 −1 0.75 ·x≤ 0 100 , 1 2 · x maksimalno. (5.10) V nadaljnjem besedilu bomo obravnavali naslednja vpraˇsanja: prviˇc, kakˇsna je mnoˇzica toˇck x prostora Vn , ki izpolnjujejo pogoj Ax ≤ b, drugiˇc, kako opiˇsemo tiste tudi soroden problem, ki zahteva, da poiˇsˇ cemo v ∈ Vn∗ tako, da velja vA ≥ c in je b · v ∗ minimalno, pri ˇ cemer je Vn dualen vektorski prostor, v in c sta vrstiˇ cna vektorja, b pa je stolpˇ cni vektor. Vendar se v tem sestavku ne bomo spuˇsˇ cali do takih podrobnosti, ker presegajo zastavljeni okvir. 3 Obstaja 5.3. LINEARNO PROGRAMIRANJE 119 toˇcke mnoˇzice, kjer funkcija c · x doseˇze maksimum in tretjiˇc, kako te toˇcke uˇcinkovito poiˇsˇcemo. 5.3.2 Konveksne poliedrske mnoˇ zice Naj bo a vrstiˇcni vektor dolˇzine n, b pa naj pripada K (torej je konstanta). Tedaj pravimo mnoˇzici toˇck x prostora Vn , ki izpolnjujejo pogoj a · x = b, hiperravnina prostora Vn . Pri n = 2 je hiperravnina premica, pri n = 3 je ravnina, itn. Mnoˇzici toˇck x prostora Vn , ki izpolnjujejo pogoj a · x ≤ b, pravimo zaprt polprostor Vn (odprt polprostor je doloˇcen s pogojem a · x < b), hiperravnini a · x = b pa pravimo mejna hiperravnina zaprtega polprostora a · x ≤ b. 5.7 Primer Oglejmo si (5.8) iz primera 5.6. Prva relacija ustreza zaprtemu polprostoru, ki vsebuje ordinatno os in vse, kar je desno od nje, druga relacija ustreza zaprtemu polprostoru, ki vsebuje abscisno os in vse, kar je nad njo, tretja pa ustreza zaprtemu polprostoru, ki vsebuje premico 0.5x1 + 0.75x2 = 100 in vse, kar je pod njo. Na podlagi dosedanjih definicij lahko takoj zapiˇsemo: 5.8 Trditev Mnoˇzica toˇck x prostora Vn , ki izpolnjujejo pogoj Ax ≤ b, kjer je A neka m × n matrika, b pa je stolpˇcni vektor dolˇzine m, je presek najveˇc m zaprtih polprostorov. V treh dimenzijah pravimo telesu, ki je omejeno z ravninami, polieder in zato mnoˇzici toˇck iz trditve 5.8 pravimo poliederska mnoˇzica tudi, kadar je n 6= 3. Mnoˇzica toˇck x prostora Vn , ki izpolnjujejo pogoj Ax ≤ b ima naslednjo pomembno lastnost: 5.9 Trditev Naj velja Ax1 ≤ b in Ax2 ≤ b. Tedaj velja tudi, A((1 − λ)x1 + λx2 ) ≤ b, pri vseh vrednostih 0 ≤ λ ≤ 1. Za dokaz gl. vajo 7. Sedaj se spomnimo, da je premica, ki vsebuje x1 in x2 , mnoˇzica toˇck, ki so odvisne od parametra λ in jih opisuje enaˇcba, x = x1 + λ(x2 − x1 ), pri − ∞ < λ < +∞. (5.11) Torej leˇzijo toˇcke (1 − λ)x1 + λx2 , pri 0 ≤ λ ≤ 1, na daljici med x1 in x2 in trditev 5.9 pravi, ˇce dve toˇcki izpolnjujeta pogoj Ax ≤ b, ga izpolnjujejo tudi vse toˇcke na daljici med njima. Za mnoˇzico, kjer velja ta lastnost, pravimo, da je konveksna. Sedaj je tudi oˇcitno, zakaj mnoˇzicam toˇck x, ki izpolnjujejo neki pogoj Ax ≤ b, pravimo konveksne poliederske mnoˇzice, oziroma s kratico KPM. (Vpraˇsanje: v ravnini predstavite poliedersko mnoˇzico, ki NI konveksna.) 120 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE toˇ cke,ki izpolnjujejo pogoj x2 −x1 200 0.5x1 100 100 200 −x2 +0.75x2 ≤ ≤ ≤ 0 0 100 x1 Slika 5.4: Konveksna poliedrska mnoˇzica 5.10 Primer V primerih 5.6 in 5.7 je KPM, ki izpolnjuje pogoj Ax ≤ b, notranjost trikotnika, ki je omejen z abscisno in ordinatno osema in premico 0.5x1 +0.75x2 = 100 (gl. sliko 5.4). V zvezi s tem primerom je morda koristno poudariti, da je vˇcasih KPM, ki izpolnjuje pogoj Ax ≤ b, prazna. Na primer v primeru, ki ga obravnavamo, ˇce bi dodali ˇse pogoj +x1 + x2 ≤ −1, bi bila ustrezna KPM prazna. Na tem mestu je koristno vpeljati ˇse neko dodatno znaˇcilnost konveksnih poliedrskih mnoˇzic. Razlikovali bomo med omejeno KPM in neomejeno KPM. Seveda je neomejena KPM taka, ki je “neskonˇcna”, vendar, kako to povedati na primeren naˇcin? Najlaˇze to povemo tako, da istovetimo neomejene KPM s takimi KPM, ki vsebujejo nek poltrak. Na primer v primeru 5.7, ˇce odpravimo omejitev 0.5x1 + 0.75x2 = 100, dobimo KPM, ki vsebuje (na primer) poltrak, ki se zaˇcenja pri izhodiˇsˇcu in ima pozitivni kot 45◦ . Pravkar smo videli, kako je videti neka konveksna poliedrska mnoˇzica v dveh dimenzijah. V treh dimenzijah je to “izboˇcen” polieder. V prostorih, ki imajo dimenzijo veˇc kot tri, pa si takih mnoˇzic veˇc ne znamo predstavljati, ˇceprav seveda matematiˇcno eksistirajo. Pri n = 2 smo ugotovili, da so mejne hiperravnine pravzaprav premice in v tem primeru pravimo toˇckam, ki pripadajo po dvema mejnima hiperravninama hkrati, obenem pa pripadajo tudi KPM, ekstremne toˇcke. Drugi pogoj (pripadnost KPM) je pomemben, ker ni nujno, da se dve mejni hiperravnini vedno sekata v toˇcki, ki pripada KPM. Na primer, v primeru 5.7 so ekstremne toˇcke (0, 0), (0, 132.3) in (200, 0). V sploˇsnem primeru je ekstremna toˇcka neke KPM tista toˇcka x, ki izpolnjuje pogoja, prviˇc Bx = bB , kjer je B neka n × n podmatrika matrike A, bB pa je ustrezni podvektor vektorja b in, drugiˇc, Ax ≤ b. V primeru 5.7 ekstremne toˇcke (0, 0), (0, 132.3) in (200, 0) ustrezajo naslednjim podmatrikam B in podvektorjem bB (po vrsti): h i h i h i h i h −1 0 0 −1 0 −1 0.5 0.75 x= i x= 0 h 0 0 100 ; i −1 0.5 0 0.75 x= 0 100 ; (5.12) V primeru neomejene KPM dodamo h seznamu ekstremnih toˇck ˇse “neskonˇcno toˇcko” zato, da lahko doloˇcene trditve izrazimo bolj jedrnato. ˇ vzamemo neko poljubno n × n podmatriko B matrike A, seveda ni nujno, da Ce ima sistem Bx = bB reˇsitev. Pogoj za obstoj reˇsitve je, da je matrika B nesingularna, 5.3. LINEARNO PROGRAMIRANJE 121 oziroma, da je njena determinanta, |B|, razliˇcna od niˇc. Lahko bi rekli, da je v primeru |B| = 0 reˇsitev Bx = bB toˇcka v neskonˇcnosti. Zaradi enostavnosti se bomo v bodoˇce omejili na primere, ko tak primer ne nastopi, torej odslej privzemamo, da je poljubna n × n podmatrika matrike A nesingularna. Naslednji privzetek se nanaˇsa na velikost matrike A. Odslej k matriki A vedno dodamo ˇse pogoje −xi ≤ 0, s ˇcimer se omejimo ˇ na pozitivni “kvadrant” prostora Vn (in obenem doseˇzemo, da je m ≥ n). Ceprav se ne bomo spuˇsˇcali v podrobnosti, naj pripomnimo, da nas slednji pogoj pravzaprav ne omejuje preveˇc, saj lahko ponavadi s preprostimi transformacijami spremenimo problem, kjer −xi ≤ 0 ne velja, v takega, kjer so ti pogoji izpolnjeni. 5.3.3 Maksimum linearne funkcije na konveksni poliedrski mnoˇ zici Preden zaˇcnemo obravnavati vpraˇsanje iz naslova razdelka, zapiˇsimo neko oˇcitno lastnost konveksnih poliederskih mnoˇzic: 5.11 Trditev Naj bo K KPM prostora Vn z matriko A in vektorjem omejitev b in naj bo a = ha1 , a2 , . . . , an i neka vrstica A, b pa ustrezna komponenta b. Tedaj je mnoˇzica toˇck, ki izpolnjujejo pogoj a · x = b (namesto a · x ≤ b), neka KPM K0 prostora Vn−1 , ekstremne toˇcke K0 pa ustrezajo doloˇcenim ekstremnim toˇckam K. Dokaz. Na podlagi relacije a · x = b izrazimo xn z xi pri 1 ≤ i ≤ n − 1. Na ta naˇcin spremenimo relacijo Ax ≤ b v A0 x0 ≤ b0 , kjer A0 nima vrstice a · x = b, ostali a −a elementi postanejo a0ij = ijan j , omejitve pa b0i = bi − abni . Tako smo ˇze dokazali, da toˇcke, ki zadoˇsˇcajo pogoju a · x = b, tvorijo KPM K0 prostora Vn−1 . Preslikavo med ekstremnimi toˇckami K0 in ekstremnimi toˇckami K nato dobimo na podlagi ugotovitve, da so ektremne toˇcke K0 doloˇcene z naborom n − 1 vrstic matrike A0 , vsak tak nabor pa ustreza enemu samemu naboru n vrstic matrike A (ki doloˇca neko ekstremno toˇcke K) tako, da na matriki A0 uporabimo nasprotno transformacijo od tiste, s katero smo A spremenili v A0 , in dodamo vrstico a. 2 Naj bo podana neka KPM prostora Vn . Iˇsˇcemo toˇcko ali toˇcke, kjer funkcija c · x doseˇze maksimum. Pravzaprav nas bo zanimala funkcija c · x + δ, kjer je δ neka poljubna konstanta, vendar ˇclen δ ne igra bistvene vloge. Reˇsitev zastavljenega problema daje naslednji izrek: 5.12 Izrek (o maksimumu ciljne funkcije na KPM) Naj bo dana neka neprazna KPM K, ki je doloˇcena z m × n matriko A in vektorjem omejitev b ter neka poljubna linearna funkcija c · x + δ. c · x + δ doseˇze svoj maksimum v neki ekstremni toˇcki K. Dokaz. Dokaz bomo izpeljali z indukcijo po n. Pri n = 1 se vse neenaˇcbe prevedejo na obliko ai1 x1 ≤ bi . Vsaka neenaˇcba ustreza nekemu poltraku, njihov presek, ˇce 122 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE je neprazen, pa je bodisi konˇcen zaprt interval ˇstevilˇcne preme x1 (torej mnoˇzica vrednosti b1 ≤ x1 ≤ b2 ) ali nek poltrak (a1 x1 ≤ b1 ). Primer vidimo na sliki 5.5. V tem primeru ima linearna funkcija obliko c1 x1 +δ in na ˇstevilˇcni premi je bodisi konstantna, monotono naraˇsˇca ali pa pada. Torej svoj maksimum doseˇze v eni ekstremni toˇcki (ki je lahko tudi neskonˇcna). −x ≤ −3 x≤7 −x ≤ −3 ∧ x ≤ 7 Pri n > 1 bomo dokazali, da eksistira neka ekstremna toˇcka x ∈ K, za katero pri vseh toˇckah y ∈ K velja c·x+δ ≥ c·y+δ. Privzeli bomo, da obstaja neka toˇcka x1 ∈ K z lastnostjo Ax1 < b (vse komponente ˇ Ax1 so strogo manjˇse od ustreznih komponent b). Ce take toˇcke ni, velja vsaj za eno omejitev a·x1 = b (a je neka vrstica A) in lahko eno komponento x1 izrazimo z drugimi ter primer prevedemo na niˇzjo dimenzijo, za katero smo izrek ˇze dokazali. Torej velja Ax1 < b. Sedaj izberemo poljubno premico, ki vsebuje toˇcko x1 . Presek premice in K je KPM K0 dimenzije 1 (gl. Slika 5.5: Presek dveh konve- nalogo 9). Na K0 doseˇze c·x+δ svoj maksimum na eni ksnih poliedrskih mnoˇzic di- od svojih dveh ekstremnih toˇck, npr. x2 (na podlagi menzije ena induktivne predpostavke). Ker pa x2 pripada vsaj eni mejni hiperravnini KPM K, velja v toˇcki x2 tudi ax2 = b vsaj za eno vrstico a matrike A. Iz trditve 5.11 sledi, da x2 pripada KPM K00 , ki ima manjˇso dimenzijo od K, ekstremne toˇcke K00 pa ustrezajo doloˇcenim ekstremnim toˇckam K. Sedaj ponovno uporabimo induktivno predpostavko in lahko sklepamo, da eksistira neka ekstremna toˇcka x3 ∈ K, tako da velja c · x3 + δ ≥ c · x2 + δ ≥ c · x1 + δ. Torej smo v vseh primerih dokazali, da toˇcki x1 ustreza neka ekstremna toˇcka x3 z lastnostjo c · x3 + δ ≥ c · x1 + δ. 2 Na koncu ˇse ugotovimo, da ima KPM K konˇcno mnogo ekstremnih toˇck, in sicer m najveˇc . n Izrek 5.12 nam ˇze ponuja algoritem za probleme linearnega programiranja (z omejitvami, ki smo jih opisali na koncu razdelka 5.3.2): ˇce je podana KPM z m × n matriko A, vektorjem omejitev b ter ciljno funkcijo c · x, preprosto poiˇsˇcemo vrednost ciljne funkcije v vseh ekstremnih toˇckah in izberemo tisto, v kateri je vrednost najveˇcja. Vendar je ta algoritem razmeroma neuˇcinkovit, saj je obseg raˇcunanja za iskanje ekstremnih toˇck nesprejemljivo velik. V naslednjem razdelku se bomo ukvarjali s vpraˇsanjem, kako lahko izraˇcun znatno pohitrimo. 123 5.3. LINEARNO PROGRAMIRANJE 5.3.4 Lokalni pogoj za globalni maksimum linearne funkcije na konveksni poliedrski mnoˇ zici Naj bo K KPM prostora Vn z matriko A in vektorjem omejitev b in naj bo xB neka ekstremna toˇcka mnoˇzice K. Kot ˇze vemo, je xB reˇsitev sistema linearnih enaˇcb B · x = bB , kjer je B neka n × n podmatrika matrike A, bB pa vektor omejitev, ki pripadajo vrsticam B. Ekstremna toˇcka xC je soseda toˇcke xB v primeru, ko je toˇcka xC reˇsitev sistema C · x = bC , kjer novi sistem enaˇcb dobimo iz B · x = bB tako, da zamenjamo eno samo enaˇcbo ax = b (z neko drugo enaˇcbo med enaˇcbami Ax = b). Denimo na primer, da zamenjamo enaˇcbo a = b z a0 = b0 . Najprej si zastavimo vpraˇsanje, kakˇsna je predstavitev premice, ki povezuje xB in xC ? Dobimo jo na ˇ xB in xC zadoˇsˇcata pogojema preprost naˇcin iz sploˇsne enaˇcbe premice (5.11). Ce Bx = bB in Cx = bC po vrsti, zadoˇsˇcajo toˇcke na premici, ki povezuje ti dve toˇcki, pogoj [(1 − λ)B + λC] x = (1 − λ)bB + λbC . Ker se B in C razlikujeta le v eni vrstici, bB in bC pa v ustrezni komponenti, se tudi ˇ pri (1 − λ)B + λC = (1 − λ)bB + λbC pojavlja parameter λ le v tisti vrstici. Ce vrstici a pripadajo komponente ai , 1 ≤ i ≤ n (in podobno za a0 ), so torej elementi (1 − λ)B + λC v vrstici, ki je odvisna od λ, (1 − λ)ai + λa0i , 1 ≤ i ≤ n. Podobno velja za vektor omejitev (1 − λ)bB + λbC . Nato dokaˇzemo: 5.13 Trditev Vsaka ekstremna toˇcka KPM K prostora Vn z matriko A in vektorjem omejitev b ima najveˇc n sosedov. V kolikor je ˇstevilo sosedov n − k, ustreza k manjkajoˇcih sosedov neskonˇcni ekstremni toˇcki. Dokaz. Naj bo ekstremna toˇcka xB in naj velja BxB = bB . Ne da bi se omejili, lahko privzamemo, da je matrika A enaka B A= , B0 vektor omejitev b pa b= bB b0 . Sedaj vpeljemo nove koordinate y na podlagi relacije x − xB = B −1 y in se nato relacija, Ax ≤ b, spremeni v, B B0 (B −1 bB b0 0 b0 − B 0 xB y + xB ) ≤ , oziroma, I B 0 B −1 y≤ . 124 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE Z drugimi besedami, B smo transformirali v enotsko matriko, ekstremno toˇcko xB v izhodiˇsˇce koordinatnega sistema, K pa premaknili v “negativni kvadrant” Vk . Sedaj prehod od trenutne ekstremne toˇcke (ki ji pripada enotska n × n matrika) do sosednje opravimo tako, da neko enaˇcbo yi = 0 zamenjamo z enaˇcbo, katere leva stran je ena izmed vrstic B 0 B −1 y, desna pa ustrezna komponenta b0 − B 0 xB . Vendar, ker so vse enaˇcbe yj = 0, j 6= i, ostale v veljavi, se spremenjena vrstica pretvori v ai yi = bi in tako postane nova ekstremna toˇcka, yi = abii , yj = 0, j 6= i; torej se je daljica med dvema ekstremnima toˇckama spremenila v eno izmed koordinatnih osi. Ker je koordinatnih osi n, je seveda ˇstevilo ekstremnih toˇck ≤ n. Seveda lahko z razliˇcnimi zamenjavami dobimo razliˇcne enaˇcbe yi = abii , vendar ekstremno toˇcko dobimo le v primeru, ko toˇcka pripada K, to pa velja le za vrednost abii , ki leˇzi najbliˇzje izhodiˇsˇcu novega koordinatnega sistema. Lahko se tudi zgodi, da je ai = 0. V tem primeru pripada mnoˇzici K celotna koordinatna os spremenljivke yi , in imamo opravka z neskonˇcno sosedno ekstremno toˇcko. 2 Potem, ko smo uporabili opisano koordinatno transformacijo, je determinanta podmatrike, ki doloˇca ekstremno toˇcko, produkt diagonalnih elementov podmatrike (v primeru toˇcke xB smo transformiraliii B v I in je torej determinanta kar 1). Ko na opisan naˇcin iz I dobimo C, je v primeru ai = 0 determinanta C enaka 0, z drugimi besedami C je singularna. Torej ustrezajo neskonˇcnim sosednim ekstremnim toˇckam singularne n × n podmatrike matrike A in ker smo privzeli, da takih ni, smo se tudi obvarovali pred neskonˇcnimi ekstremnimi toˇckami. Sedaj pa osnovni rezultat tega razdelka: 5.14 Izrek (o lokalnem pogoju za globalni maksimum) Naj bodo dani: neka KPM K prostora Vn z matriko A in vektorjem omejitev b, neka linearna funkcija f = c · x + δ ter neka n × n podmatrika B matrike A in ustrezna ekstremna toˇcka ˇ je vrednost f v vseh xB , ki predstavlja reˇsitev sistema linearnih enaˇcb B · x = bB . Ce sosednih ekstremnih toˇckah manjˇsa ali enaka vrednosti f v toˇcki xB , potem f v xB doseˇze svoj globalni maksimum na mnoˇzici K. Dokaz. Izrek dokaˇzemo tako, da ponovno uporabimo koordinatno transformacijo, ki smo jo uporabili v dokazu trditve 5.13. Po uporabi transformacije x − xB = B −1 y se ciljna funkcija transformira v c(B −1 ·y +xB ) +δ. V trditvi 5.13 smo ugotovili, da ima transformacija, ki jo uporabljamo, lastnosti (a) vse dovoljene vrednosti spremenljivk yi , 1 ≤ i ≤ n, so nepozitivne in (b) do sosednih ekstremnih toˇck pridemo tako, da ˇ sedaj velja pogoj iz izreka, da v nobeni opravimo premik po neki koordinatni osi. Ce sosednji ekstremni toˇcki vrednost ciljne funkcije ni veˇcja od vrednosti c · xB + δ, sledi, da so vse komponente nove predstavitve koeficientov ciljne funkcije, c · B −1 nenegativne. V takem primeru pa tudi nobena linearna kombinacija dovoljenih vrednosti spremenljivk (in vsaka toˇcka K ima tako lastnost) ne more dati veˇcje vrednosti ciljne funkcije od c · xB + δ. 2 125 5.3. LINEARNO PROGRAMIRANJE x1 +4 +1 +2 +1 x2 +1 +5 +3 +1 b +5 +20 +8 0 y1 y2 y3 −f Slika 5.6: Zaˇcetna simpleksna tabela. Izrek 5.14 nas napeljuje na naslednji algoritem za reˇsitev problemov linearnega programiranja: postavimo se v neko ekstremno toˇcko in ponavljamo zanko, v kateri se vsakiˇc premaknemo do neke sosedne ekstremne toˇcke, kjer je vrednost ciljne funkcije veˇcja; ˇce take ekstremne toˇcke ni, se ustavimo in smo na podlagi izreka 5.14 naˇsli globalni maksimum ciljne funkcije. 5.3.5 Simpleksni algoritem V tem razdelku bomo nekoliko bolj podrobno opisali algoritem, ki smo ga nakazali na koncu prejˇsnjega razdelka. Pri opisu algoritma bomo uporabljali naslednji primer: +4x1 +1x1 +2x1 +1x2 +5x2 +3x2 ≤ 5 ≤ 20 ≤ 8, (5.13) pri ˇcemer mora biti x1 + x2 (5.14) maksimalno. Pogojem (5.13) moramo seveda ˇse pripisati pogoj nenegativnosti vseh spremenljivk. Uporabili bomo metodo, ki nekoliko spominja na dokaz izreka 5.14: uporabili bomo nek koordinatni sistem, kjer prehod do sosedne ekstremne toˇcke doseˇzemo s spremembo dveh koordinat (in ne ene kot v dokazu izreka 5.14), vendar ker bo ciljna funkcija odvisna le od ene izmed teh koordinat, bo zato takoj moˇzno odkriti smer njenega spreminjanja. Natanˇcen opis postopka pa je naslednji: dodali bomo m spremenljivk yi , pri 1 ≤ i ≤ m, in vsak pogoj ai · x ≤ bi spremenili v ai · x + yi = bi , pri ˇcemer je bi komponenta b, ki ustreza vrstici ai matrike A. Na ta naˇcin smo A podaljˇsali v desno z m × m enotsko matriko, vse neenakosti pa smo spremenili v enakosti. Poleg pogojev, ki so izraˇzeni s spremenjeno matriko A, imamo ˇse implicitne pogoje yi ≥ 0, 1 ≤ i ≤ m, in xj ≥ 0, 1 ≤ j ≤ n. Vsega skupaj imamo torej n + m spremenljivk in n + 2m pogojev. Za zaˇcetno ekstremno toˇcko izberemo toˇcko s komponentami yi = bi , pri 1 ≤ i ≤ m, in xj = 0, pri 1 ≤ j ≤ n. Tej ekstremni toˇcki ustreza m pogojev Ax + y = b ter n pogojev x = 0. Sedaj opiˇsemo osnovni korak algoritma, s katerim poveˇcamo vrednost ciljne funkcije. V tem koraku zamenjamo enega izmed opisanih pogojev z nekim pogojem yi = 0, 1 ≤ i ≤ m, ki ni bil vsebovan v opisu trenutne ekstremne toˇcke. Z nekoliko premisleka ugotovimo, da pogoj yi = 0 lahko nadomesti bodisi pogoj ai · x + yi = bi ali pa enega izmed 126 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE y1 x2 b + 14 − 14 − 24 + 14 + 19 4 + 10 4 + 54 x1 + 75 4 + 22 4 y2 − 14 + 34 − 54 −f y3 Slika 5.7: Prvi korak. pogojev xj = 0, 1 ≤ j ≤ n. Prvi primer ni zanimiv zato, ker se pri nespremenjeni vrednosti spremenljivk xj , 1 ≤ j ≤ n ciljna funkcija ne poveˇca. Torej moramo z yi = 0 nadomestiti enega izmed pogojev xj = 0, 1 ≤ j ≤ n. Izberemo tak pogoj xj = 0, katerega odpravljanje zagotavlja veˇcanje ciljne funkcije. Pri roˇcnem izraˇcunu podatke ponavadi uredimo v tabelo, ki je za primer (5.13) prikazana na sliki 5.6. Stolpci tabele pripadajo vektorju omejitev ter spremenljivkam, ki trenutno imajo vrednost 0 in od katerih je odvisna ciljna funkcija, vrstice pa spremenljivkam, ki so trenutno neniˇcelne (od katerih pa ciljna funkcija ni odvisna). Prehod do sosedne ekstremne toˇcke z viˇsjo vrednostjo funkcije f opravimo tako, da najprej izberemo neko spremenljivko x, ki ji vrednost poveˇcamo, neki spremenljivki y 6= 0 pa priredimo vrednost 0 tako, da ostanejo vsi ostali pogoji izpolnjeni. Spremenljivko x izberemo na podlagi komponent c (spodnje vrstice tabele): izberemo t´ako spremenljivko x, kateri ustreza pozitivna vrednost v vrstici c, kar pomeni, da se pri veˇcanju te spremenljivke veˇca tudi vrednost f . V naˇsem primeru izberemo x1 . x1 bomo toliko poveˇcali, da se bo neka vrednost y ˇ se pri veˇcanju x1 tudi vse vrednosti y veˇcajo, to pomeni, da sosedna spustila do 0. Ce ektremna toˇcka ni konˇcna (v tabeli se to vidi po tem, da so v ustreznem stolpcu vse vrednosti, razen one, ki pripada zadnji vrstici, negativne). V naˇsem primeru to ne velja, saj so koeficienti v prvem stolpcu, ki pripada x1 , 4, 1, 2. Ko smo izbrali spremenljivko x1 , da jo bomo postavili v mnoˇzico neniˇcelnih spremenljivk, pogledamo, katera spremenljivka yi se bo pri veˇcanju x1 prva spustila do 0. To se bo zgodilo z y1 , pri vrednosti x1 = 45 . Sedaj bomo postavili y1 v zgornjo vrstico, x1 pa v desni stolpec in obenem spremenili A z upoˇstevanjem novih razmer. Kakˇsne spremembe so potrebne? Vrstici, ki ustreza y1 , moramo dati novo obliko, ko se x1 pojavlja s koeficientom 1: to doseˇzemo preprosto tako, da celo vrstico delimo z elementom na preseku stolpca x1 in vrstice y1 (temu elementu pravimo pivota ali, slovensko, opora). Naslednja sprememba je, da moramo iz vseh vrstic, ki ustrezajo ostalim y spremenljivkam, izloˇciti spremenljivko x1 . To storimo tako, da od vsake vrstice odˇstejemo novo vrednost v vrstici, ki ustreza x1 , pomnoˇzeno s starim elementom stolpca x1 (ki pripada vrstici, ki jo spreminjamo). Enako storimo z zadnjo vrstico v tabeli, ki vsebuje komponente c. Nova tabela za naˇs primer je prikazana na sliki 5.7. V naslednjem koraku je edina pozitivna komponenta c c2 (za prvo komponento ni presenetljivo, da je manjˇsa od niˇc, saj ustreza poti, po kateri smo priˇsli v trenutno ekstremno toˇcko, 127 5.4. POVZETEK OSNOVNIH POJMOV y1 y3 b 3 + 10 7 + 10 2 − 10 1 − 10 1 − 10 − 19 10 4 + 10 3 − 10 7 + 10 x1 + 83 10 + 22 10 − 29 10 y2 x2 −f Slika 5.8: Drugi korak. in to na podlagi kriterija veˇcanja ciljne funkcije). Na podlagi poskusov ugotovimo, da se z veˇcanjem x2 prva spusti do niˇc spremenljivka y3 , kar pomeni, da bo prav ta spremenljivka zamenjala x2 . Tabela, ki smo jo spremenili na podlagi istih pravil kot prej, je prikazana na sliki 5.8. Celotni simpleksni algoritem je prikazan na sliki 5.9. 5.4 Povzetek osnovnih pojmov 1. Problem maksimalnega pretoka skozi omreˇzje 2. Zakon o ohranjanju pretoka 3. Poti med izvorom in ponorom — pozitivne in negativne povezave 4. Zasiˇcene in nezasiˇcene povezave, oziroma poti 5. Izrek o zasiˇcenih poteh 6. Izrek o celoˇstevilˇcnosti maksimalnega pretoka 7. Izrek o maksimalnem pretoku pri minimalnem prerezu 8. Algoritem za maksimalni pretok 9. Problem linearnega programiranja in primeri 10. Ciljna funkcija 11. Hiperravnina v prostoru Vn 12. Zaprt polprostor v Vn 13. Mejna hiperravnina zaprtega polprostora 14. Konveksnost 15. Konveksna poliedrska mnoˇzica Vn (KPM) 16. Omejena, neomejena KPM 17. Ekstremna toˇcka KPM 18. Linearna funkcija doseˇze svoj maksimum na KPM v neki ekstremni toˇcki 19. Osnovni privzetek, ki ga uporabljamo v naˇsi obravnavi problema linearnega programiranja 128 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE Vhod: m×n matrika A, vektor omejitev b dolˇ zine m in linearna ciljna funkcija f = c·x+δ. Izhod: vrednosti spremenljivk xi , pri 1 ≤ i ≤ n, kjer f doseˇ ze maksimum (ter velikost tega maksimuma). Postopek: 1. Sestavimo zaˇ cetno simpleksno tabelo tako, da elemente A vpiˇsemo v vrstice 1 . . . m in stolpce 1 . . . n. V vrstico m + 1 zapiˇsemo komponente c, v stolpec n + 1 pa komponente b. V element hm + 1, n + 1i zapiˇsemo vrednost −δ. Stolpce poimenujemo x1 , x2 , . . . , xn , b, vrstice pa y1 , y2 , . . . , ym , −f . 2. Izberemo katerikoli stolpec xJ , ki mu ustreza pozitivna komponenta cJ in ˇ takega stolpca ni, se ustavimo, ker smo globalni maksimum nadaljujemo. Ce f ˇ ze dosegli in lahko vrednosti neniˇ celnih spremenljivk odˇ citamo iz stolpca n + 1, negativno vrednost funkcije f pa iz elementa hm + 1, n + 1i. 3. Izberemo tisto vrstico yI , ki ima pozitiven aIJ in mu ustreza najmanjˇsa vreˇ so vse vrednosti aIJ negativne, to pomeni, da f nima konˇ cne dnost abI . Ce IJ maksimalne vrednosti in se ustavimo. 4. Vrstico yI spremenimo tako, da vse elemente, razen aIJ , delimo z aIJ , element aIJ pa nadomestimo z a 1 . IJ 5. Ostale vrstice yi spremenimo tako, da od vseh elementov aij , razen aiJ , a aiJ odˇstejemo Ij ter aiJ zamenjamo z − aaiJ . a IJ IJ 6. Stolpec xJ preimenujemo v yI , vrstico yI pa v xJ in se vrnemo na korak 2. Slika 5.9: Simpleksni algoritem 20. Sosedne ekstremne toˇcke in njihovo ˇstevilo 21. Lokalni pogoj za globalni maksimum linearne funkcije na KPM 22. Simpleksni algoritem. 5.5 Naloge 1. Poiˇsˇcite vse prereze z minimalno kapaciteto v grafu na sl. 5.1. 3mc35 3 Z > ~ l Z 5 2 1m- 2m 5 Z ? > c24 ~ m Z 3 4 Slika 5.10: Graf iz naloge 2. 129 5.5. NALOGE 2. Za graf na sl. 5.10 izrazite vrednost maksimalnega pretoka kot funkcijo c24 in c35 . 3. Mnoˇzico S, ki jo sestavljajo vozliˇsˇca, do katerih vodi nezasiˇcena pot od izvora (vozliˇsˇca 1), lahko definiramo rekurzivno: 1 ∈ S, i ∈ S, xij < cij ⇒ j ∈ S, i ∈ S, xji > 0 ⇒ j ∈ S. Podajte podobno rekurzivno definicijo mnoˇzice T , ki jo sestavljajo vozliˇsˇca, iz katerih vodijo nezasiˇcene poti do ponora (vozliˇsˇca n). 4. Katera izmed naslednjih dveh trditev je resniˇcna? ˇ je x maksimalen pretok, eksistirata i in j, tako da je bodisi xij = 0 ali xji = 0 (a) Ce (b) Obstaja tak maksimalen pretok, pri katerem velja za nek i in j bodisi xij = 0 ali xji = 0. Podajte utemeljitev svojega mnenja. 5. Kritiˇcno povezavo definiramo kot tisto povezavo, katere odstranitev najbolj zmanjˇsa maksimalni pretok. Presodite, ali je naslednja trditev resniˇcna: kritiˇcna povezava je povezava z maksimalno kapaciteto v prerezu minimalne kapacitete. Podajte utemeljitev svojega mnenja. 6. Dokaˇzite trditev 5.9. 7. V primeru 5.6 smo obravnavali primer problema linearnega programiranja. Spremenite zapis problema (matriko A, vektor omejitev b in ciljno funkcijo c), tako da bo ustrezal (a) istemu opisu z dodatno omejitvijo “ˇstevilo majhnih ˇskatel ni manjˇse od ˇstevila velikih ˇskatel”. Nariˇsite geometrijsko predstavo spremenjenega problema. (b) Poleg predhodne omejitve naj velja ˇse “ˇstevilo majhnih ˇskatel ne presega 20”. Tudi za ta primer opiˇsite A, b in c ter nariˇsite geometrijsko predstavo. 8. Konveksno poliedrsko mnoˇzico (KPM) smo opisali kot mnoˇzico elementov x, ki zadoˇsˇcajo pogoju Ax = b. Opiˇsite isto mnoˇzico potem, ko smo v prostoru Vn uporabili neko koordinatno transformacijo. 9. Naj bosta K1 in K2 konveksni poliedrski mnoˇzici prostora Vn . Dokaˇzite, da je njun presek KPM dimenzije, ki je enaka ali manjˇsa od dimenzij K1 in K2 . Napotek: dimenzijo Ki definiramo kot minimalno ˇstevilo r, tako da lahko z neko koordinatno transformacijo predstavimo Ki v obliki A · x ≤ b, pri ˇcemer je A m × n matrika z r neniˇcelnimi stolpci. 10. Problem linearnega programiranja smo opisali z m × n matriko A, (stolpˇcnim) vektorjem b ter (vrstiˇcnim) vektorjem c, potrebno pa je poiskati (stolpˇcni) vektor x tako, da velja Ax ≤ b in c · x je maksimalno. Doloˇcite koliˇcine A, b in c pri problemu maksimalnega pretoka. 130 POGLAVJE 5. PRETOKI IN LINEARNO PROGRAMIRANJE Poglavje 6 ˇ DINAMICNO PROGRAMIRANJE IN ˇ POTI NAJCENEJSE 6.1 Uvod Dinamiˇcno programiranje je pristop k reˇsevanju problemov, ki je soroden z metodo rekurzivnega razcepa, uporablja pa se predvsem za reˇsevanje doloˇcene vrste optimizacijskih problemov. Podobno kot pri rekurzivnem razcepu, tudi v tem primeru nalogo razbijemo na podnaloge, vendar v nasprotju z rekurzivnim razcepom, kjer so podnaloge neodvisne, so lahko pri dinamiˇcnem programiranju medsebojno odvisne. Zaradi tega je potrebno v tem primeru reˇsitve vseh podnalog hraniti, ker se neka podnaloga med reˇsitvijo celotnega problema lahko pojavi veˇckrat. 6.1 Primer Pri urejanju s porazdelitvami kot tipiˇcni nalogi, ki jo reˇsujemo z rekurzivnim razcepom, razdelimo prvotno nalogo na dva dela, nato dva dela reˇsujemo loˇceno, ker reˇsevanje ene podnaloge ni povezano z reˇsevanjem druge. 2 6.2 Primer Kot bomo pa videli kasneje, je tipiˇcna naloga, ki jo reˇsujemo z dinamiˇcnim programiranjem, iskanje najcenejˇsih poti v grafih med dvema izbranima toˇckama. Na sliki 6.1 je prikazan graf, kjer je vsaki povezavi prirejena cena. Naloga ˇ ceno najcenejˇse poti med i in j je poiskati najcenejˇso pot med vozliˇsˇcema 1 in 6. Ce oznaˇcimo z uij , lahko zapiˇsemo u16 = M IN (u14 + c46 , u15 + c56 ). Torej je celotna naloga odvisna od cene najcenejˇsih poti med 1 in 4 ter med 1 in 5. Vendar obe podnalogi vsebujeta nadaljnji podnalogi iskanja cen najcenejˇsih poti med 131 ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 132 c12 c24 2 c13 c46 c34 c25 1 4 3 c35 6 5 c56 Slika 6.1: Graf z uteˇzenimi povezavami 1 in 2 ter med 1 in 3. Zato izraˇcuna u14 in u15 nista neodvisna. 2 6.2 Problem 0-1 nahrbtnika Prvi problem, ki ga bomo reˇsevali z metodo dinamiˇcnega programiranja, je 0-1 nahrbtnik: podano je n predmetov, pri ˇcemer ima i-ti predmet prostornino vi in vrednost ˇ imamo nahrbtnik s prostornino V , se postavlja problem izbire take podci . Ce mnoˇzice predmetov, ki ima najveˇcjo skupno vrednost (vsota vrednosti predmetov v podmnoˇzici), obenem pa po skupni prostornini ne presega V . Torej, Pn xi vi ≤ V, Pi=1 n i=1 xi ci maksimalno, xi ∈ {0, 1}. Problem bomo reˇsili tako, da ga bomo najprej razcepili na podprobleme, nato pa slednje reˇsevali po velikosti (zaˇceli bomo z najmanjˇsim). S ki (W ) bomo oznaˇcili vrednost optimalnega nahrbtnika, ki ima skupno prostornino W , ˇce upoˇstevamo le prvih i predmetov. Bodimo pozorni, da je ki funkcija enega argumenta, katere vrednost je izraˇzena v istih enotah kot vrednosti predmetov, ci . Oˇcitno v tem primeru velja ki (W ) = M AX(ki−1 (W ), ki−1 (W − vi ) + ci ), 1 ≤ i ≤ n, (6.1) pri ˇcemer definiramo, k0 (W ) = −∞ pri W < 0 0 sicer. (6.2) Razlaga enaˇcbe (6.1), ki ji pravimo Bellmanova enaˇcba, je, da moramo pri odloˇcitvi, ali predmet i pripada optimalni podmnoˇzici predmetov, primerjati optimalno vrednost nahrbtnika z upoˇstevanjem i − 1 predmetov, kar ustreza primeru, ko i-ti predmet NI vkljuˇcen v optimalno podmnoˇzico, z optimalno vrednostjo, ko je i-ti predmet vkljuˇcen. Slednjo vrednost dobimo kot vsoto optimalne vrednosti z upoˇstevanjem i − 1 predmetov in prostornino, ki je zmanjˇsana za prostornino i-tega predmeta, in vrednosti i-tega predmeta. Reˇsitev prvotne naloge je kn (V ). 133 6.2. PROBLEM 0-1 NAHRBTNIKA k0 (W ) W Slika 6.2: Stopnica Funkcije ki (W ) raˇcunamo po naraˇsˇcajoˇci vrednosti i, definicija (6.2) pa je posledica enaˇcbe (6.1). Namreˇc potrebno je zagotoviti, da je nemogoˇce dobiti pozitivno vrednost drugega argumenta desne strani (6.1) v primeru W − vi < 0, ne glede na velikost ci . ˇ pri izraˇcunu k1 (W ) stopnico k0 (W ) Funkciji (6.2) pravimo stopnica (gl. sliko 6.2). Ce vstavimo v enaˇcbo (6.1) namesto ki−1 (W ), ustreza desni parameter stopnici, ki je zamaknjena v desno za vi in navzgor za ci (gl. sliko 6.3). Na ta naˇcin dobimo dve stopnici, oziroma po veˇckratni uporabi enaˇcbe (6.1) “vzpenjajoˇce se stopnice” ali, ˇce uporabimo nekoliko bolj uˇcen izraz, odsekoma konstantno, nepadajoˇco funkcijo (OKN funkcijo). Zaradi preproste zgradbe OKN funkcij je tudi raˇcunanje z njimi razmeroma preprosto. OKN funkcijo predstavljamo z zaporedjem parov, ki predstavljajo zamike, pri ˇcemer prvi element para predstavlja zamik po abscisni osi, drugi element pa po ordinatni osi. 6.3 Primer Podani so naslednji podatki n = 4, V = 9 v = (4, 2, 3, 7) c = (5, 2, 3, 9). Iz podatkov izraˇcunamo vrednost optimalnega nahrbtnika in optimalno mnoˇzico. Vrednost raˇcunamo na podlagi enaˇcbe (6.1), pri ˇcemer desni argument funkcije ki oznaˇcujemo z li = ki (W − vi+1 ) + ci+1 . Rezultat izraˇcuna je prikazan v tabeli na sliki 6.4. Na vsakem koraku iz ki−1 ter li−1 izraˇcunamo ki tako, da pare v ki−1 in li−1 uredimo po naraˇsˇcajoˇci vrednosti prve komponente para in pri doloˇceni vrednosti u izberemo par z najveˇcjo vrednostjo v. Drugaˇce povedano: ˇce imamo para (u1 , v1 ) in (u2 , v2 ) in ˇce velja (u2 ≥ u1 ) ∧ (v2 ≤ v1 ), (6.3) lahko (u2 , v2 ) izloˇcimo. Na primer pri izraˇcunu k4 na sliki 6.4 sta izpadla para (7,8) in (9,10). Poleg tega je moˇzno izloˇciti vse pare s prvo komponento veˇcjo od celotne prostornine V . Iz funkcij ki in li je moˇzno tudi sestaviti optimalno podmnoˇzico. V funkciji k4 poiˇsˇcemo par, ki doloˇca vrednost pri celotni prostornini (v naˇsem primeru V = 9). ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 134 k0 (W ) l0 (W ) W (a) Stopnica k1 (W ) W (b) Zamaknjena stopnica (c) Vzpenjajoˇce se stopnice Slika 6.3: Odsekoma konstantne nepadajoˇce funkcije i 0 1 2 3 4 k/l ki oziroma li k (0, 0) l (4, 5) k (0, 0)(4, 5) l (2, 2)(6, 7) k (0, 0)(2, 2)(4, 5)(6, 7) l (3, 3)(5, 5)(7, 8)(9, 10) k (0, 0)(2, 2)(3, 3)(4, 5)(6, 7)(7, 8)(9, 10) l (7, 9)(9, 11) k (0, 0)(2, 2)(3, 3)(4, 5)(6, 7)(7, 9)(9, 11) Slika 6.4: Izraˇcun optimalnega nahrbtnika 6.2. PROBLEM 0-1 NAHRBTNIKA 135 To je (9,11). Nato ta par poiˇsˇcemo v k3 , oziroma l3 . Ker se ta par nahaja v definiciji l3 , sklepamo, da je predmet 4 prisoten v optimalni podmnoˇzici. Ker je par (9,11) nastal iz (2,2), sedaj poiˇsˇcemo (2,2) v k2 , oziroma l2 . Najdemo ga v k2 , kar pomeni, da predmet 3 ni prisoten v optimalni podmnoˇzici. Na podoben naˇcin ugotovimo, da je predmet 2 v optimalni podmnoˇzici, predmet 1 pa ni. Algoritem za reˇsitev problema 0-1 nahrbtnika je prikazan kot popoln program 6.1. Program uporablja tudi zunanji modul Qs, ki realizira dinamiˇcne sezname in ki je prikazan kot program 6.2. Program 6.1: Izraˇ cun optimalnega 0-1 nahrbtnika 1 3 3 5 6 6 7 8 9 10 11 12 13 14 15 16 17 18 20 21 21 22 23 24 26 26 27 28 29 30 31 32 33 34 MODULE Knapsack ; IMPORT In,Qs,Out; TYPE ObjRec = RECORD v ,c:LONGINT END; (∗ podatki o predmetih ∗) PAObjRec = POINTER TO AObjRec ; AObjRec = ARRAY OF ObjRec ; PICNfunc = POINTER TO ICNfunc; ICNfunc = RECORD(Qs.Lifo) vStep,cStep: LONGINT END ; (∗ OKN funkcija ∗) ICNfuncPair = RECORD k ,l :Qs.Lifo END;(∗ k => OKN funkcija; l => zamaknjena OKN funkcija ∗) PAICNfuncPair = POINTER TO AICNfuncPair ; AICNfuncPair = ARRAY OF ICNfuncPair ; PABOOLEAN = POINTER TO ARRAY OF BOOLEAN ; ABOOLEAN = ARRAY OF BOOLEAN ; VAR i,n,V ,optV : LONGINT ; pObjects: PAObjRec; (∗ opisi predmetov ∗) pRes : PAICNfuncPair ; pOptSet: PABOOLEAN ;(∗ optimalna mnoˇzica ∗) PROCEDURE Init; VAR i:LONGINT ;p:PICNfunc; BEGIN In.Open; (∗ preberemo ˇstevilo predmetov in prostorsko omejitev ∗) In.LongInt(n);In.LongInt(V ); NEW (pObjects,n); FOR i:=0 TO n−1 DO In.LongInt(pObjects[i].v ); In.LongInt(pObjects[i].c) se nadaljuje ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 136 Izraˇ cun optimalnega 0-1 nahrbtnika (nadaljevanje) 35 36 37 38 39 40 41 42 43 44 45 47 47 48 49 50 51 53 53 54 55 56 58 58 59 60 61 62 63 64 65 67 67 68 69 70 71 72 74 74 76 76 77 78 79 END; NEW (pRes,n+1); FOR i:=0 TO n DO pRes[i].k .Init;pRes[i].l .Init END; NEW (pOptSet,n); NEW (p); p.vStep:=0; p.cStep:=0; pRes[0].k .Insert(p) END Init; PROCEDURE MkTranslPar (i:LONGINT ); (∗ Izracun zamaknjene OKN funkcije ∗) VAR p:Qs.Lifo; r :Qs.Fifo; q:PICNfunc; BEGIN r .Init; p:=pRes[i].k ; WHILE (p.next # NIL) & (p.next(PICNfunc).vStep+pObjects[i].v <=V ) DO NEW (q); q.vStep:=p.next(PICNfunc).vStep+pObjects[i].v ; q.cStep:=p.next(PICNfunc).cStep+pObjects[i].c; r .Insert(q); p.Step END; pRes[i].l :=r .first (∗ kazalec na seznam parov ∗) END MkTranslPar ; PROCEDURE Merge(i:LONGINT ); (∗ zlivanje OKN funkcije in iste funkcije z zamikom ∗) VAR p,q :Qs.Lifo; plast:PICNfunc; s:Qs.Fifo; r :PICNfunc; PROCEDURE Insert; BEGIN NEW (r ); IF p.next(PICNfunc).vStep<q.next(PICNfunc).vStep THEN r ↑:=p.next(PICNfunc)↑;p.Step ELSIF p.next(PICNfunc).vStep>q.next(PICNfunc).vStep THEN se nadaljuje 6.2. PROBLEM 0-1 NAHRBTNIKA Izraˇ cun optimalnega 0-1 nahrbtnika (nadaljevanje) 80 81 82 83 84 85 86 87 88 89 90 91 92 94 94 95 97 97 98 99 101 101 102 103 105 106 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 r ↑:=q.next(PICNfunc)↑;q.Step ELSIF p.next(PICNfunc).cStep<=q.next(PICNfunc).cStep THEN r ↑:=q.next(PICNfunc)↑; q.Step; p.Step ELSE r ↑:=p.next(PICNfunc)↑; q.Step; p.Step END ; plast:=r ; s.Insert(r ) END Insert; PROCEDURE Skip(p:Qs.Lifo); (∗ zanemarjanje odveˇcnih parov ∗) BEGIN WHILE (p.next#NIL) &(p.next(PICNfunc).vStep>=plast.vStep) &(p.next(PICNfunc).cStep<=plast.cStep) DO p.Step END; END Skip; BEGIN s.Init; p:=pRes[i−1].k ;q:=pRes[i−1].l ; WHILE (p.next # NIL) & (q.next # NIL) DO Insert; Skip(p); Skip(q) END; WHILE p.next # NIL DO NEW (r ); r ↑:=p.next(PICNfunc)↑; p.Step; s.Insert(r ) END; WHILE q.next # NIL DO NEW (r ); r ↑:=q.next(PICNfunc)↑; q.Step; s.Insert(r ) END; se nadaljuje 137 ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 138 Izraˇ cun optimalnega 0-1 nahrbtnika (nadaljevanje) 125 126 128 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 154 154 155 156 157 158 159 160 161 163 164 165 165 167 168 168 169 pRes[i].k :=s.first END Merge; PROCEDURE OptSet; VAR i:LONGINT ; p:Qs.Lifo; q:PICNfunc; optR:ICNfunc; BEGIN IF n>0 THEN p:=pRes[n].k ; REPEAT q:=p.next(PICNfunc); p.Step UNTIL (p.next = NIL) OR (p.next(PICNfunc).vStep > V ); optV :=q.cStep; optR:=q↑; FOR i:=n−1 TO 0 BY −1 DO p:=pRes[i].k ; WHILE (p.next # NIL) &( (p.next(PICNfunc).vStep # optR.vStep) OR (p.next(PICNfunc).cStep # optR.cStep) ) DO p.Step END ; pOptSet[i]:= p.next = NIL ; IF pOptSet[i] THEN optR.vStep:=optR.vStep−pObjects[i].v ; optR.cStep:=optR.cStep−pObjects[i].c END END; Out.Ln;Out.String(“Optimalna vrednost: ”); Out.Int(optV ,10); Out.Ln;Out.String(“Optimalna mnozica:”); FOR i:=0 TO n−1 DO Out.Ln;Out.Int(i+1,2); IF pOptSet[i] THEN Out.String(“: +”) ELSE Out.String(“: −”) END END END END OptSet; BEGIN Init ; FOR i:=1 TO n DO se nadaljuje 6.2. PROBLEM 0-1 NAHRBTNIKA Izraˇ cun optimalnega 0-1 nahrbtnika (nadaljevanje) 170 171 172 174 174 MkTranslPar (i−1); Merge(i) END ; OptSet END Knapsack . Izraˇ cun optimalnega 0-1 nahrbtnika (konec) Program 6.2: Dinamiˇ cni seznami 1 2 3 5 6 7 7 9 9 10 12 12 14 14 16 16 17 19 19 20 21 23 23 24 25 26 27 29 29 30 31 32 34 34 MODULE Qs; (∗ Dinamiˇcni seznami ∗) TYPE PEmpty∗ = POINTER TO Empty; Empty∗ = RECORD END; PLifo∗ = POINTER TO Lifo; PFifo∗ = POINTER TO Fifo; Lifo∗ = RECORD(Empty) next∗: PLifo END ; (∗ sklad − “zadnji noter, prvi ven”∗) Fifo∗ = RECORD(Empty) first∗,last∗: Lifo END; (∗ navadni seznam − “prvi noter, prvi ven” ∗) (∗ algoritmi ∗) PROCEDURE (VAR p:Lifo)Init∗; (∗ Zaˇcetne nastavitve ∗) BEGIN p.next:=NIL END Init; PROCEDURE (VAR p:Lifo)Insert∗(q:PLifo); (∗ q↑ na zaˇcetek p ∗) BEGIN q.next:=p.next ; p.next:=q END Insert; PROCEDURE (VAR p:Lifo)Step∗; se nadaljuje 139 ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 140 Dinamiˇ cni seznami (nadaljevanje) 35 36 37 39 39 40 41 42 44 44 45 47 47 48 49 50 51 53 53 54 56 56 57 58 59 61 61 62 (∗ pomikanje po seznamu ali brisanje elementov ∗) BEGIN p.next:=p.next.next END Step; PROCEDURE (VAR p:Fifo)Init∗; (∗ Zaˇcetne nastavitve ∗) BEGIN p.first.Init END Init; PROCEDURE (VAR p:Fifo)Insert∗(q:PLifo); (∗ Seznam podaljˇsamo s q↑ ∗) BEGIN IF p.first.next=NIL THEN p.first.Insert(q) ELSE p.last.next.Insert(q) END; p.last.next:=q END Insert; PROCEDURE (VAR f :Fifo)Join∗(VAR g:Fifo); (∗ Zdruˇzimo seznama f in g ∗) BEGIN IF f .first.next=NIL THEN f :=g ELSE f .last.next.next:=g.first.next; f .last.next:=g.last.next END END Join; END Qs. Dinamiˇ cni seznami (konec) 6.3 Problem najcenejˇ sih poti Problem iskanja najcenejˇsih poti v grafu je tipiˇcen problem, ki se reˇsuje z metodo dinamiˇcnega programiranja (v posebnih primerih pa tudi z drugimi metodami). V tem razdelku bomo opisali veˇc razliˇcic problema in metod reˇsevanja. Podan je usmerjen graf G = hV, Ei, pri ˇcemer je V mnoˇzica vozliˇsˇc, E pa mnoˇzica povezav. Na primer na sliki 6.1 je mnoˇzica vozliˇsˇc {1, 2, 3, 4, 5, 6}, mnoˇzica povezav pa {(1, 2), (1, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 6)(5, 6)}. Poleg tega imamo ˇse funkcijo c : E −→ IR (namesto IR lahko uporabljamo katerokoli popolnoma urejeno mnoˇzico, na kateri je definirano seˇstevanje). Koliˇcini c(e) pri e ∈ E pravimo cena povezave e. Na primer, na sliki 6.1 je cena povezave hi, ji oznaˇcena s cij . Graf, ki mu je prirejena cenilna funkcija c, poimenujemo graf z uteˇzenimi povezavami . Pot med vozliˇsˇcema iZ ˇ POTI 6.3. PROBLEM NAJCENEJSIH 141 in iK je zaporedje vozliˇsˇc iZ = i1 , i2 , . . . , in = iK , tako da je hij , ij+1 i ∈ E, 1 ≤ j < n, njena cena pa je uiZ ,iK = n−1 X c(ij , ij+1 ). j=1 V kolikor pri neki poti velja iZ = iK , ji pravimo cikel . Naloga iskanja najcenejˇsih poti ima ˇstevilne razliˇcice, od katerih bomo obravnavali najpomembnejˇse. Najprej razlikujemo med iskanjem najcenejˇsih poti • med zaˇcetnim vozliˇsˇcem in vsemi ostalimi ter • med vsemi pari vozliˇsˇc. Lahko bi pomislili, da bi bilo potrebno k dvema zapisanima nalogama dodati ˇse nalogo iskanja najcenejˇse poti med zaˇcetnim vozliˇsˇcem ter nekim posameznim konˇcnim vozliˇsˇcem, kajti gotovo je taka naloga laˇzja kot iskanje poti med zaˇcetnim vozliˇsˇcem in vsemi ostalimi. Napaka! Empiriˇcne izkuˇsnje namreˇc kaˇzejo, da sta ti dve nalogi po teˇzavnosti enakovredni. 6.3.1 Najcenejˇ se poti med zaˇ cetnim vozliˇ sˇ cem in vsemi ostalimi Za to, da v nekem grafu z uteˇzenimi povezavami, G = hV, E, ci, obstaja pot z enoliˇcno doloˇceno najniˇzjo ceno od zaˇcetnega vozliˇsˇca, 1, do vseh ostalih vozliˇsˇc in da ima konˇcno vrednost, morata biti izpolnjena dva pogoja: prviˇc, zaˇcetno vozliˇsˇce mora biti povezano z vsemi ostalimi in drugiˇc, graf ne sme imeti negativnih ciklov (ciklov, pri katerih je vsota cen povezav negativna). Oba pogoja sta oˇcitna, na primer, ˇce drugi ni izpolnjen, od vsake poti do kateregakoli vozliˇsˇca, do katerega vodi pot skozi neko vozliˇsˇce na negativnem ciklu, obstaja cenejˇsa pot, ki jo dobimo tako, da se sprehodimo po ciklu veˇckrat. Najcenejˇse poti imajo neko pomembno lastnost, ki nam omogoˇca, da jih raˇcunamo z dinamiˇcnim programiranjem: 6.4 Trditev Naj bo dan graf z uteˇzenimi povezavami G = hV, E, ci in neka najcenejˇsa pot med vozliˇsˇcema iZ in iK . Naj bo i1 , i2 , . . . , in neka pot, ki je del poti med iZ in iK . V tem primeru je i1 , i2 , . . . , in najcenejˇsa pot med i1 in in . ˇ i1 , i2 , . . . , in ni najcenejˇsa pot med i1 in in , jo zamenjamo z najcenejˇso Dokaz. Ce potjo in dobimo cenejˇso pot med iZ in iK kot je prvotna, kar nasprotuje privzetku, da je prvotna pot med iZ in iK najcenejˇsa. 2 Pri izpolnjenih dveh osnovnih pogojih (vozliˇsˇce 1 je povezano z vsemi ostalimi in odsotnost negativnih ciklov) je reˇsitev naloge moˇzno opisati z nekim naborom enaˇcb, ki jim pravimo Bellmanove enaˇcbe. ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 142 Bellmanove enaˇ cbe. Naj bo 1 zaˇcetno vozliˇsˇce in ui , pri 1 ≤ i ≤ n, cena najcenejˇse poti med 1 in i. V tem primeru lahko zapiˇsemo 0 pri i = 1; ui = (6.4) mink6=i {uk + cki } sicer. Z besedami: najcenejˇsa pot do zaˇcetnega vozliˇsˇca ima ceno 0, do ostalih vozliˇsˇc pa je najcenejˇsa pot podaljˇsek neke druge najcenejˇse poti. Pri zapisanih pogojih je reˇsitev Bellmanovih enaˇcb natanko reˇsitev problema najcenejˇsih poti, kar dokaˇzemo z dvema izrekoma: 6.5 Izrek Naj bo G = hV, Ei graf z uteˇzenimi povezavami, ki nima negativnih ciklov. Naj bo vozliˇsˇce 1 povezano z vsemi ostalimi in naj bo u1 , u2 , . . . , un reˇsitev problema najcenejˇsih poti od vozliˇsˇca 1 do vseh ostalih. V tem primeru zadoˇsˇcajo koliˇcine ui pri 1 ≤ i ≤ n Bellmanovim enaˇcbam (6.4). Dokaz. Dejstvo, da za vsak ui velja ui = uk + cki pri nekem k, sledi iz trditve 6.4. Sedaj pa denimo, da trditev iz izreka ni resniˇcna. V tem primeru obstaja i, tako da ui = min{uk + cki } k6=i ˇ nato spremenimo predhodno vozliˇsˇce k tako, da pravkar zapisana ni resniˇcno. Ce enaˇcba postane resniˇcna, dobimo cenejˇso pot do i, kar je v protislovju s privzetkom, da predstavlja nabor u1 , u2 , . . . , un reˇsitev problema najcenejˇsih poti od vozliˇsˇca 1 do vseh ostalih. 2 V primeru, ko so koliˇcine u1 , u2 , . . . , un reˇsitev Bellmanovih enaˇcb (6.4), se izkaˇze, da je vozliˇsˇca grafa moˇzno urediti v drevesno strukturo. Relacija “vo3 2 zliˇsˇce i je neposredno podrejeno vozliˇsˇcu j” oziroma 3 5 v simboliˇcnem zapisu, i ≺ j, je definirana kot j je 5 4 vrednost k, pri kateri uk + cki doseˇze minimum. Ker 2 mora biti j doloˇceno enoliˇcno, se domenimo, da v pri6 meru uk0 + ck0 j = uk00 + ck00 j izberemo manjˇsega od dveh indeksov, k 0 in k 00 . Slika 6.5: Drevo najceˇ Ce za vsak i zapiˇsemo zaporedje indeksov i ≺ i1 ≺ nejˇsih poti za graf na sliki i . . ., ugotovimo , da ima vsak element enega predho2 6.1, ˇce so cene povezav c12 = 2, c13 = 2, c24 = dnika (na podlagi definicije), zaporedje pa se konˇcuje z 3, c25 = 5, c34 = 4, c35 = vozliˇsˇcem 1 (ker je to edino vozliˇsˇce brez predhodnika, ˇ gl. sliko 6.5). Stevilu povezav na poti od vozliˇsˇca i do 6, c46 = 2 in c56 = 2 korena, ˇce uporabljamo povezave, na podlagi katerih je definirana relacija ≺, pravimo globina vozliˇsˇca i. 2 1 2 6.6 Izrek Podan je graf G = hV, Ei z uteˇzenimi povezavami brez negativnih ciklov. V tem primeru je reˇsitev Bellmanovih enaˇcb (6.4) enoliˇcna in predstavlja reˇsitev problema najcenejˇsih poti od vozliˇsˇca 1 do vseh ostalih. ˇ POTI 6.3. PROBLEM NAJCENEJSIH 143 Dokaz. Najprej za poljubno reˇsitev Bellmanovih enaˇcb (6.4) u1 , u2 , . . . , un dokaˇzemo, da predstavlja ui ceno neke poti od vozliˇsˇca 1 do i. Dokaz izpeljemo z indukcijo po globini vozliˇsˇca v drevesu vozliˇsˇc, ki je bilo pravkar opisano. Za globino 0 (vozliˇsˇce 1 oziroma koren drevesa) je trditev oˇcitna, kajti u1 = 0 je cena prazne poti. Sedaj dokaˇzimo trditev za vozliˇsˇce i s privzetkom, da smo trditev za vozliˇsˇca na ˇ velja i ≺ j, je globina j manjˇsa od globine globini, ki je manjˇsa od i, ˇze dokazali. Ce i in smo za uj ˇze dokazali, da je cena neke poti. Na podlagi definicije je potemtakem ui cena poti do j, ki je podaljˇsana s povezavo hj, ii. Naj bo sedaj u1 , u2 , . . . , un reˇsitev problema najcenejˇsih poti, ki na podlagi izreka 6.5 zadoˇsˇca Bellmanovim enaˇcbam (6.4) in naj bo u1 , u2 , . . . , un neka druga reˇsitev Bellmanovih enaˇcb, ki se razlikuje od u1 , u2 , . . . , un . Za reˇsitev u1 , u2 , . . . , un sestavimo drevo vozliˇsˇc in izberemo neko vozliˇsˇce i na najmanjˇsi globini, pri katerem velja ui 6= ui . V tem primeru oˇcitno velja ui > ui . Vendar, ker velja pri vseh vozliˇsˇcih j, uj ≥ uj , pri tistih na manjˇsi globini, kot je globina i pa uj = uj , sklepamo, da ui = mink6=i {uk + cki } ne velja, kar nasprotuje trditvi, da je u1 , u2 , . . . , un reˇsitev Bellmanovih enaˇcb (6.4), s ˇcimer smo trditev izreka dokazali. 2 Vendar z dokazom predhodnih dveh izrekov naloga izraˇcuna najcenejˇsih poti ˇse zdaleˇc ni zakljuˇcena. Namreˇc, kot smo v uvodu poglavja zapisali, se izraˇcun pri metodi dinamiˇcnega programiranja izvaja tako, da postopno raˇcunamo podnaloge prvotne naloge, zaˇcenˇsi z najmanjˇsimi. Pri reˇsevanju 0-1 nahrbtnika smo z enaˇcbo (6.1) ˇze odkrili hierarhijo velikosti podproblemov, saj je raˇcunanje funkcije ki−1 “manjˇsa naloga” od raˇcunanja ki (1 ). Pri sedanji nalogi pa enaˇcbe (6.4) narekujejo, da pri vsaki koliˇcini ui uporabljamo vse ostale uk , kar pomeni, da brez dodatne informacije ne poznamo vrstnega reda izraˇcunov. V nadaljevanju poglavja bomo opisali nekaj primernih hierarhij podnalog pri reˇsevanju problema najcenejˇsih poti. Najcenejˇ se poti v acikliˇ cnih grafih. Eden od moˇznih naˇcinov za urejanje pod∗ nalog iskanja najcenejˇsih poti je na podlagi dejstva, da takrat, ko velja u −→ / v (med u in v ni poti), naloga iskanja najcenejˇsih poti do v ne more biti odvisna od iskanja najcenejˇsih poti do u (ker ni moˇzno za iskanje “dobre” poti do v izkoristiti neko pot do u). V takem primeru lahko reˇsujemo nalogo iskanja najcenejˇsih poti do v pred nalogo iskanja najcenejˇsih poti do u. ∗ V primeru, ko graf nima ciklov, ima u −→ v (med u in v obstaja pot) za posledico ∗ v −→ / u (med v in u ni poti) in zato pri veljavnem prvem pogoju ter na podlagi zgoraj zapisane misli naloga iskanja najcenejˇsih poti do u ne potrebuje rezultata naloge iskanja najcenejˇsih poti do v. V sploˇsnem lahko iz tega naˇcela izpeljemo 1 Primerna in natanˇ cna definicija pojma “manjˇsa naloga” je, da je P1 manjˇsa naloga kot P2 , v kolikor pri iskanju reˇsitve za P1 ne potrebujemo rezultatov P2 . ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 144 3 1 0 5 4 2 Slika 6.6: Stopnje vozliˇsˇc nekega acikliˇcnega grafa neko urejenost vozliˇsˇc grafa ≺, na podlagi katere pridobijo Bellmanove enaˇcbe (6.4) naslednji videz: 0 pri i = 1 ui = (6.5) mink≺i {uk + cki } sicer in lahko raˇcunanje podnalog ui uredimo na podlagi urejenosti ≺ (torej “manjˇse” koliˇcine ui raˇcunamo pred “veˇcjimi”). To, kar smo pravkar zapisali, lahko povzamemo z naslednjo trditvijo: 6.7 Trditev Graf G = hV, Ei je acikliˇcen natanko tedaj, ko obstaja popolna urejenost V z lastnostjo, ∗ u v =⇒ u −→ / v. (6.6) Dokaz. Trditev je sestavljena iz dveh delov. Prvi pravi, da, v kolikor opisana popolna urejenost obstaja, je graf acikliˇcen; drugi pa nasprotno, ˇce je graf acikliˇcen, taka urejenost zagotovo obstaja. Dokaz prvega dela je oˇciten, saj lahko na podlagi urejenosti ≺ vozliˇsˇca razvrstimo v ravno ˇcrto, pogoj (6.6) pa pomeni, da povezave gredo le od leve proti desni, kar seveda pomeni, da ni ciklov. Pri dokazu drugega pa iz privzetka acikliˇcnosti definiramo urejenost, ki ima ˇzeleno lastnost. Najprej definiramo pojem stopnje vozliˇsˇca v kot najveˇcje ˇstevilo povezav na neki poti, ki izhaja iz v. Pojem je dobro definiran, kajti kadar je graf acikliˇcen, iz vsakega vozliˇsˇca vodi konˇcno mnogo poti in je potemtakem tudi najveˇcje ˇstevilo povezav na neki poti natanˇcno definirano. Kot primer prikazuje slika 6.6 acikliˇcni graf, kjer so vozliˇsˇca oznaˇcena s stopnjami. Ker oˇcitno velja ∗ u −→ v =⇒ stopnja(u) > stopnja(v), oziroma, ∗ stopnja(u) ≤ stopnja(v) =⇒ u −→ / v, lahko relaciji u v ustrezajo padajoˇce stopnje. Bolj natanˇcno, v primeru stopnja(u) < stopnja(v) definiramo u v, v primeru stopnja(u) = stopnja(v) pa bodisi u v ali u ≺ v. 2 Urejenosti pravimo topoloˇska urejenost (vozliˇsˇc grafa), postopek za njen izraˇcun pa je prikazan na sliki 6.7. Postopek izhaja iz tega, da ima zagotovo najviˇsjo stopnjo (in s tem najmanjˇsi poloˇzaj v urejenosti ) neko vozliˇsˇce, do katerega ne vodi nobena povezava. Zato najprej poiˇsˇcemo neko tako vozliˇsˇce in ga zapiˇsemo kot naslednje ˇ POTI 6.3. PROBLEM NAJCENEJSIH 145 Vhod: Seznam povezav E grafa G = hV, Ei. Izhod: Topoloˇsko urejeno zaporedje vozliˇsˇ c, V . Postopek: 1. Preberemo seznam parov hi, ji in na podlagi seznama zgradimo podatkovno strukturo, ki predstavlja graf. Vsakemu vozliˇsˇ cu v priredimo koliˇ cino inv , ki predstavlja ˇstevilo povezav, ki so usmerjene v vozliˇsˇ ce; 2. r = ∅; 3. poiˇsˇ cemo vsa vozliˇsˇ ca v, pri katerih je inv = 0, ter jih dodamo seznamu r; ˇ ce takega vozliˇsˇ ca ni in je mnoˇ zica vozliˇsˇ c neprazna, je to znak, da G vsebuje cikel. V tem primeru se ustavimo; 4. spremenljivki u priredimo prvi element seznama r ter ga obenem odstranimo s seznama; 5. vse povezave, ki so usmerjene iz u, odstranimo in vsakiˇ c, ko odstranimo neko povezavo, zmanjˇsamo za 1 koliˇ cino inv , ˇ ce je v vozliˇsˇ ce, kamor je bila povezava usmerjena; 6. u damo na konec izhodnega zaporedja; 7. ˇ ce je seznam r prazen, se ustavimo, sicer nadaljujemo pri 3. Slika 6.7: Topoloˇsko urejanje (oziroma zaˇcetno) vozliˇsˇce v vrstnem redu . Nato vse povezave, ki izhajajo iz tega ˇ vozliˇsˇca, izbriˇsemo in postopek ponovimo2 . Cas, ki ga porabi postopek za topoloˇsko urejanje ni teˇzko oceniti: naj bo ˇstevilo vozliˇsˇc n, ˇstevilo povezav pa m. Za sestavljanje podatkovne strukture, ki predstavlja vozliˇsˇca, in za obiskovanje vozliˇsˇc, ki nimajo vhodnih povezav, potrebujemo ˇcas Θ(n), ˇcas za izraˇcun vhodne stopnje vozliˇsˇc ter za aˇzuriranje slednje koliˇcine pa je Θ(m), torej je skupen ˇcas Θ(m + n). 6.8 Primer Sled algoritma na sliki 6.7 na primeru s slike 6.1 je prikazana v tabeli na sliki 6.8. 2 Dijkstrov algoritem. V kolikor imajo vse povezave v grafu pozitivne cene, se nam ponuja neka druga moˇznost urejanja izraˇcunov koliˇcin ui v enaˇcbah 6.4. V tem primeru jih lahko raˇcunamo na podlagi naraˇsˇcajoˇce vrednosti (ui ). To je moˇzno zato, ker v primeru ui > uj najcenejˇsa pot do i ne pride v poˇstev kot gradnik za najcenejˇso ∗ pot do j (ˇcetudi velja i −→ j) zato, ker bi povezave od i do j (ki so pozitivne) le ˇse poveˇcale vrednost te koliˇcine. Posledica tega je da, lahko izraˇcunamo ui kasneje 2 V naˇ sem primeru, ko iˇsˇ cemo najcenejˇse poti med zaˇ cetnim vozliˇsˇ cem in vsemi ostalimi, je na zaˇ cetku zaˇ cetno vozliˇsˇ ce edino, ki izpolnjuje pogoj, da nima k sebi usmerjenih povezav (zaradi privzetka o povezanosti zaˇ cetnega vozliˇsˇ ca z vsemi ostalimi). 146 ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE r=1 u = 1 (odstranimo r = 2,3 u = 2 (odstranimo r=3 u = 3 (odstranimo r = 4,5 u = 4 (odstranimo r=5 u = 5 (odstranimo r=6 u=6 povezavi 1-2 ter 1-3) povezavi 2-4 ter 2-5) povezavi 3-4 ter 3-5) povezavo 4-6) povezavo 5-6) Slika 6.8: Sled algoritma za topoloˇsko urejanje (slika 6.7) za graf na sliki 6.1. Izpis r se nanaˇsa na trenutek, ko se izvrˇsi toˇcka 3, izpis u pa se nanaˇsa na trenutek, ko se izvrˇsi toˇcka 5 kot uj . Torej izraˇcune uredimo tako, da na vsakem koraku izraˇcunamo naslednjo veˇcjo koliˇcino ui . Algoritmu, ki deluje na ta naˇcin, pravimo Dijkstrov algoritem, po izumitelju E. W. Dijkstri in je prikazan na sliki 6.9. Za razumevanje algoritma je bistvena invarianta zanke, ki obsega toˇcke 2-5 na sliki 6.9. Le-ta se glasi: “Koliˇcine ui za elemente mnoˇzice P so dokonˇcne in pravilne vrednosti cen najcenejˇsih poti, koliˇcine ui za elemente T pa predstavljajo le zgornjo mejo dokonˇcne vrednosti. ui za nek element i ∈ T predstavlja najmanjˇso ceno med takimi potmi do i, v katerih le vozliˇsˇce i pripada T , ostala pa pripadajo P .” Zapisana invarianta oˇcitno velja pred toˇcko 2, njeno sploˇsno veljavnost pa dokaˇzemo z indukcijo. Pred prvim izvajanjem zanke je njena veljavnost oˇcitna, na podlagi zaˇcetnih prirejanj (P vsebuje le vozliˇsˇce 1, cena poti do njega pa je 0). Sedaj je potrebno dokazati, da invarianta ostane v veljavi tudi po izvajanju jedra zanke. Denimo, da smo v neki iteraciji na 3. koraku (slika 6.9) naˇsli element k. Moramo se prepriˇcati, da je uk dejansko pravilna vrednost najcenejˇse poti med 1 in k. Naj to ne drˇzi. V tem primeru obstaja neka druga, cenejˇsa pot do k. Pri cenejˇsi poti se ne more zgoditi, da vse povezave, razen zadnje, povezujejo elemente P , kajti med takimi je pot s ceno uk ˇze najcenejˇsa. Torej gre domnevno najcenejˇsa pot do nekega vozliˇsˇca h0 , nato preskoˇci v mnoˇzico T pri elementu k 0 , od koder nadaljuje pot do k (gl. sliko 6.10). Vendar, ker je uk0 ≥ uk , bi morala pot med k 0 in k imeti skupno negativno ceno, ˇce bi hoteli doseˇci manjˇso vrednosti od uk . To pa ni moˇzno zaradi izhodiˇsˇcnega privzetka, da so cene povezav pozitivne. Torej je uk najcenejˇsa pot do k. Pravzaprav ni nujno, da pot k 0 −→ k poteka le po vozliˇsˇcih T . Lahko tudi enkrat ali veˇckrat preskoˇci nazaj v P . Vendar to ne spremeni zapisanega premisleka in zakljuˇcek ostane isti. Nadalje moramo preveriti, ali se po popravku vrednosti ui , pri i ∈ T ohranja lastnost, da je ui cena najcenejˇse med potmi, pri katerih le konˇcno vozliˇsˇce ne pripada ˇ POTI 6.3. PROBLEM NAJCENEJSIH 147 Vhod: Seznam cen povezav, cij , grafa G = hV, Ei, pri ˇ cemer je V = {1, 2, . . . , n}. Izhod: Vrednosti najcenejˇsih poti, ui . Postopek: 1. Koliˇ cinam ui priredimo zaˇ cetne vrednosti c1i , ˇ ce povezava h1, ii obstaja, sicer dobi ui vrednost ∞; nato razdelimo V na dve medsebojno tuji podmnoˇ zici: P = {1} in T = {2, . . . , n}. 2. ˇ ce je T prazno, se ustavimo, sicer nadaljujemo; 3. poiˇsˇ cemo minimalno vrednost uk v mnoˇ zici T ter k prestavimo v mnoˇ zico P ; ˇ ce ima veˇ c koliˇ cin ui isto minimalno vrednost, izberemo najmanjˇsi indeks; 4. popravimo koliˇ cine ui za i ∈ T po pravilu ui := min(ui , uk + cki ); 5. vrnemo se na korak 2. Slika 6.9: Dijkstrov algoritem k0 T ch0 k0 h0 chk h 1 k P Slika 6.10: Dijkstov algoritem: izbira naslednjega elementa k, do katerega poznamo najcenejˇso pot P (gl. sliko 6.9, toˇcka 4). To pa oˇcitno drˇzi, saj potem, ko smo dodali k mnoˇzici P , je edina nova moˇznost za povezave med P in i, pot, ki pelje preko k. In to moˇznost smo ravno preverili pri popravljanju vrednosti ui . 6.9 Primer Delovanje Dijkstrovega algoritma na grafu s slike 6.11 je prikazano na tabeli na sliki 6.12. 2 Tudi ˇcas Dijkstrovega algoritma ni teˇzko oceniti: ˇcas za zaˇcetni izraˇcun koliˇcin ui , 2 ≤ i ≤ n, pri ˇcemer je n ˇstevilo vozliˇsˇc, je Θ(n). Nato pri i-tem izvajanju jedra zanke 2-5 potrebujemo ˇcas Θ(n−i) za izbiro najmanjˇse vrednosti ui ter za spremembo koliˇcin ui . Celotni ˇcas je torej enak Θ(n2 ). ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 148 1 7 2 2 7 7 7 3 4 3 2 6 1 2 5 1 Slika 6.11: Primer grafa u u u u u u 1 0∗ 0∗ 0∗ 0∗ 0∗ 0∗ 2 2 2∗ 2∗ 2∗ 2∗ 2∗ 3 7 5 5∗ 5∗ 5∗ 5∗ 4 ∞ 9 9 7 7∗ 7∗ 5 ∞ 9 6 6∗ 6∗ 6∗ 6 ∞ ∞ ∞ 8 8 8∗ Slika 6.12: Potek Dijkstrovega algoritma za graf na sliki 6.11 (elementi mnoˇzice P so oznaˇceni z zvezdicami). Odsotnost povezave med P in i je nakazana z znakom ∞. Tako zasnovan algoritem za najcenejˇse poti v grafih spada med poˇzreˇsne algoritme (ker izbiramo k na podlagi najmanjˇse vrednosti ui , i ∈ T ), kljub temu, da se od algoritmov iz poglavja 4 razlikuje po tem, da se ocene preostalih predmetov (ui pri i ∈ T ) spreminjajo med raˇcunanjem. Trditev je upraviˇcena zato, ker je najbolj znaˇcilna lastnost poˇzreˇsnih algoritmov, da predmete izbiramo enega za drugim na podlagi njihove vrednosti in nobena izbira predmeta ne more postaviti pod vpraˇsaj neke predhodne izbire. Bellman-Fordov algoritem. Pri zadnjem naˇcinu urejanja podnalog naloge iskanja najcenejˇsih poti uporabljamo pribliˇzke koliˇcin ui , ki se razlikujejo po ˇstevilu povezav na neki poti. Pri tem z uhi oznaˇcimo najcenejˇso pot do i med vsemi potmi, ki vsebujejo h ali manj povezav. Oˇcitno je, da pot s h povezavami lahko uporablja kot gradnike le poti, ki imajo manj kot h povezav. Zato lahko izraˇcun uredimo tako, da raˇcunamo koliˇcine uhi po naraˇsˇcajoˇcem h. Dokonˇcne vrednosti koliˇcin ui dobimo kot vrednosti un−1 , kjer je n ˇstevilo vozliˇsˇc v grafu. i V tem primeru imajo Bellmanove enaˇcbe (6.4) obliko pri i = 1; 0 h c pri h = 1; ui = 1i h−1 h−1 min{ui , mink6=i {uk + cki }} sicer. (6.7) ˇ POTI 6.3. PROBLEM NAJCENEJSIH 149 ˇ ˇ ˇ OBICAJNO MATRICNO MNOZENJE Cij = n X Aik · Bkj . k=1 ˇ ˇ SPREMENJENO MATRICNO MNOZENJE n Cij = min{Aik + Bkj }. k=1 Slika 6.13: Dve vrsti matriˇcnega mnoˇzenja ˇ privzamemo, da je cii = 0, lahko zadnjo vrstico zapiˇsemo v enostavnejˇsi obliki, Ce n uhi = min{ukh−1 + cki }. k=1 (6.8) Za ta algoritem se ni teˇzko prepriˇcati, da porabi ˇcas Θ(n3 ). 6.3.2 Najcenejˇ se poti med vsemi pari vozliˇ sˇ c Naˇceloma je moˇzno poiskati najcenejˇse poti med vsemi pari vozliˇsˇc tako, da algoritem za najcenejˇse poti med zaˇcetnim vozliˇsˇcem in vsemi ostalimi sproˇzimo pri vsakem ˇ je ˇstevilo vozliˇsˇc n, dobimo potemtakem algoritem s ˇcasovno zahtevnostjo vozliˇsˇcu. Ce nT (n), kjer je T (n) ˇcasovna zahtevnost algoritma za najcenejˇse poti med zaˇcetnim vozliˇsˇcem in vsemi ostalimi. Zanimiva pa je naloga poiskati algoritem za najcenejˇse poti med vsemi pari vozliˇsˇc, ki bi imel manjˇso zahtevnost od zapisane vrednosti. V nadaljevanju bomo predstavili dva takˇsna algoritma. Posploˇ seni Bellman-Fordov algoritem. V tem primeru izhajamo iz BellmanFordovega algoritma. Koliˇcino ui iz (6.8) poimenujemo u1i , oziroma po zamenjavi i z j, u1j , nato pa dobimo kot vrednost najcenejˇse poti, ki vsebuje najveˇc h povezav, od i do j izraz pri i = j 0 cij pri h = 1 uhij = (6.9) h−1 minnk=1 {uik + ckj } pri 2 ≤ h ≤ n − 1. ˇ koliˇcine uh uredimo v matriko velikosti n × n in jo poimenujemo C h , pri ˇcemer je Ce ij C 1 matrika cen cij , lahko opazimo, da je izraz (6.9) dobro znano pravilo za element hi, ji matriˇcnega produkta C h−1 · C 1 potem, ko smo operacijo mnoˇzenja zamenjali s seˇstevanjem in vsoto nadomestili z minimalizacijo (gl. sliko 6.13). ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 150 h h−1 uhj h−1 uih i uh−1 ij j vozliˇsˇca 1, . . . , h − 1 Slika 6.14: Osnovna operacija pri Floyd-Warshallovem algoritmu Na prvi pogled s tako spremembo zapisa nismo pridobili niˇc. Na podlagi prvega odstavka tega razdelka ugotavljamo, da porabi posploˇseni Bellman-Fordov algoritem ˇcas nT (n) = Θ(n4 ). Ocena se ne spremeni, ˇce uporabljamo matriˇcni zapis, kajti matrika najcenejˇsih poti je 1 1 (6.10) U =C · . . . · C }1 , | · C {z n−1 faktorjev in ˇce privzamemo, da eno matriˇcno mnoˇzenje porabi Θ(n3 ) operacij, vsega skupaj pa imamo n − 2 matriˇcnih mnoˇzenj, dobimo tudi na ta naˇcin oceno Θ(n4 ) operacij. Vendar nas matriˇcni zapis napelje na idejo, da (6.10) raˇcunamo po shemi, 2| · 2 ·{z. . . · 2} (C ) dlog2 (n−1)e 1 (6.11) (torej z iteriranim kvadriranjem), kar pri nespremenjeni oceni za ˇstevilo operacij posameznega matriˇcnega mnoˇzenja pripelje do konˇcne ocene Θ(n3 log2 n). (6.12) Bodimo pozorni na to, da je ˇstevilo kvadriranj k = dlog2 (n − 1)e, kar pomeni, da je 2k najmanjˇsi eksponent 2, ki je veˇcji od n − 1. Floyd-Warshallov algoritem. Pri Bellman-Fordovem algoritmu smo podnaloge uredili na podlagi najveˇcjega ˇstevila povezav na poti med dvema vozliˇsˇcema. Izkaˇze se, da obstaja ˇse en naˇcin za njihovo urejanje, ki pripelje do hitrejˇsega algoritma. V tem primeru uredimo podnaloge naloge iskanja najcenejˇse poti med i in j na podlagi najveˇcjega indeksa nekega vmesnega vozliˇsˇca na poti med i in j (v primeru, ko je pot sestavljena iz ene same povezave, definiramo najveˇcji indeks vmesnega vozliˇsˇca kot 0). Na primer, na sliki 6.11 imamo med vozliˇsˇcema 1 in 4 poti 1-2-4, 1-3-4 in 1-3-5-4. Najveˇcji indeksi vmesnih vozliˇsˇc so 2, 3 in 5, po vrsti. V tem primeru so Bellmanove ˇ POTI 6.4. PREVEDBA 0-1 NAHRBTNIKA NA NAJCENEJSE inA Tin 151 inB (inA ) MB outA (inA ) Tout outB (inB (inA )) Slika 6.15: Prevedba problema A na problem B enaˇcbe uhij = cij h−1 h−1 min{uh−1 ij , uih + uhj } pri h = 0; sicer (6.13) in dokonˇcne vrednosti najcenejˇsih poti, uij , dobimo kot unij . Porabo ˇcasa ocenimo tako, da preˇstejemo ˇstevilo operacij minimalizacije. Pri vsaki vrednosti 0 < h ≤ n izraˇcunamo n2 vrednosti uhij , za katere porabimo konstanten ˇcas. Torej je celotni ˇcas Θ(n3 ). 6.4 Prevedba 0-1 nahrbtnika na najcenejˇ se poti Lahko pokaˇzemo, da je problem najcenejˇsih poti v nekem smislu najteˇzji v svojem razredu. To utemeljimo tako, da pokaˇzemo, da si lahko pri reˇsitvi drugih problemov pomagamo z algoritmom, ki reˇsuje problem najcenejˇsih poti. Namreˇc, teˇzko si je predstavljati, da bi bil problem B laˇzji od problema A, obenem pa bi lahko A reˇsili, ˇce poznamo naˇcin reˇsevanja problema B. Zaenkrat je pojem “pomagamo” ˇse nekoliko nenatanˇcen, zato ga bomo doloˇcili nekoliko natanˇcneje. Podana sta problema A in B, ki sta opisana s svojimi vhodnimi podatki inA in inB (gl. sliko 6.15). Za vsak problem je moˇznih neskonˇcno vhodnih podatkov, kot npr. v primeru najcenejˇsih poti, ko je problem moˇzno postaviti za najrazliˇcnejˇse grafe in cene povezav. Problema A in B imata reˇsitvi outA (inA ) in outB (inB ) (na ta naˇcin nakaˇzemo, da je reˇsitev odvisna od vhodnih podatkov). Pravimo, da A prevedemo na B (ali, da si pri reˇsitvi A pomagamo z B), ˇce obstajata transformaciji Tin in Tout , tako da Tin spremeni inA v inB (inA ) (zopet smo nakazali, da so podatki, ki jih transformacija sestavi, odvisni od podatkov inA ), Tout pa spremeni outB (inB (inA )) v outA . Dodaten pogoj je, da sta transformaciji Tin in Tout razmeroma preprosti, tako da je skoraj celotno delo pri reˇsevanju A odvisno od raˇcunskega napora pri reˇsevanju B. V veˇcje podrobnosti se ne bomo spuˇsˇcali. Najpreprostejˇsi primer opisanega postopka je prevedba problema najdraˇzje poti skozi graf na problem najcenejˇse poti. Torej ˇzelimo poiskati najdraˇzjo pot od zaˇcetnega vozliˇsˇca v grafu do vseh ostalih (problem A), imamo pa na razpolago algoritem, ki zna poiskati najcenejˇso pot (problem B). Kako lahko uporabimo algoritem za problem B ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 152 xs x01 0 x02 0 c2 0 ... xt 0 c3 c1 .. . .. xv2 2 . 0 .. . x xv1 111 0 .. . xv1 +v2 ,2 .. . c2 xv1 0 xv2 0 c2 .. . x0n 0 c2 xW 1 xvn 0 ... 0 c3 xv+v2 ,2 .. . 0 xW n xW 2 0 .. . 0 ... 0 Slika 6.16: Graf, ki ustreza sploˇsnemu problemu 0-1 nahrbtnika. za reˇsevanje problema A? V tem primeru so podatki inA ter inB , podatki o grafih in cenah povezav. Za reˇsitev problema A z algoritmom za B zadostuje, da Tin vse cene povezav negira, Tout pa vse vrednosti ui ponovno negira. V tem primeru bo najcenejˇsa pot problema B ustrezala najdraˇzji poti problema A. Ta preprosti primer tudi nekoliko daje obˇcutek, kaj smo mislili s pogojem, da morata Tin in Tout biti enostavni operaciji. Netrivialen primer istega postopka predstavlja prevedba problema 0-1 nahrbtnika ˇ je podan problem nahrbtnika s ˇstevilom predmetov n, na problem najdraˇzje poti. Ce vektorjema v (prostornin predmetov) in c (cen predmetov) ter prostorsko omejitvijo W , bomo opisali transformacijo Tin , ki spremeni opis nahrbtnika v opis grafa G = hV, Ei s cenami povezav cij , pri i, j ∈ V tako, da bo najdraˇzja pot od zaˇcetnega vozliˇsˇca v G do nekega posebnega konˇcnega vozliˇsˇca enoliˇcno doloˇcala reˇsitev problema nahrbtnika. Privzetek, iz katerega izhajamo, je, da so vse prostornine celoˇstevilˇcne. Oˇcitno se tudi primer poljubnih racionalnih prostornin da prevesti na to obliko tako, da vse prostornine pomnoˇzimo z najmanjˇsim skupnim mnogokratnikom imenovalcev ˇstevil, ki predstavljajo vrednosti prostornin. Graf G definiramo tako, da je V = 153 6.5. POVZETEK OSNOVNIH POJMOV xs x01 x02 0 3 x11 0 x12 x22 x31 0 2 x41 0 x13 0 1 x33 0 0 x43 0 0 x23 0 x42 xt 0 0 6 x24 0 x34 6 0 x25 0 x35 0 0 1 0 0 x15 x14 1 4 2 4 x32 x05 0 1 4 0 x04 0 0 2 x21 x03 0 x44 x45 0 Slika 6.17: Primer prevedbe za konkretni problem 0-1 nahrbtnika (6.14) {xs , xt } ∪ {xij | 0 ≤ i ≤ W, 1 ≤ j ≤ n}, E pa vsebuje, prviˇc, dve povezavi, ki izvirata pri xs ; ena s ceno 0 do vozliˇsˇca x01 in druga s ceno c1 do vozliˇsˇca xv1 ,1 , drugiˇc V + 1 povezav s ceno 0, ki izvirajo pri vozliˇsˇcih xin , 0 ≤ i ≤ W in vodijo do xt in konˇcno povezave, ki vodijo iz vozliˇsˇc xij , 0 ≤ i ≤ W, 1 ≤ j ≤ n − 1, in sicer tako, da iz vsakega izhajata dve povezavi, ena do vozliˇsˇca xi,j+1 s ceno 0 in druga do vozliˇsˇca xi+vj+1 ,j+1 s ceno cj . Graf G je shematiˇcno prikazan na sliki 6.16. ˇ kot konkreten zgled vzamemo naslednji problem 0-1 nahrbtnika: Ce W = 4, v1 = 1, v2 = 2, v3 = 2, v4 = 1, v5 = 3, c1 = 3, c2 = 2, c3 = 4, c4 = 1, c5 = 6, (6.14) dobimo kot rezultat opisane transformacije graf, ki je prikazana na sliki 6.17. 6.5 Povzetek osnovnih pojmov 1. Razlika med dinamiˇcnim programiranje in rekurzivnim razcepom 2. Tipiˇcne naloge, ki jih reˇsujemo z dinamiˇcnim programiranjem 3. 0-1 nahrbtnik. Bellmanova enaˇcba. OKN funkcija 4. Najcenejˇse poti v grafih. Razliˇcne vrste nalog najcenejˇsih poti 5. Bellmanove enaˇcbe pri problemu najcenejˇsih poti. Osnovni privzetek za uporabnost Bellmanovih enaˇcb. Zakaj ni moˇzno iz Bellmanovih enaˇcb neposredno pridobiti algoritma za reˇsevanje problema ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE 154 −6 1 1 3 2 2 −4 3 3 9 4 3 2 1 5 1 6 5 2 6 8 6 4 9 2 3 (a) Graf brez ciklov 1 10 4 3 1 3 2 5 6 4 (b) Graf s pozitivnimi povezavami Slika 6.18: Dva grafa 6. Najcenejˇsa pot od zaˇcetnega vozliˇsˇca do vseh ostalih v acikliˇcnem grafu 7. Dijkstrov algoritem 8. Bellman-Fordov algoritem in posploˇseni Bellman-Fordov algoritem. Uˇcinkovita izvedba slednjega algoritma 9. Floyd-Warshallov algoritem 10. Prevedba 0-1 nahrbtnika na najcenejˇse poti. 6.6 Naloge 1. Naj bodo povezave nekega grafa podane z matriko A= 0 ∞ ∞ ∞ ∞ −1 0 ∞ 0 0 3 5 0 −1 0 ∞ 6 ∞ 0 −1 6 8 10 ∞ 0 in naj bodo najcenejˇse poti od zaˇcetnega vozliˇsˇca, u1 = 0, u2 = −1, u3 = 3, u4 = 5 in u6 = 6. Sestavite drevo najcenejˇsih poti. 2. Poiˇsˇcite reˇsitev problema najcenejˇsih poti od vozliˇsˇca 1 do vseh ostalih za acikliˇcen graf na sliki 6.18a. 3. Uporabite Dijsktrov algoritem na grafu na sliki 6.18b. 4. Naj bodo povezave nekega grafa podane z matriko 0 ∞ ∞ A= ∞ ∞ ∞ ∞ ∞ 0 9 4 0 −1 4 4 −1 0 0 1 −1 3 10 −3 8 0 2 3 ∞ 3 2 3 8 0 2 ∞ ∞ 11 2 6 3 0 2 ∞ 0 1 3 −1 0 0 . Poiˇsˇcite najcenejˇse poti od vozliˇsˇca 1 do vseh ostalih z Bellman-Fordovo metodo. 6.6. NALOGE 155 5. Uporabite “matriˇcno mnoˇzenje” (posploˇseno Bellman-Fordovo metodo) na grafu na sliki 6.18b. 6. Uporabite Floyd-Warshallov algoritem na grafu na sliki 6.18b. 156 ˇ POGLAVJE 6. DINAMICNO PROGRAMIRANJE Poglavje 7 SESTOPANJE 7.1 Uvod Sestopanje je primerno, ko v neki mnoˇzici moˇznih reˇsitev iˇsˇcemo eno ali veˇc reˇsitev, za katere je znaˇcilno, da jih je moˇzno sestaviti z zaporednimi koraki, pri ˇcemer na vsakem koraku lahko ugotovimo, ali smo na pravi poti (ali je delna reˇsitev “dopustna”). Ko na nekem koraku izˇcrpamo vsa moˇzna nadaljevanja, ne da bi dosegli ˇzeleni cilj, zadnji korak trenutne reˇsitve podremo (“sestopimo”) in nadaljujemo od prejˇsnjega koraka v drugi smeri. Izraz “sestopanje” izvira iz prispodobe drevesa: reˇsitve si predstavljamo, kot da so urejene v drevesno strukturo, pri ˇcemer nasledniki nekega vozliˇsˇca predstavljajo moˇzne alternative na nekem koraku. Torej “sestopimo”, ko pri nekem vozliˇsˇcu ugotovimo, da ne moremo nadaljevati s “plezanjem” do konˇcne reˇsitve in se spustimo po isti poti do predhodnega vozliˇsˇca (gl. sliko 7.1). Glede na to, da je metoda sestopanja najmanj uˇcinkovita (beri: najpoˇcasnejˇsa) med vsemi metodami sestavljanja algoritmov, ki smo jih obravnavali, se k njej zatekamo le takrat, ko nobena druga ni uporabna in ko moramo preprosto preizkusiti vse moˇznosti. Njena prednost pa je v tem, da je raz- Slika 7.1: Plezanje po drevesu meroma enostavna za realizacijo in ne potrebuje po- in sestopanje globljenega poznavanja problema, ki ga reˇsujemo. Iz pravkar zapisane pripombe sledi, da jo lahko uporabljamo na zaˇcetku raziskav nekega problema, kasneje, ko problem bolje spoznamo, pa morebiti odkrijemo kakˇsno uˇcinkovitejˇso reˇsitev. Metodo bomo prikazali na nekaj znaˇcilnih in dobro znanih problemih. 157 158 POGLAVJE 7. SESTOPANJE 1 2 4 4 5 6 7 8 9 11 11 12 13 14 15 PROCEDURE poskus(i: INTEGER); BEGIN izbira prvega koraka na seznamu moˇznih; REPEAT IF korak dopusten THEN zapiˇsi ga ; IF ni ˇse konec THEN poskus(i+1) ; IF izid neuspeˇsen THEN sestop END ELSE zapiˇsi “izid uspeˇsen” END END; izbira naslednjega koraka UNTIL izid uspeˇsen OR (konec seznama moˇznih korakov ) END poskus ; Slika 7.2: Vzorec za osnovno proceduro sestopanja 7.2 Problem skakaˇ cevega obhoda Problem opiˇsemo takole: podana je ˇsahovska deska velikosti n × n (obiˇcajno je n = 8, vendar ima lahko n tudi drugaˇcno vrednost) in skakaˇc, ki se premika na obiˇcajen naˇcin. Na primer, moˇzne poteze skakaˇca, ki je postavljen na polje, ki je oznaˇceno z ˇ skakaˇca postavimo na polje hi, ji, “∗”, so na sliki 7.3 prikazane s ˇstevilkami 0-7. Ce 2 je naloga poiskati zaporedje n − 1 skakaˇcevih potez, s katerimi le-ta obiˇsˇce vsa polja in seveda vsako polje le enkrat (gl. tudi nalogo 2). Pri sestavljanju algoritma, ki realizira metodo sestopanja, moramo najprej izbrati pri7 merne podatkovne strukture za problem, ki ga 0 7 6 reˇsujemo. Ko je ta del naloge opravljen, se lotimo 1 6 5 samega algoritma. Algoritem obiˇcajno vsebuje ∗ 4 neko osrednjo proceduro, ki izvaja “poskuse”, 3 5 2 pridobimo pa jo iz osnovnega vzorca, ki je pri2 4 3 kazan na sliki 7.2, tako da ga prilagodimo trenu1 tnemu problemu. 00 1 2 3 4 5 6 7 V primeru skakaˇcevega obhoda izberemo kot primerno podatkovno strukturo za predstavitev ˇ Slika 7.3: Sahovnica velikosti 8 × 8 ˇ s ahovnice tabelo in moˇzne poteze skakaˇca a: ARRAY n,n OF INTEGER. (7.1) Njeni elementi predstavljajo obiskanost polj ˇsahovnice, in sicer tako, da z -1 oznaˇcujemo neobiskana polja, z 0 zaˇcetno polje in z i, 0 < i ≤ n2 − 1 polje, ki je bilo obiskano na i-ti potezi. ˇ 7.2. PROBLEM SKAKACEVEGA OBHODA 159 Naslednja podatkovna struktura, ki jo potrebujemo, je predstavitev moˇznih skakaˇcevih potez. Kot najprimernejˇsa se izkaˇze uporaba dveh tabel, dx in dy velikosti 8, ki podajata odmik ciljnega polja od izvornega polja po x in y oseh (gl. tudi sliko 7.3): i dx dy 0 1 2 1 2 1 2 2 -1 3 1 -2 4 -1 -2 5 -2 -1 6 -2 1 7 -1 2 Sedaj bomo vse sploˇsne opise na sliki 7.2 nadomestili z elementi, ki se nanaˇsajo na problem skakaˇcevega obhoda. Prvi na vrsti je stavek izbira prvega koraka na seznamu moˇznih. Za uresniˇcitev stavka je potrebno v proceduri poskus deklarirati celoˇstevilˇcno spremenljivko j, ki ˇsteje ˇstevilo opravljenih poskusov, in ji na zaˇcetku prirediti vrednost 0. Za realizacijo stavka korak dopusten je najprej potrebno definirati pojem dopustnosti. Korak je dopusten, ko ciljno polje 7 6 5 ∗ 4 3 2 1 00 1 2 3 4 5 6 7 ˇ Slika 7.4: Sahovnica velikosti 8 × 8 in moˇzne poteze dame 1. ni zunaj mej ˇsahovnice in 2. ˇse ni zasedeno. Torej lahko ta stavek v primeru, ko sta koordinati izvornega polja podani z globalnima spremenljivkama x in y, realiziramo kot (x +dx [j ]≥0)&(x +dx [j ]<n) &(y+dy[j ]≥0)&(y+dy[j ]<n) &(a[x +dx [j ],y+dy[j ]]<0). Stavek zapiˇsi ga realiziramo preprosto kot, x :=x +dx [j ] ; y:=y+dy[j ] ; a[x ,y]:=i , ni ˇse konec pa kot i < n2 − 1. Za izid neuspeˇsen potrebujemo globalno spremenljivko uspeh, ki ji na zaˇcetku priredimo vrednost FALSE. V tem primeru se izid neuspeˇsen prevede preprosto na ∼uspeh. sestop se prevede na zaporedje, a[x ,y]:=−1 ; x :=x −dx [j ] ; y:=y−dy[j ], zapiˇsi “izid uspeˇsen” na uspeh := TRUE, stavek izid uspeˇsen na uspeh in konec seznama moˇznih korakov na j = 8. Popoln program za skakaˇcev obhod, ki smo ga pravkar razvili in ki vsebuje tudi zaˇcetna prirejanja ter izpis rezultata, je prikazan kot program 7.1. 160 POGLAVJE 7. SESTOPANJE Program 7.1: Skakaˇ cev obhod 1 3 3 4 5 6 8 8 9 10 11 12 14 14 15 17 17 18 19 21 22 22 24 24 25 26 28 28 29 30 32 32 33 34 35 36 38 38 40 40 41 42 43 45 MODULE Skakac; CONST n=8; x0 =1; y0 =1; VAR a: ARRAY n,n OF INTEGER; dx ,dy: ARRAY 8 OF INTEGER; x ,y : INTEGER; uspeh: BOOLEAN ; PROCEDURE Inita; VAR i,j :INTEGER; BEGIN FOR i:=0 TO n−1 DO FOR j :=0 TO n−1 DO a[i,j ]:=−1 END END END Inita; PROCEDURE poskus(i: INTEGER); VAR j : INTEGER; BEGIN j :=0; REPEAT IF (x +dx [j ]>=0)&(x +dx [j ]<n) &(y+dy[j ]>=0)&(y+dy[j ]<n) &(a[x +dx [j ],y+dy[j ]]<0) THEN x :=x +dx [j ] ; y:=y+dy[j ] ; a[x ,y]:=i ; IF i < n∗n−1 THEN poskus(i+1) ; IF ∼uspeh THEN a[x ,y]:=−1 ; x :=x −dx [j ] ; y:=y−dy[j ] END ELSE uspeh:=TRUE END END; INC (j ) UNTIL uspeh OR (j =8) END poskus ; se nadaljuje 7.3. PROBLEM OSMIH DAM 161 Skakaˇ cev obhod (nadaljevanje) 45 47 47 48 49 50 51 52 53 54 56 56 57 58 59 60 62 63 63 65 66 66 67 68 69 70 71 72 74 74 PROCEDURE Izpis; BEGIN Out.String(“PROBLEM SKAKACEVEGA OBHODA”); Out.WrLn; Out.String(“Zacetna x−koordinata: ”); Out.Int(x0 ); Out.String(“Zacetna y−koordinata: ”); Out.Int(y0 ); Out.WrLn; IF ∼uspeh THEN Out.String(“Ni resitve”) ELSE FOR i:=7 STEP −1 TO 0 DO FOR j :=0 TO 7 DO Out.Int(a[i,j ]);Out.String(“;”) END; Out.WrLn END END END Izpis; BEGIN dx [0]:=1; dx [1]:=2; dx [2]:=2; dx [3]:=1; dx [4]:=−1; dx [5]:=−2; dx [6]:=−2; dx [7]:=−1; dy[6]:=1; dy[7]:=2; dy[0]:=2; dy[1]:=1; dy[2]:=−1; dy[3]:=−2; dy[4]:=−2; dy[5]:=−1; x :=x0 ; y:=y0 ; uspeh:=FALSE ; Inita; a[x ,y]:=0; poskus(1); Izpis END Skakac. Skakaˇ cev obhod (konec) 7.3 Problem osmih dam Problem osmih dam se prav tako kot predhodni problem nanaˇsa na podroˇcje ˇsaha, lahko pa ga uporabimo za to, da poudarimo nek pomemben vidik tehnike sestopanja. Kot smo ˇze omenili, je metoda sestopanja ˇcasovno potratna in pogosto se izkaˇze, da za doseganje praktiˇcno uporabnih rezultatov razpoloˇzljiv raˇcunalnik komaj zadostuje. Zaradi tega je potrebno pri tej metodi biti ˇse posebno pozoren na uˇcinkovitost operacij, ki se najpogosteje izvajajo, saj je od tega lahko odvisno, ali bomo dosegli praktiˇcno uporaben rezultat. Zopet imamo ˇsahovnico velikosti n × n, na katero je sedaj potrebno postaviti n ˇsahovskih dam tako, da nobena ne napada druge. Spomnimo se, da se v ˇsahu dama 162 POGLAVJE 7. SESTOPANJE PROCEDURE dopustno(x ,y:INTEGER): BOOLEAN ; (∗ Preverjanje dopustnosti postavitve dame na polje (x ,y) ∗) VAR i: INTEGER; BEGIN (∗ Preverjamo le polja z absciso, i < x ∗) (∗ preizkus, ali je vrstica dopustna ∗) i:=0; WHILE (i < x ) & (a[i,y] = 0) DO INC (i) END; IF i < x THEN RETURN FALSE END; (∗ dopustnosti stolpca ni potrebno preverjati, ker damo vedno postavimo v prazen stolpec ∗) (∗ preizkus, ali je diagonala, ki se spuˇsˇca od leve proti desni, dopustna ∗) i:=0; WHILE (i < x ) & (a[x −i−1,y+i+1] = 0) DO INC (i) END; IF i < x THEN RETURN FALSE END; (∗ preizkus, ali je diagonala, ki se dviga od leve proti desni, dopustna ∗) i:=0; WHILE (i < x ) & (a[x −i−1,y−i−1] = 0) DO INC (i) END; RETURN i = x END dopustno; Slika 7.5: Predikat dopustno pri problemu osmih dam in predstavitvi ˇsahovnice s tabelo (7.1) premika, oziroma napada po vrsticah, stolpcih in dveh vrstah diagonal (gl. sliko 7.4). Problem bomo zopet reˇsevali s sestopanjem. V proceduri poskus na sliki 7.2 se sedaj parameter i nanaˇsa na postavitev dame v i-ti stolpec. Torej bomo na prvem koraku damo postavili v 1. stolpec, na drugem v 2. stolpec itn. do n-tega koraka. Pri tem problemu je zanimiva izbira podatkovne strukture za ˇsahovnico. Na podlagi izkuˇsenj s problemom skakaˇcevega obhoda bi bila prva misel ponovno uporabiti podatkovno strukturo (7.1). V takem primeru bi lahko prisotnost dame na polju hx, yi oznaˇcili z 1, nezasedenost polja pa z 0. Predikat dopustno(x, y) (ko je dama na polju z absciso x in ordinato y) je pri tej predstavitvi prikazan na sliki 7.5. Izkaˇze pa se, da predikat dopustno lahko mnogo uˇcinkoviteje realiziramo, ˇce uporabljamo podatkovne ˇ strukture, ki neposredno vsebujejo informacijo o prostih vrsticah in diagonalah. Ce v ta namen uporabljamo naslednje podatkovne strukture: prVrst: ARRAY n OF BOOLEAN ; (∗ nezasedenost vrstic ∗) prDgRzlXY :ARRAY 2∗n−1 OF BOOLEAN ; (∗ nezasedenost diagonal s konstantno razliko komponent; indeks diagonale je x − y + n − 1 ∗) prDgVstXY :ARRAY 2∗n−1 OF BOOLEAN ; (∗ nezasedenost diagonal s konstantno vsoto komponent; indeks diagonale je x + y ∗) in jim priredimo vrednost vsakiˇc, ko postavimo neko damo, porabi predikat dopustno mnogo manj ˇcasa: 163 7.4. PROBLEM TRDNIH ZAKONOV j := 0 izbira prvega koraka korak dopusten prVrst[j] & prDgRzlXY [i − j + n − 1] & prDgVstXY [i + j] polozajDame[i] := j; prVrst[j] := FALSE ; prDgRzlXY [i − j + n − 1] := FALSE ; prDgVstXY [i + j] := FALSE ; i<n ∼uspeh prVrst[j] := TRUE ; prDgRzlXY [i − j + n − 1] := TRUE ; prDgVstXY [i + j] := TRUE ; uspeh := TRUE zapiˇsi ga ni ˇse konec izid neuspeˇsen sestop zapiˇsi “izid uspeˇsen” izbira naslednjega koraka konec seznama moˇznih korakov INC (j) j=n Slika 7.6: Zamenjave v vzorcu na sliki 7.2 pri problemu osmih dam w m0 w0 m Slika 7.7: Zakon med m in w ni trden PROCEDURE dopustno(x ,y); BEGIN RETURN prVrst[y] & prDgRzlXY [x −y+n−1] & prDgVstXY [x +y] END dopustno; (x in y, koordinati dame, ki jo nameravamo postaviti na ˇsahovnico, sta pravzaprav enaki vrednostim spremenjlivk i in j v proceduri poskus). Za konec obravnave problema osmih dam je na sliki 7.6 prikazana tabela zamenjav v vzorcu procedure poskus s slike 7.2, s katerimi ga priredimo za problem osmih dam. 164 7.4 POGLAVJE 7. SESTOPANJE Problem trdnih zakonov Na koncu bomo opisali ˇse en problem, ki ga reˇsujemo s sestopanjem, pripada pa druˇzini problemov, ki ima precejˇsen praktiˇcen pomen. Gre za probleme prirejanja (angl. matching), katerih tipiˇcen (ˇceprav ne dobesedno praktiˇcno pomemben) predstavnik je problem trdnih zakonov: je n ˇzensk in prav toliko moˇskih, ki imajo predstavnike nasprotnega spola razvrˇsˇcene po naklonjenosti (do prve(ga) ˇcuti najveˇcjo naklonjenost). Naloga je poiskati n “trdnih zakonov”, pri ˇcemer je zakon med ˇzensko w in moˇskim m netrden takrat, ko bodisi obstaja moˇski m0 , ki je poroˇcen z w0 , in ima w raje kot svojo ˇzeno w0 , obenem pa ima tudi w raje m0 kot svojega moˇza m, ali pa obstaja ˇzenska w00 , ki je poroˇcena z m00 , in ima raje m kot svojega moˇza m00 , obenem pa ima tudi m raje w00 kot svojo ˇzeno w (gl. sliko 7.7). ˇ so vrstni redi pripadnikov nasprotnega spola po naklonjenosti taki, 7.1 Primer Ce kot jih kaˇze naslednja tabela: NAKLONJENOST ˇ ˇ ZENSK DO MOSKIH w1 1 2 3 w2 2 3 1 w3 3 2 1 NAKLONJENOST ˇ ˇ MOSKIH DO ZENSK m1 2 3 1 m2 1 2 3 m3 3 1 2 seznam zakonov hw1 , m3 i, hw2 , m2 i, hw3 , m1 i ni trden, ker pri zakonu hw1 , m3 i obstaja moˇski m2 , ki ima w1 raje kot svojo ˇzeno w2 , obenem pa ima w1 raje m2 kot svojega moˇza m3 . 2 Problem lahko ponovno reˇsimo z ustreznimi zamenjavami v vzorcu na sliki 7.2. Zaˇcetni podatki so podani z dvema tabelama, nklMpriZ in nklZpriM tako, da je nklMpriZ [i, j] indeks moˇskega, ki je na i-tem mestu na seznamu naklonjenosti ˇzenske j (in z nasprotnima vlogama moˇskih in ˇzensk pri drugi tabeli). Podobno kot pri problemu osmih dam je kljuˇc do uˇcinkovite reˇsitve izbira primernih podatkovnih struktur, ki zmanjˇsujejo obseg raˇcunanja znotraj osnovne procedure poskus. Najprej se lotimo osnovne zgradbe problema. Reˇsujemo ga tako, da izberemo bodisi ˇzenske ali moˇske in pri vsakem klicu procedure poskus poskuˇsamo poroˇciti naslednjega pripadnika izbrane skupine z nekim predstavnikom nasprotnega spola. Prva (ˇse ne dokonˇcna) razliˇcica procedure poskus je prikazana na sliki 7.8. V tej razliˇcici predstavlja i i-to ˇzensko (i bi prav tako lahko predstavljal moˇskega), tabela logiˇcnih vrednosti jeSamski pa vodi evidenco o neporoˇcenih moˇskih (v primeru, ko bi i predstavljalo moˇske, bi uporabljali tabelo jeSamska). Tabela moz rabi za izpis konˇcne reˇsitve (moz[i] predstavlja moˇza ˇzenske i). Predikat Trden pa je zaenkrat ˇse nedoloˇcen. Na podlagi prej zapisane definicije lahko zapiˇsemo njegovo prvo razliˇcico v obliki, ki je prikazana na sliki 7.9. Oˇcitno je uˇcinkovitost procedure na sliki 7.9 odvisna od hitrosti raˇcunanja predikatov m je poroˇcen in ima raje i kot svojo ˇzeno in z je poroˇcena in ima raje j kot svojega 165 7.4. PROBLEM TRDNIH ZAKONOV izbira prvega koraka korak dopusten zapiˇsi ga ni ˇse konec izid neuspeˇsen sestop zapiˇsi “izid uspeˇsen” izbira naslednjega koraka konec seznama moˇznih korakov j := 0 jeSamski [j] & Trden(i, j) jeSamski [j] := F ALSE; moz [i] := j i<n ∼uspeh jeSamski [j] := T RU E; uspeh := TRUE INC (j) j=n Slika 7.8: Zamenjave v vzorcu na sliki 7.2 za problem trdnih zakonov PROCEDURE Trden(i,j :INTEGER):BOOLEAN ; VAR x ,m,z :INTEGER; BEGIN x :=0; m:=nklMpriZ [x ,i]; WHILE m # j DO IF m je poroˇcen in ima raje i kot svojo ˇzeno THEN RETURN FALSE END; INC (x ) ; m:=nklMpriZ [x ,i] END; x :=0; z :=nklZpriM [x ,j ]; WHILE z # i DO IF z je poroˇcena in ima raje j kot svojega moˇza THEN RETURN FALSE END; INC (x ) ; z :=nklZpriM [x ,j ] END ; RETURN TRUE END Trden; Slika 7.9: Preverjanje trdnosti zakona 166 POGLAVJE 7. SESTOPANJE ˇ uporabljamo tabeli idxZpriM [i, j] (indeks ˇzenske i na seznamu moˇskega j) moˇza. Ce ter idxMpriZ [i, j] (indeks moˇskega i na seznamu ˇzenske j), ki ju lahko izraˇcunamo pred prvim klicem procedure poskus, imata zapisana predikata vrednost ∼ jeSamski [m] &(idxZpriM [i, m] > idxZpriM [zena[m], m]) in 7.5 z<i &(idxMpriZ [j, z] > idxMpriZ [moz [z], z]) Naˇ cini za omejevanje pretiranega razraˇ sˇ canja iskalnega drevesa Omenili smo ˇze, da je sestopanje ponavadi ˇcasovno izredno potratno, zato je za doseganje praktiˇcno uporabnih rezultatov potrebno poskrbeti za ˇcim uˇcinkovitejˇso realizacjio posameznih operacij, oziroma za odpravljanje nepotrebnih. Na zaˇcetku smo ˇze omenili, da si lahko predstavljamo delovanje procedure poskus z drevesno strukturo, ki predstavlja zgodovino njenih klicev. Oˇcitno lahko prihranimo ˇcas, ˇce odkrijemo ˇ moˇznost za izpuˇsˇcanje doloˇcenih poddreves med postopkom iskanja. Ceprav so taki postopki zelo odvisni od posameznega problema, bomo na tem mestu opisali nekaj ukrepov, ki imajo ˇsirˇsi pomen. Preurejanje iskalnih korakov. V kolikor iˇsˇcemo neko posamezno reˇsitev, je moˇzno znotraj procedure poskus izbiro korakov preurediti tako, da na zaˇcetek zaporedja postavimo alternative, za katere je moˇzno utemeljiti, da z veˇcjo verjetnostjo pripeljejo do reˇsitve. Takrat, ko so koraki, ki vodijo do reˇsitve, neodvisni, je moˇzno preurejanje uporabiti tudi na celotnem zaporedju korakov. 7.2 Primer Vzemimo za primer problem 0-1 nahrbtnika, ki ga spremenimo v toliko, da iˇsˇcemo prvo reˇsitev z vrednostjo, ki je veˇcja od V . Seveda je ta primer s praktiˇcnega staliˇsˇca nezanimiv, ker zanj obstajajo uˇcinkovitejˇse metode od sestopanja, vendar je le primeren za ilustracijo pojmov, ki nas tu zanimajo. Problem reˇsimo s prilagojeno obliko procedure poskus, ki je prikazana na sliki 7.10. Podatke o predmetih hranimo v tabeli a z elementi, ki jim pripadata dve komponenti: .p predstavlja prostornino predmeta, .v pa njegovo vrednost. Pri vsakem klicu procedure preverimo, ali smo dosegli ˇzeleno vrednost in v primeru, ko nismo, ter ko vseh predmetov ˇse nismo porabili, kakor tudi, ko je prostor ˇse na voljo, ponovno pokliˇcemo poskus z naslednjo vrednostjo parametra ter s spremenjenima koliˇcinama p in v. Proceduro smo preizkusili na primeru, ki je prikazan na sliki 7.11. Enkrat smo zaporedje predmetov uredili na podlagi naraˇsˇcajoˇce vrednosti indeksov, drugiˇc pa 7.5 ˇ OMEJEVANJE RAZRASˇCANJA ISKALNEGA DREVESA 167 PROCEDURE poskus(i:index ; VAR a:AObj ; (∗ predmeti ∗) p, (∗ trenutna prostornina ∗) v , (∗ trenutna vrednost ∗) V , (∗ iskana vrednost ∗) P , (∗ prostorninska omejitev ∗) :INTEGER; VAR set (∗ trenutna mnoˇzica ∗):SET ) :BOOLEAN ; (∗ Iˇsˇcemo prvo reˇsitev z vrednostjo ≥ cilj ∗) BEGIN INC (stKlicev ); IF v ≥ V THEN RETURN TRUE ELSIF i>n THEN RETURN FALSE ELSE (∗ najprej predmet i poskuˇsamo vkljuˇciti ∗) IF p+a[i].p<=P THEN INCL(s,i); IF poskus(i+1,a,p+a[i].p,v +a[i].v ,V ,P ,s) THEN RETURN TRUE ELSE EXCL(s,i) END END ; (∗ . . . nato izkljuˇciti ∗) RETURN poskus(i+1,a,p,v ,V ,P ,s) END END poskus ; Slika 7.10: Procedura poskus za spremenjeni problem 0-1 nahrbtnika na podlagi padajoˇce vrednosti predmetov (in v primeru iste vrednosti na podlagi padajoˇce prostornine). Elementi tabele na sliki 7.11, ki se nanaˇsajo na prvo urejenost, imajo znak ∗, tisti, ki se nanaˇsajo na drugo urejenost, pa znak +. V tabeli je pri hitrosti jasno razvidna rahla prednost druge urejenosti. Glede na to, da postopek sestopanja iˇsˇce primerno reˇsitev problema s pregledovanjem neke drevesne strukture, lahko ˇcas postopka skrajˇsamo, ˇce pred obiskom nekega poddrevesa ugotovimo, ali je neobetavno in se v takem primeru obisku izognemo. Metode za odkrivanje neobetavnih poddreves so moˇcno odvisne od posameznega problema, zato je teˇzko podati neka sploˇsno veljavna navodila, vendar lahko nekatere znaˇcilnosti tega pristopa prikaˇzemo na primeru skakaˇcevega obhoda. Na zaˇcetku poglavja smo razvili popoln program za skakaˇcev obhod (gl. program 7.1). Program deluje tako, da sistematiˇcno preizkuˇsa vse moˇzne poteze skakaˇca, ˇ dokler ne obiˇsˇce vseh polj ˇsahovnice (ali pa dokler ne izˇcrpa vseh moˇznosti). Ce program dopolnimo tako, da iˇsˇce reˇsitve za vse moˇzne zaˇcetne poloˇzaje skakaˇca in za vse velikosti ˇsahovnice do neke zgornje meje, dobimo pri vrednosti zgornje meje 6 rezultat, ki je prikazan na sliki 7.12. Iz slike je razvidno, da najveˇcje ˇstevilo klicev procedure poskus (in z njim tudi ˇcas) strmo naraˇsˇca tako, da je za praktiˇcno zanimiv 168 POGLAVJE 7. SESTOPANJE predmet ˇst. vrednost prostornina 1 7 4 2 6 5 ˇ PORABA CASA PRI DVEH N V P ˇst.:1 2 ∗ + 3 4 11 10 + + 5 4 22 20 + + 7 5 33 30 + + 8 6 44 40 + + 9 8 55 50 +* + 11 10 66 60 +* +* 3 4 3 4 6 4 5 8 6 6 10 5 7 9 6 8 6 7 9 8 7 10 9 5 UREJENOSTIH ISKALNIH KORAKOV 3 4 5 6 7 8 9 10 pripadnost iskani mnoˇzici * * + + * * * + + + +* * * * + + +* +* +* * * + + +* +* +* +* * * + +* +* +* +* +* +* +* N : ˇstevilo klicev procedure poskus; V : minimalna iskana vrednost nahrbtnika; P : prostorninska omejitev; + se nanaˇsa na urejenost 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; ∗ pa na urejenost 6, 7, 10, 9, 5, 1, 8, 2, 4, 3 Slika 7.11: Rezultat delovanja procedure poskus na sliki 7.10 n 3 4 5 6 m 0 0 13 36 Nmax 15 2,223 1,829,421 5,224,517,158 Nmin 1 1,501 40 2,253 Navg 13 1,873 814,653 243,235,490 n, velikost ˇsahovnice m, ˇstevilo zaˇcetnih poloˇzajev, pri katerih obstaja reˇsitev ˇstevilo klicev procedure poskus pri nekem zaˇcetnem poloˇzaju: Nmax , najveˇcje Nmin , najmanjˇse Navg , povpreˇcno Slika 7.12: Podatki o delovanju programa za problem skakaˇcevega obhoda pri velikosti ˇsahovnice med 3 in 6 7.5 ˇ OMEJEVANJE RAZRASˇCANJA ISKALNEGA DREVESA 1 2 3 4 5 6 7 8 9 10 11 12 13 15 15 17 18 18 19 20 169 PROCEDURE poskus(i: INTEGER); VAR j : INTEGER; ch:CHAR; BEGIN INC (stKlicev ); j :=0; REPEAT INC (j ); IF stKlicev =preveri THEN stKlicev :=0; slpUl (n,slMeja) END; IF (x +dx [j −1]≤n)&(x +dx [j −1]≥1) &(y+dy[j −1]≤n)&(y+dy[j −1]≥1) &(a[x +dx [j −1]−1,y+dy[j −1]−1]<0) &(slMeja≥i−1) THEN INC (x ,dx [j −1]); INC (y,dy[j −1]); a[x −1,y−1]:=i ; IF i < n∗n−1 THEN poskus(i+1) ; IF ∼uspeh THEN a[x −1,y−1]:=−1; DEC (x ,dx [j −1]); DEC (y,dy[j −1]) END ELSE uspeh:=TRUE END END UNTIL uspeh OR (j =8); IF slMeja=i−1 THEN slMeja:=MAX (INTEGER) END END poskus ; Slika 7.13: Procedura poskus, ki uporablja ugotavljanje slepih ulic primer n = 8 ˇze zunaj dosega osebnega raˇcunalnika (danes, leta 1997). Odkrivanje neobetavnih poddreves. Odkrivanja neobetavnih poddreves se lotimo tako, da razmislimo, kdaj v neki smeri ni moˇzno napredovati do 23 37 reˇsitve. En primer nastopi takrat, ko obstaja neza47 42 sedeno polje, iz katerega ni moˇzno priti s skakaˇcem -1 do drugega nezasedenega polja, razen ko je v dosegu 35 11 skakaˇca neko polje, ki ima oznako n2 − 2, kar pomeni, 21 17 da je opazovano nezasedeno polje cilj zadnje poteze. V kolikor na ta naˇcin ugotovimo, da je trenutna smer napredovanja neobetavna, lahko reˇsitev podremo (se- Slika 7.14: Skakaˇcev obhod: stopimo) vsaj do poteze z indeksom, ki je enak najviˇsji sestopimo lahko vsaj do pooznaki nekega polja v dosegu skakaˇca iz opazovanega teze 47 ˇ opisano analizo opravimo za polja (gl. sliko 7.14). Ce vsa nezasedena polja, lahko trenutno reˇsitev podremo do minimalnega indeksa, ki ga dobimo s tem izraˇcunom. Procedura poskus, ki je sestavljena po tem receptu, je prikazana na sliki 7.13. Deluje tako, da na vsak preveri klic pokliˇce proceduro za ugotavljanje “slepe ulice” ˇ je postopek v slepi ulici v opisanem pomenu, priredimo (poddrevo je neobetavno). Ce spremenljivki slMeja vrednost indeksa poteze, do katere je moˇzno trenutno reˇsitev 170 POGLAVJE 7. SESTOPANJE 1 2 3 4 5 6 7 8 9 11 11 12 13 15 16 17 17 18 20 21 22 23 23 PROCEDURE slpUl (n:INTEGER;VAR mm:INTEGER) ; VAR h,i,j ,k ,m:INTEGER; BEGIN mm:=MAX (INTEGER); FOR i:=1 TO n DO FOR j :=1 TO n DO IF a[i−1,j −1]=−1 THEN h:=0; m:=−1; FOR k :=1 TO 8 DO IF (i+dx [k −1]≥1)&(i+dx [k −1]≤n) &(j +dy[k −1]≥1)&(j +dy[k −1]≤n) THEN IF a[i+dx [k −1]−1,j +dy[k −1]−1]=−1 THEN INC (h) ELSIF a[i+dx [k −1]−1,j +dy[k −1]−1]>m THEN m:=a[i+dx [k −1]−1,j +dy[k −1]−1] END END END END; IF (h=0)&(m # n∗n−2)&(mm > m) THEN mm:=m END END END END END END slpUl ; Slika 7.15: Procedura za ugotavljanje slepih ulic n 3 4 5 6 Nmax 15 2,223 1,815,772 2,815,100,202 Nmin 1 1,501 40 2,253 Navg 13 1,873 797,527 131,451,996 NB 0 0 123 217,674 n, velikost ˇsahovnice m, ˇstevilo zaˇcetnih poloˇzajev, pri katerih obstaja reˇsitev ˇstevilo klicev procedure poskus pri nekem zaˇcetnem poloˇzaju: Nmax , najveˇcje Nmin , najmanjˇse Navg , povpreˇcno NB , najveˇcje ˇstevilo ugotovljenih slepih ulic Slika 7.16: Rezultati poskusa z reˇsevanjem skakaˇcevega obhoda z ugotavljanjem slepih ulic. Vrednost konstante preveri je 50,000 7.5 ˇ OMEJEVANJE RAZRASˇCANJA ISKALNEGA DREVESA 171 podreti. Na zaˇcetku je vrednost slMeja enaka MAX(INTEGER). To ima za posledico, da se zaradi vrstice 9 na sliki 7.13 iskanje v globino ustavi in se opravi sestop do poteze slMeja. Ko algoritem sestopi do poteze slMeja, se v vrstici 19 vrednost slMeja vrne na MAX(INTEGER) in se postopek nadaljuje normalno. Procedura slpUl, ki odkriva slepe ulice, je prikazana na sliki 7.15. Kot se vidi iz primerjave tabel na slikah 7.12 in 7.16, rezultat, ki smo ga dosegli, ni ˇ nam je cilj poiskati eno samo zanemarljiv, vendar vseh moˇznosti ˇse nismo izˇcrpali! Ce reˇsitev in ˇce so reˇsitve med vsemi primeri razmeroma “gosto posejane”, lahko z zanemarjanjem nekaterih reˇsitev hitrost postopka znatno izboljˇsamo. Do zanemarjanja pride, ˇce proceduro slpUl sestavimo tako, da je nekoliko “prestroga” pri ocenjevanju poddreves in nekatera poddrevesa, ki vsebujejo reˇsitve, kljub temu razglasi za slepe ulice. V primeru, ko je takˇsnih napaˇcno ocenjenih poddreves razmeroma malo, njihova velikost (in s tem prihranek na hitrosti) pa velika, se to izplaˇca. Opisano idejo bomo realizirali tako, da bomo razglasili za slepe ulice ne le poloˇzaje, ki vsebujejo nezasedena polja z niˇc sosednjimi nezasedenimi polji, 23 48 37 temveˇc tudi poloˇzaje, ki vsebujejo nezasedena polja 47 42 s samo enim nezasedenim sosedom. Na ta naˇcin si-1 cer lahko napravimo napako, kajti moˇzno je, da v 35 11 konˇcni reˇsitvi do opazovanega polja pridemo z zadnjo 17 potezo, do sosednjega (trenutno nezasedenega) polja pa s predzadnjo, vendar je verjetnost tega majhna in ˇce do tega pojava pride, se zanaˇsamo na eksistenco Slika 7.17: Skakaˇcev obhod: drugih reˇsitev, pri katerih se s tem ne sreˇcujemo. To- sestopimo lahko vsaj do porej bomo sedaj v proceduri slpUl zamenjali vrstico 18 teze 48 z IF (h=0)&(m#n∗n−2) THEN IF mm > m THEN mm:=m END ELSIF h=1 THEN (7.2) IF mm > m+1 THEN mm:=m+1 END END Pri ˇstevilu nezasedenih sosedov h = 1 razmiˇsljamo takole: iˇsˇcemo reˇsitev, ki zagotavlja ˇ prekliˇcemo obisk opazovanega polja in nato nadaljevanje do nezasedenega polja. Ce korake, oziroma sestopimo do koraka z indeksom, ki je za ena veˇcji od najveˇcjega indeksa nekega soseda opazovanega polja, pospeˇsimo na ta naˇcin preizkus alternative, ki jo predstavlja poteza od najveˇcjega sosednjega indeksa do opazovanega polja (gl. sliko 7.17). Torej je to vrednost, ki jo priredimo spremenljivki mm (v klicu procedure slpUl je to spremenljivka slMeja). Zapisali smo, da pri iskanju skakaˇcevega obhoda z uporabo stroˇzjega ugotavljanja slepih ulic lahko pride do zanemarjanja doloˇcenih reˇsitev. Zaradi tega algoritma brez ugotavljanja slepih ulic (program 7.1) ter z natanˇcnim ugotavljanjem slepih ulic (slika 7.13) v sploˇsnem poiˇsˇceta drugaˇcno reˇsitev od algoritma s stroˇzjim ugotavljanjem slepih ulic (slika 7.13 s spremembo (7.2)). Na primer, pri n = 6 in zaˇcetnih koordinatah 172 POGLAVJE 7. SESTOPANJE n 3 4 5 6 Nmax 15 2,223 1,782,486 1,687,607 Nmin 1 1,501 40 2,253 Navg 13 1,873 706,281 492,011 NB 0 0 229 238 n, velikost ˇsahovnice m, ˇstevilo zaˇcetnih poloˇzajev, pri katerih obstaja reˇsitev ˇstevilo klicev procedure poskus pri nekem zaˇcetnem poloˇzaju: Nmax , najveˇcje Nmin , najmanjˇse Navg , povpreˇcno NB , najveˇcje ˇstevilo ugotovljenih slepih ulic Slika 7.18: Rezultati poskusa z reˇsevanjem skakaˇcevega obhoda s stroˇzjim preverjanjem slepih ulic. Vrednost konstante preveri je 50,000 35 22 31 16 33 0 30 17 34 1 24 15 21 2 23 32 11 6 18 29 20 7 14 25 3 8 27 12 5 10 28 19 4 9 26 13 3 6 17 34 31 0 16 35 4 1 18 33 5 2 7 32 25 30 8 15 26 21 12 19 27 22 13 10 29 24 14 9 28 23 20 11 Slika 7.19: Dve razliˇcni reˇsitvi istega problema skakaˇcevega obhoda h0, 0i poiˇsˇceta prva dva algoritma levo reˇsitev na sliki 7.19, medtem ko tretji algoritem reˇsitvi najde desno reˇsitev. Algoritmi z razvejitvijo in omejitvijo1 . Ta tehnika je primerna, ko s sestopanjem iˇsˇcemo optimalno mnoˇzico na podlagi neke numeriˇcne koliˇcine (torej najveˇcjo ali najmanjˇso vrednost koliˇcine) in imamo pred vsakim klicem procedure poskus moˇznost izraˇcunati neko zgornjo mejo za iskano koliˇcino po korakih, ki jih ˇse nismo opravili. V kolikor je tako izraˇcunana zgornja meja manjˇsa od najveˇcje vrednosti, ki smo jo ˇze dosegli, lahko naˇcrtovani klic procedure poskus opustimo. 7.3 Primer Kot primer bomo ponovno obravnavali 0-1 nahrbtnik, ki ga bomo reˇsevali s sestopanjem, ˇceprav za to obstaja uˇcinkovitejˇsa metoda, ki smo jo razvili v poglavju 6. Problem bomo reˇsevali na dva naˇcina: prviˇc brez upoˇstevanja omejitve za najveˇcjo 1 Angleˇ sko: Branch and bound algorithms 7.6. POVZETEK OSNOVNIH POJMOV 173 PROCEDURE poskus(i:index ; VAR a: AObj ; p, (∗ trenutna prostornina ∗) v , (∗ trenutna vrednost ∗) P , (∗ prostorninska omejitev ∗) zgM (∗ zgornja meja dodane vrednosti v bodoˇcih korakih ∗) :INTEGER); VAR s, (∗ trenutna mnoˇzica predmetov ∗) S , (∗ optimalna mnoˇzica predmetov ∗): SET ; VAR V (∗ optimalna vrednost ∗) :INTEGER; BEGIN INC (stKlicev ); (∗ i poskuˇsamo vkljuˇciti v mnoˇzico s ∗) IF p+a[i].p <= P THEN INCL(s,i); IF (i < n) & (V <v +zgM ) THEN poskus(i+1,a,p+a[i].p,v +a[i].v ,P ,zgM −a[i].v ,s,S ,V ) ELSIF v > V THEN V :=v ; S :=s END; EXCL(s,i) END; (∗ . . . nato izkljuˇciti ∗) IF (i < n) & (V <v +zgM −a[i].v ) THEN poskus(i+1,a,p,v ,P ,zgM −a[i].v ,s,S ,V ) ELSIF v > V THEN V :=v ; S :=s END END poskus ; Slika 7.20: Reˇsitev 0-1 nahrbtnika s sestopanjem dosegljivo vrednost in drugiˇc z njenim upoˇstevanjem. Prvi program prepuˇsˇcamo bralcu za vajo, procedura poskus za drugega pa je prikazanana sliki 7.20. Procedura je seveda v glavnih obrisih podobna proceduri poskus na sliki 7.10: a vsebuje podatke o predmetih, p in v sta po vrsti, trenutni prostornina ter vrednost, P pa je prostorninska omejitev. zgM je preprosto vsota vrednosti vseh predmetov z indeksi i . . . n, ki je oˇcitno zgornja meja za dodano vrednost v korakih od trenutnega do konca. s je trenutno izbrana mnoˇzica predmetov, S je najboljˇsa mnoˇzica do danega trenutka, medtem ko je V optimalna vrednost nahrbtnika. Rezultati poskusa so prikazani na sliki 7.21 7.6 Povzetek osnovnih pojmov 1. Metoda sestopanja in iskalno drevo 2. Osnovna oblika procedure poskus 3. Prirejanje procedure poskus reˇsevanju problema skakaˇcevega obhoda 4. Prirejanje procedure poskus reˇsevanju problema osmih dam 174 POGLAVJE 7. SESTOPANJE predmet ˇst. vrednost prostornina 1 7 4 2 6 5 3 4 3 4 6 4 5 8 6 6 10 5 7 9 6 8 6 7 9 8 7 10 9 5 ˇ PRIMERJAVA PORABE CASA ZA 0-1 NAHRBTNIK BREZ IN Z UPORABO ZGORNJE MEJE ZA DOSEGLJIVO VREDNOST NAHRBTNIKA N 1 138 532 899 1015 1023 1023 N: 1: 2: P: P 2 129 379 328 123 48 48 10 20 30 40 50 60 ˇst.:1 * * * * * * 2 3 4 5 6 7 8 9 pripadnost optimalni mnoˇzici * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10 * ˇstevilo klicev procedure poskus; uporaba procedure brez upoˇstevanja zgornje meje; z upoˇstevanjem zgornje meje; prostorninska omejitev. Slika 7.21: Rezultat delovanja procedure poskus na sliki 7.20 5. Prirejanje procedure poskus reˇsevanju problema trdnih zakonov 6. Naˇcini za omejevanje rasti iskalnega drevesa: (a) s spremembo vrstnega reda poskusov in (b) z ocenjevanjem obetavnosti poddreves 7. Algoritmi z razvejitvijo in omejitvijo. 7.7 Naloge 1. Spremenite programa za problema skakaˇcevega obhoda in osmih dam tako, da se izpiˇsejo vse reˇsitve. 2. Problem skakaˇcevega obhoda je soroden tim. problemu Hamiltonovega cikla: podan je usmerjen graf G = hV, Ei, pri ˇcemer je V = {v1 , v2 , . . . , vn } mnoˇzica vozliˇsˇc, E pa mnoˇzica povezav in je potrebno najti tako permutacijo zaporedja vozliˇsˇc, vi1 , vi2 , . . . , vin , da sta vij in vij+1 pri 1 ≤ j ≤ n − 1 povezana in prav ˇ je podan primer problema skakaˇcevega obhoda (z vrednostjo tako vin ter vi1 . Ce n in zaˇcetnim poljem skakaˇca), definirajte graf, ki ima Hamiltonov cikel natanko tedaj, ko obstaja reˇsitev problema skakaˇcevega obhoda. 7.7. NALOGE 175 3. V poglavju o maksimalnem pretoku in linearnem programiranju smo opisali algoritem za iskanje maksimalnega pretoka skozi omreˇzje od izvora do ponora (algoritem 5.3). Osnovni korak algoritma je iskanje nezasiˇcene poti od izvora do ponora. Uresniˇcite ta postopek s sestopanjem. 4. Spremenite program 7.1 po navodilih iz odstavka “Odkrivanje neobetavnih poddreves” in poskuˇsajte ponoviti rezultate na slikah 7.12, 7.16 in 7.18. 176 POGLAVJE 7. SESTOPANJE Poglavje 8 ISKANJE PO ZNAKOVNIH ZAPOREDJIH V tem poglavju gre podobno kot pri urejanju za enega izmed najbolj mnoˇziˇcno uporabljenih algoritmov (ali druˇzin algoritmov). Problem opiˇsemo takole: podani sta dve znakovni zaporedji, T [1 . . . n] in P [1 . . . m] in je potrebno poiskati vsa mesta, kjer se zaporedje P pojavlja kot podzaporedje v T . Z drugimi besedami, zanima nas, ali obstaja k > 0 in zaporedje indeksov si , pri i = 1, . . . , k, tako da je T [si + j] = P [j] pri j = 1, . . . , m. Vˇcasih nas zanima le s1 . Najprej bomo predstavili terminologijo. Σ∗ oznaˇcuje mnoˇzico vseh znakovnih zaporedij konˇcne dolˇzine, ki vsebujejo znake neke izbrane abecede Σ. Med taka zaporedja uvrˇsˇcamo tudi zaporedje niˇcelne dolˇzine (prazno zaporedje), ki ga oznaˇcujemo z ε. Dolˇzino zaporedja x oznaˇcujemo z |x|. Stik zaporedij x in y (x podaljˇsano z y) ˇ je u zaˇcetek zaporedja x (x = uy pri nekem y ∈ Σ∗ ), zapiˇsemo oznaˇcujemo z xy. Ce to kot u < x, ˇce pa u predstavlja konec zaporedja x (x = yu pri nekem y ∈ Σ∗ ), zapiˇsemo to kot u = x. Bodimo pozorni, da za poljubno zaporedje x velja ε < x in obenem ε = x. 8.1 Navadni algoritem za iskanje po znakovnih zaporedjih Podobno kot pri urejanju izhajamo iz nekega zaˇcetnega, “navadnega” algoritma, ki ga bomo nato izboljˇsali na dva naˇcina. Navadni algoritem za iskanje po znakovnih zaporedjih je prikazan na sliki 8.1, deluje pa tako, da pri vsakem odmiku s preveri ˇ enakost P [1 . . . m] = T [s + 1 . . . s + m] in v primeru enakosti izpiˇse resultat. Ce privzamemo, da je priˇcakovani ˇcas operacije P [1 . . . m] = T [s + 1 . . . s + m] enak Θ(m) (gl. nalogo 1), se ni teˇzko prepriˇcati, da je poraba ˇcasa algoritma 8.1 enaka Θ((n − m + 1) · m). V nadaljnjih razdelkih bomo pravkar zapisani rezultat izboljˇsali. 177 178 POGLAVJE 8. ISKANJE PO ZNAKOVNIH ZAPOREDJIH Vhod Znakovni zaporedji T [1 . . . n] in P [1 . . . m] Izhod Seznam odmikov s, pri katerih se P pojavlja v T. Postopek n:=|T |; m:=|P |; s := 0; WHILE s ≤ n−m DO j := 1 ; WHILE (j ≤ m) & (P [j ] = T [s + j ]) DO INC (j ) END ; IF j > m THEN “P se pojavlja v T z odmikom s” END; INC (s) END Slika 8.1: Navadni algoritem za iskanje po znakovnih zaporedjih. T : b a c b P : a b a b a b a a b a b a b a c 6= b b a b Slika 8.2: Primer iskanja podzaporedja v zaporedju. 8.2 Knuth-Morris-Prattov algoritem Najprej se bomo seznanili z algoritmom, ki v povpreˇcju porabi ˇcas Θ(n + m), kot uvod pa bo rabil primer na sliki 8.2. Denimo, da uporabljamo algoritem na sliki 8.1 in preizkuˇsamo odmik s = 4. Pri tem smo odkrili neenakost znakov na nakazanem mestu. Postavlja se vpraˇsanje, kateri je naslednji moˇzni kandidat za odmik? Vrednost ˇ veˇc: to, da ne pride v poˇstev, je moˇzno ugotoviti s + 1 zagotovo ne pride v poˇstev. Se k }| z s s0 | { {z k } 0 Slika 8.3: Iskanje kandidata s0 za naslednji odmik. 179 8.2. KNUTH-MORRIS-PRATTOV ALGORITEM k }| z { k}|00 z s s0 | { s00 {z } k0 Slika 8.4: Trije zaporedni kandidati za naslednji odmik. z analizo zaporedja P , ker prvi znak P ni enak drugemu znaku. V naˇsem primeru lahko kveˇcjemu upamo na uspeh pri novem odmiku s + 2 (ki pa se tudi izkaˇze za neprimernega). V sploˇsnem denimo, da smo ugotovili, da velja P [1 . . . k] = T [s + 1 . . . s + k] in P [k + 1] 6= T [s + k + 1]. s + k + 1 naj bo tekoˇci indeks, k pa dolˇzina vzorca P , na kateri smo ugotovili enakost med vzorcem in osnovnim zaporedjem. Zanima nas naslednji kandidat za odmik. To je najmanjˇsa vrednost s0 > s, pri kateri velja P [1 . . . k 0 ] = T [s0 + 1 . . . s0 + k 0 ] in s + k = s0 + k 0 (gl. sl. 8.3). V skrajnem primeru se izkaˇze, da je s0 = s + k in k 0 = 0. Ta primer je seveda najugodnejˇsi s staliˇsˇca porabe ˇcasa, ker pri iskanju zagotavlja najveˇcji pomik naprej. Oˇcitno velja P [1 . . . k 0 ] = P [1 . . . k], torej iˇsˇcemo najdaljˇsi zaˇcetek zaporedja P [1 . . . k] (dolˇzine < k), ki je obenem konec istega zaporedja. Namreˇc, v kolikor je pri novem odmiku k 0 > 0, mora biti zaˇcetek zamaknjenega zaporedja P enak koncu zaporedja T [s + 1 . . . s + k], oziroma P [1 . . . k]. (Potrebno je izbrati najdaljˇsi zaˇcetek, ki je obenem konec, ker nam taka izbira zagotavlja prvi naslednji odmik s0 .) Tisto, kar je pomembno pri postopku iskanja, je, da je velikost naslednjega moˇznega odmika odvisna le od podzaporedja P in jo je potemtakem moˇzno izraˇcunati pred zaˇcetkom postopka iskanja z nekakˇsnim “predprocesiranjem”. Odmik je odvisen od “preskakovalne funkcije” π, ki jo na podlagi pravkar povedanega definiramo kot π[q] = max{k | k < q&P [1 . . . k] = P [1 . . . q]}. (8.1) V tem primeru q predstavlja poljuben poloˇzaj znotraj podzaporedja P , do koder smo ugotovili enakost med P in zaporedjem T , medtem ko se je pri indeksu q + 1 pojavila neenakost. Razlika med trenutnim odmikom P in naslednjim moˇznim odmikom je tedaj q − π[q]. Na primer potem ko smo pri odmiku s = 4 na sliki 8.2 ugotovili neenakost pri indeksu q + 1 = 8, je naslednji moˇzen odmik dva znaka naprej, ker je π[7] = 5. Sedaj se bomo lotili naloge sestavljanja algoritma za izraˇcun π[q]. Izpeljali bomo rekurzivno relacijo za π[q], ki jo bomo neposredno prevedli v algoritem. π[q] je najveˇcji element v mnoˇzici U [q] = {k | k < q & P [1 . . . k] = P [1 . . . q]}, (8.2) 180 POGLAVJE 8. ISKANJE PO ZNAKOVNIH ZAPOREDJIH Vhod Znakovno zaporedje P [1 . . . m] Izhod Vrednosti preskakovalne funkcije π[1], π[2], . . . , π[m]. Postopek m:=|P | ; π[1]:=0 ; k :=0 ; FOR q:=2 TO m DO (∗ zaˇ cetna vrednost k je π[q − 1]; zanka, ki sledi, pregleduje elemente U [q − 1] po padajoˇ ci vrednosti in se zakljuˇ ci, ko je izpolnjen pogoj (8.4) ∗) WHILE (k > 0) & (P [k +1] # P [q]) DO k :=π[k] END ; IF P [k +1] = P [q] THEN INC (k ) END; π[q]:=k END Slika 8.5: Izraˇcun preskakovalne funkcije π. torej π[q] = max{U [q]}. Vsem mnoˇzicam U [q] bomo dodali ˇse element 0, ki je dolˇzina praznega zaporedja. Na primer v primeru na sliki 8.2 je mnoˇzica U [7] = {0, 1, 3, 5}. ˇ elemente U [q] uredimo po padajoˇci vrednosti, U [q]1 , U [q]2 , . . ., lahko ugotovimo Ce na podlagi slike 8.4, da velja U [q]j+1 = π[U [q]j ]. (8.3) Z besedami: naslednji element v U [q] dobimo tako, da uporabimo funkcijo π na trenutnem elementu U [q]. Ugotovimo lahko, da je element 0 edini element mnoˇzice U [1]. Kakˇsna pa je zveza med U [q] in U [q − 1]? Oˇcitno velja U [q] = {k | k − 1 ∈ U [q − 1], P [k] = P [q]}. (8.4) Z besedami: element U [q] je dolˇzina j nekega zaˇcetka P , ˇcigar zadnji znak je enak P [q], zaˇcetek do tega znaka pa ima dolˇzino, ki je element U [q − 1]. Algoritem za izraˇcun preskakovalne funkcije je prikazan na sl. 8.5. Po zaˇcetnem prirejanju vrednosti π[1] algoritem vstopi v zanko, ki raˇcuna π[q]. Notranja zanka pregleduje elemente U [q] in preverja pogoj P [k + 1] = P [q]. V primeru enakosti se znotraj pogojnega stavka, pred koncem zunanje zanke, vrednost k poveˇca. Zanimiv je tudi naˇcin izraˇcuna ˇcasa, ki ga porabi algoritem na sliki 8.5. Iz analize zunanje zanke FOR na sliki ugotovimo, da stavka zunaj notranje zanke WHILE porabita konstanten ˇcas; torej je osnovni problem ugotoviti, kolikokrat se ponovi jedro notranje zanke. Pri vsakem njegovem izvajanju se vrednost k zmanjˇsa, ker velja 8.3. BOYER IN MOOREOV ALGORITEM 181 Vhod Znakovni zaporedji P [1 . . . m] in T [1 . . . n]. Izhod Seznam odmikov, s katerimi se P pojavlja znotraj T . Postopek n:=|T | ; m:=|P | ; izraˇ cun π ; q:=0 FOR i:=1 TO n DO WHILE (q > 0) & (P [q+1] # T [i]) DO q:=π[q] END ; IF P [q+1] = T [i] THEN INC (q) END; IF q = m THEN “P se pojavlja z odmikom i − m” q:=π[q] END END Slika 8.6: KMP algoritem. π(k) < k. Torej je ˇstevilo ponavljanj jedra notranje zanke odvisno od vsote prirastkov vrednosti k. In ker se k med celotnim delovanjem algoritma lahko poveˇca najveˇc m − 1 krat, se tudi jedro notranje zanke med celotnim delovanjem algoritma ponovi najveˇc m − 1 krat. Torej je ˇcas, ki ga porabi algoritem na sl. 8.5 O(m). Zelo podobna analiza velja za algoritem iskanja podzaporedja P v T na sl. 8.6, tako da je skupna poraba obeh algoritmov O(m + n). Pri opisani analizi si lahko predstavljamo, kot da pogojni stavek znotraj zanke FOR v algoritmu na sliki 8.1 ustvarja nek “kapital” (vrednost k), ki ga lahko zanka WHILE porabi in torej, ˇce nas zanima, koliko kapitala porabi WHILE, je to moˇzno izraˇcunati tako, da ugotovimo, kolikokrat se izvaja pogojni stavek znotraj zanke FOR. 8.3 Boyer in Mooreov algoritem Drugi algoritem, ki ga bomo opisali, uporablja za pospeˇsevanje iskanja dvoje hevristiˇcnih pravil in je posebno primeren, kadar je vzorec P razmeroma dolg in abeceda Σ razmeroma velika. V takem primeru se pogosto izkaˇze, da je ta algoritem najhitrejˇsi. Algoritem je prikazan na sliki 8.7 in pri njem predvsem opazimo precejˇsno podobˇ odstranimo vrstici 2 in 3 ter zamenjamo nost z navadnim algoritmom na sliki 8.1. Ce 9–11 z INC (s) END, 182 POGLAVJE 8. ISKANJE PO ZNAKOVNIH ZAPOREDJIH 1 2 3 4 5 6 7 8 9 10 12 13 n := |T | ; m := |P | ; s := 0 ; λ := Zadnji indeks znaka(P ,m,Σ) ; γ := Zadnji indeks konca podzaporedja(P ,m) ; WHILE s ≤ n − m DO j := m ; WHILE (j > 0) & (P [j ] = T [s + j ]) DO DEC (j ) END ; IF j = 0 THEN “Vzorec se pojavlja z odmikom s” INC (s,γ[0]) ELSE INC (s,MAX (γ[j ],j −λ[T [s+j ]])) END END Slika 8.7: Boyer in Mooreov algoritem T : t P : n a v p a a r d o e s n t 6= o s t s t e r e (a) Prva neenaka znaka (od zadaj) v besedilu in vzorcu sta presledek in o T : t P : n a a v p a r d o e s n t 6= o s t s t e r e (b) Hevristiˇ cno pravilo “slabih znakov” predlaga pomik naprej za 6 mest T : P : t n a a v p a r d o e n s t s t 6= o s e r e t (c) Hevristiˇ cno pravilo “dobrih koncev podzaporedij” predlaga pomik naprej za 3 mest Slika 8.8: Iskanje vzorca a prostost v besedilu t navaden stere z Boyer in Mooreovim algoritmom dobimo enak algoritem le, da se preizkus enakosti izvaja od zadaj. Oˇcitno sta funkciji λ in γ bistveni za delovanje algoritma, zato se bomo lotili njune razlage. Prva realizira hevristiˇcno pravilo “slabih znakov”, druga pa pravilo “dobrih koncev podzaporedij”. Obe pravili sta zasnovani “varno” v pomenu, da Boyer in Mooreov algoritem nikoli ne spregleda kakega moˇznega odmika vzorca v zaporedju, ki ga preiskujemo. Bistvo algoritma je, da v primeru neuspele primerjave med vzorcem in besedilom pride do pomika vzorca naprej. Ker pa vˇcasih algoritem realizira pomik, ki v danih okoliˇsˇcinah ni optimalen, pravimo, da sta pravili, na podlagi katerih se raˇcuna pomik, “hevristiˇcni”. Delovanje algoritma na konkretnem primeru je prikazano na sliki 8.8. V vsakem poloˇzaju primerjamo vzorec z ustreznim kosom zaporedja od desnega konca vzorca proti zaˇcetku in se ustavimo na prvem mestu, kjer ugotovimo neenakost znakov ali pa levo od zaˇcetka vzorca v primeru enakosti [gl. sliko 8.8(a)]. Nato vsako od omenjenih 183 8.3. BOYER IN MOOREOV ALGORITEM j m−k k m pomik (a) Primer j ≤ m − k k m−k j m pomik (b) Primer j > m − k Slika 8.9: Delovanje pravila “dobrih koncev podzaporedij” FOR a ∈ Σ DO λ[a] := 0 END ; FOR j := 1 TO m DO λ[P [j ]] := j END ; RETURN λ(P ,m,Σ) Slika 8.10: Izraˇcun funkcije λ (zadnji indeks nekega znaka v vzorcu P ) pravil predlaga neko vrednost za dolˇzino pomika vzorca naprej, izbere pa se veˇcja. Na primer, pravilo “slabih znakov” na sliki 8.8(b) predlaga pomik naprej za 6 mest, oziroma za razdaljo do prvega znaka, ki je enak “slabemu znaku” v besedilu, ki ga preiskujemo. To pravilo pravzaprav realiziramo v nekoliko spremenjeni obliki, ki prispeva k uˇcinkovitosti: predlog za pomik naprej je j − λ(T [j]), ˇce je j mesto, kjer smo ugotovili neenakost, vrednost funkcije λ(a) pa je enaka zadnjemu indeksu znaka a v vzorcu P . Na ta naˇcin se lahko zgodi, da pravilo predlaga negativen pomik naprej (v primeru, ko velja λ(T [j]) > j). Vendar, ker drugo pravilo vedno predlaga pozitiven pomik, je dejanski pomik pozitiven. Pravilo “dobrih koncev podzaporedij” predlaga pomik naprej za dolˇzino, ki zagotavlja, da so vsi znaki “dobrega konca podzaporedja” P [j + 1], . . . , P [m] enaki istoleˇznim znakom zamaknjenega vzorca. To pa je, najmanjˇsa vrednost m − k, kjer je k dolˇzina nekega zaˇcetka Pk , tako da je bodisi j ≤ m − k in velja Pk = P [j + 1], . . . , P [m] ali pa j > m − k in velja P [j + 1], . . . , P [m] = Pk ; (8.5) (gl. sliko 8.9, v obeh primerih sta enaka dela vzorca zasenˇcena). V konkretnem primeru na sliki 8.8(c) je predlog za pomik naprej 3, na podlagi zapisanega pa je dejanski pomik M AX(6, 3) = 6. Sedaj se najprej lotimo izraˇcuna funkcije λ. Algoritem je prikazan na sliki 8.10 in ne potrebuje dodatnih pojasnil. 184 POGLAVJE 8. ISKANJE PO ZNAKOVNIH ZAPOREDJIH P : k m−k j m (a) k je konec najdaljˇsega zaˇcetka P , ki se konˇca enako kot P PR : k m − j = π R (l) m−k m l =m−j+m−k (b) Zrcalna slika P Slika 8.11: Izraˇcun γ(j) v primeru j > m − k 1 2 3 4 5 6 7 8 9 10 12 12 13 π := Preskakovalna funkcija(P ,m) ; P 0 := P R ; (∗ Zrcalna slika P ∗) π R := Preskakovalna funkcija(P 0 ,m) ; FOR j := 0 TO m DO γ[j ] := m − π[m] END ; FOR l := 1 TO m DO j := m − π R [l ] ; IF γ[j ] > l − π R [l ] THEN γ[j ] := l − π R [l ] END END ; RETURN γ(P ,m) Slika 8.12: Izraˇcun funkcije γ (zadnji indeks konca podzaporedja) 8.4. POVZETEK OSNOVNIH POJMOV 185 ˇ je j indeks, kjer Algoritem za izraˇcun funkcije γ pa zahteva nekoliko veˇc truda. Ce smo ugotovili neenakost, je γ(j) definirano s pravilom (8.5). Kadar je j ≤ m − k, je oˇcitno k = π(m), kjer je π preskakovalna funkcija, ki jo poznamo iz algoritma KMP [gl. (8.1) in sliko 8.9(a)]. Za primer j > m − k pa podaja obrazloˇzitev izraˇcuna γ(j) slika 8.11: ˇce vzorec P zrcalimo, ugotovimo, da velja m − j = π R (l), pri ˇcemer je π R preskakovalna funkcija zrcaljenega vzorca, za l pa velja l = m−j +m−k = 2m−j −k. Algoritem za γ(j) je prikazan na sliki 8.12. Glede na to, da vnaprej ne vemo, ali pri nekem j velja primer (a) oziroma (b) na sliki 8.9, izraˇcunamo obe vrednosti in izberemo manjˇso. Prva vrednost se raˇcuna v vrsticah 4–6, druga pa v 7–12. Pri drugem izraˇcunu pa prav tako vnaprej ne poznamo vrednosti l, ki ustreza nekemu indeksu j. Zato sproˇzimo izraˇcun pri vseh vrednostih l v intervalu [1,m], pri vsaki vrednosti izraˇcunamo π R (l), nato iz slednje vrednosti dobimo j in na podlagi tega morebiti popravimo vrednost γ(j). Kot zadnjo pripombo, ki se nanaˇsa na algoritem KMP ter Boyer in Mooreov algoritem, lahko zapiˇsemo, da v primerjavi s svojo majhno velikostjo zahtevajo razmeroma veliko miselnega napora za to, da jih razumemo. 8.4 Povzetek osnovnih pojmov 1. Navadni algoritem in ocena njegove porabe ˇcasa 2. Algoritem Knutha, Morrisa in Pratta. Preskakovalna funkcija in njen izraˇcun. Ocena porabe ˇcasa 3. Boyer in Mooreov algoritem. Hevristiˇcna pravila za israˇcun naslednjega odmika. Utemeljitev pravilnosti algoritma. Opis funkcij λ in γ. 8.5 Naloge 1. Ocenite najveˇcjo porabo ˇcasa navadnega algoritma za iskanje po znakovnih zaporedjih ˇ ocenite s ˇstevilom primerjanj v izstopnem pogoju notranje zanke. (slika 8.1). Cas 2. Naj bosta P [1 . . . m] in T [1 . . . n] niza, ki sta sestavljena iz nakljuˇcno izbranih znakov nekega znakovnega nabora, ki vsebuje d znakov. Dokaˇzite, da je povpreˇcno (priˇcakovano) ˇstevilo operacij primerjanja znakov v izstopnem pogoju notranje zanke navadnega algoritma na sliki 8.1 enako (n − m + 1) 1 − d−m ≤ 2(n − m + 1). 1 − d−1 3. Izraˇcunajte preskakovalno funkcijo π KMP algoritma za vzorec ababbabbababbababbabb. 4. Izraˇcunajte funkciji λ in γ za vzorec P = 0101101201 in znakovni nabor Σ = {0, 1, 2}. 186 POGLAVJE 8. ISKANJE PO ZNAKOVNIH ZAPOREDJIH Poglavje 9 VZPOREDNI ALGORITMI 9.1 Uvod Glede na hiter razvoj raˇcunalniˇske tehnologije, ki se med ostalim kaˇze v zmanjˇsevanju izmer raˇcunalniˇskih sestavnih delov, postaja kot ˇcedalje zanimivejˇsa moˇznost za reˇsevanje nekaterih nalog, uporaba veˇc sodelujoˇcih procesorjev, ki istoˇcasno obdelujejo vsak svoj del naloge. Zato bomo v tem poglavju na kratko predstavili osnovne ideje vzporednega raˇcunanja, kot pravimo taki uporabi raˇcunalniˇskih procesorjev. Vendar, preden lahko smiselno govorimo o vzporednem raˇcunanju, se je potrebno dogovoriti o osnovnih pojmih. Predvsem je treba takoj poudariti, da “vzporedni raˇcunalniki” ne predstavljajo enotnega, homogenega razreda, P0 temveˇc se razliˇcni modeli in praktiˇcne realizacije precej razlikujejo po svojih lastnostih in parametrih, kakor tudi, da so le podP1 Skupen druˇzina ˇsirˇse druˇzine veˇcprocesorskih raˇcunalniˇskih sistemov , ki P2 pomtemelji na uporabi veˇc sodelujoˇcih procesorjev. Prva razlika se nilnik nanaˇsa na naravo povezave med procesorji. Imamo lahko pro.. cesorje, od katerih vsak izvaja svoj program in ki si le obˇcasno . poˇsiljajo sporoˇcila. Takim veˇcprocesorskim sistemom pravimo P p−1 ˇsibko povezani veˇcprocesorski sistemi in pri zasnovi algoritmov zanje naletimo na nekatere nove probleme, vendar se postopek ne razlikuje bistveno od zasnove algoritmov za navadne eno- Slika 9.1: Raˇcunalprocesorske raˇcunalnike. Ta druˇzina veˇcprocesorskih sistemov niˇski model PRAM nas na tem mestu ne bo zanimala. Po drugi strani pa imamo veˇcprocesorske sisteme, kjer so izraˇcuni na razliˇcnih procesorjih tesno povezani med seboj in takim pravimo krepko povezani veˇcprocesorski sistemi . To druˇzino bomo enaˇcili s pojmom vzporednega raˇcunalnika, ki smo jo omenili na zaˇcetku. Opisano druˇzino vzporednih raˇcunalnikov bomo enaˇcili z raˇcunalniˇskim modelom PRAM, ki je shematiˇcno prikazan na sliki 9.1. Ime je angleˇska kratica za Parallel 187 188 POGLAVJE 9. VZPOREDNI ALGORITMI Random Access Machine, oziroma stroj z vzporednim enakopravnim dostopom do pomnilnika, sestavljen pa je iz doloˇcenega ˇstevila procesorjev in osrednjega pomnilnika, ki je povezan z vsemi procesorji. Vsak procesor lahko opravlja obiˇcajne operacije danaˇsnjih raˇcunalnikov (aritmetiˇcne operacije, operacije nad biti, operacije, ki preusmerjajo izraˇcun) in ima enakopraven dostop do vseh celic pomnilnika. Osnovni privzetek, ki ga bomo na tem mestu upoˇstevali, je, da so procesorji med seboj sinhronizirani, kar pomeni, da se ukazi na njih izvajajo istoˇcasno v taktu neke zunanje ure. Danes obstaja ˇze veˇc realizacij vzporednih raˇcunalnikov, ki pribliˇzno ustrezajo takemu opisu, vendar se v podrobnostih precej razlikujejo. Elementi, v katerih se razliˇcni stvarni vzporedni raˇcunalniki med seboj razlikujejo, so npr. ˇstevilo procesorjev (do nekaj tisoˇc), zmogljivost procesorjev (npr. ali ima lokalni pomnilnik) ipd. Precejˇsna raznolikost na podroˇcju vzporednih raˇcunalnikov se tudi zrcali v teoretiˇcnih ˇ pri opisanem modelu PRAM zasledimo najmanj ˇstiri podrazrede glede modelih. Ze na naˇcin branja iz pomnilnika in zapisovanja vanj: branje lahko poteka zaporedno, ko iz neke celice lahko naenkrat bere le en procesor, in vzporedno, ko naenkrat lahko bere veˇc procesorjev. Na podoben naˇcin razlikujemo med zaporednim in vzporednim zapisom. Tako imamo naslednje ˇstiri razrede vzporednih raˇcunalnikov zbzz zaporedno branje, zaporeden zapis; zbvz zaporedno branje, vzporeden zapis; vbzz vzporedno branje, zaporeden zapis; vbvz vzporedno branje, vzporeden zapis. Od teh razredov imamo najveˇckrat opravka z razredoma zbzz in vbvz. Oˇcitno je glede praktiˇcne realizacije najenostavnejˇsi razred zbzz, s staliˇsˇca programiranja pa vbvz. Pri vzporednem zapisu ni vnaprej oˇcitno, katera vrednost ostane zapisana v celici, potem ko vsi procesorji operacijo zakljuˇcijo. Moˇznih je veˇc dogovorov: operacija je smiselna le takrat, ko so zapisane vrednosti enake; obvelja vrednost, ki jo zapiˇse procesor z najmanjˇsim (najveˇcjim) indeksom ipd. V naˇsih primerih pa bomo uporabljali le razred zbzz in nas opisane dileme ne bodo zanimale. Naslednje pomembno vpraˇsanje pri vzporednih raˇcunalnikih je, kako jih programiramo. Z drugimi besedami, katere programske gradnike je potrebno dodati obiˇcajnim programskim jezikom, da jih lahko uporabljamo na vzporednih raˇcunalnikih. Pri tem so moˇzne razliˇcne reˇsitve, uporabili pa bomo najenostavnejˇso, ki zadoˇsˇca naˇsim potrebam. Obiˇcajnim programskim jezikom dodamo stavek PARALLEL DO(i) S1 ; S2 ; . . . ; Sn END, katerega uˇcinek je, da se zaporedje stavkov S1 ; S2 ; . . . ; Sn izvaja vzporedno (istoˇcasno) na vseh procesorjih, identiteta trenutnega procesorja pa je i. Nadalje potrebujemo stavek, s katerim preverjamo nek globalni pogoj, ki je odvisen od veˇc celic pomnilnika in ki ga je moˇzno izraˇcunati kot en ukaz: PARALLEL WHILE P DO S1 ; S2 ; . . . ; Sn END, 189 9.2. METODA PRESTAVLJANJA KAZALCEV (a) 1 1 1 1 1 (b) 2 2 2 2 1 / 0 / (c) 4 4 3 (d) 5 4 / / 0 / / 2 / 1 / 0 / 3 / 2 / 1 / 0 / Slika 9.2: Prestavljanje kazalcev 1 2 4 4 5 6 7 8 9 10 11 13 13 PROCEDURE ListRank (p:PNode); VAR q:PNode; n,i :INTEGER; BEGIN q:=p; i :=0; WHILE q # NIL DO q.d := i ; INC (i ); q := q.next END; n := i ; q := p; WHILE q # NIL DO q.d := n − q.d ; q := q.next END END ListRank ; Slika 9.3: Zaporedni algoritem za problem razdalje do konca seznama kjer je P opisane vrste pogoj. V kolikor nek stavek ni v obmoˇcju stavka PARALLEL DO, privzamemo, da se izvaja na enem samem procesorju, ki ga (nakljuˇcno) izbere operacijski sistem. 9.2 Metoda prestavljanja kazalcev Kot prvi primer naloge, ki jo je primerno reˇsevati na vzporednem raˇcunalniku, bomo opisali problem razdalje do konca seznama. Podan je seznam in pri vsakem elementu ˇzelimo zapisati razdaljo do konca seznama [gl. sliko 9.2(a)]. Tako bo zadnji element imel vsebino 0, predzadnji 1 itn. Obiˇcajen, zaporedni algoritem za to nalogo je prikazan na sliki 9.3 in se ni teˇzko prepriˇcati, da porabi ˇcas Θ(n). Algoritem uporablja nek podatkovni tip Node, ki ima zgradbo, 190 POGLAVJE 9. VZPOREDNI ALGORITMI 1 2 3 4 6 6 7 8 9 10 12 13 14 14 PROCEDURE ParListRank ; PARALLEL DO(i ) IF next[i ] = NIL THEN d [i ] :=0 ELSE d [i ] :=1 END END ; PARALLEL WHILE ∃i next[i ] 6= NIL DO PARALLEL DO(i ) IF next[i ] 6= NIL THEN INC (d [i ],d [next[i ]]) ; next[i ] := next[next[i ]] END END END END ParListRank ; Slika 9.4: Vzporedni algoritem za izraˇcun razdalje do konca seznama korak 1 2 3 4 5 6 opis izraˇcuna branje next[i] branje d[i] branje d[next[i]] zapis d[i] + d[next[i]] v d[i] branje next[next[i]] zapis next[next[i]] v next[i] Slika 9.5: Podrobnosti izraˇcuna v vrsticah 8–12 na sliki 9.4 PNode = POINTER TO Node ; Node = RECORD d : INTEGER ; next: PNode END Nalogo pa je moˇzno reˇsiti tudi z mnogo uˇcinkovitejˇsim vzporednim algoritmom, ki je prikazan na sliki 9.4. Za predstavitev seznama uporablja algoritem dve tabeli, d , next: ARRAY n OF INTEGER; Algoritem deluje tako, da na zaˇcetku priredi vsakemu elementu seznama na sliki 9.2(a) procesor in nato (istoˇcasno) zapiˇse v zadnji element seznama 0, v ostale pa 1 [vrstice 2–6 na sliki 9.4]. Nato zanka v vrsticah 7–13 na vsakem koraku opravi vzporedni izraˇcun (vrstice 8–12), ki vsakemu elementu priˇsteje vsebino naslednjega in obenem kazalec prestavi tako, da kaˇze do naslednika naslednika (vrstica 10). Izraˇcun se zakljuˇci, ko je seznam popolnoma “razvezan”. ˇ 9.3. ASOCIATIVNI PRODUKT ZACETNIH ELEMENTOV SEZNAMA 191 Pri analizi algoritma naprej ugotovimo, da opisani algoritem pripada razredu zbzz. V to se je moˇzno prepriˇcati na podlagi “smiselnega” prevoda slike 9.4 v nek niˇzji jezik, ki je prikazan na sliki 9.5. Osnovna ugotovitev je, da ˇceprav iz neke celice bere veˇc procesorjev (procesor i in njegov predhodnik), se to dogaja v zaporednih ˇcasovnih trenutkih (na primer koraka 2 in 3), tako da ne pride do vzporednega branja, oziroma zapisa. Nadaljnji koraki algoritma za primer na sliki 9.2(a) so prikazani na slikah (b)-(d). Kazalec iz prvega elementa, ki v sliki (a) kaˇze na drugega, se kot posledica stavka v vrstici 10 prestavi tako, da kaˇze na tretjega. Obenem se vsebina d[1] poveˇca za vsebino d[2]. Podobno pri drugih elementih. V konˇcnem koraku (d) so vrednosti pri elementih enake razdalji do konca prvotnega seznama, seznam pa je popolnoma “razvezan”. Sedaj se bomo prepriˇcali v pravilnost algoritma. Uporabili bomo obiˇcajno tehniko zanˇcnih invariant. Invarianta zanke v vrsticah 7–13 je, da je vsota elementov seznama, ki se zaˇcne pri elementu i, enaka razdalji do konca prvotnega seznama. To oˇcitno velja na zaˇcetku, ker smo v vsak element, razen zadnjega, zapisali vrednost 1. Invarianta se ohranja zato, ker na vsakem koraku sicer prestavimo kazalec, vendar elementu i dodamo vrednost elementa, ki smo ga na ta naˇcin izpustili. 2 V naslednjem koraku se lotimo izraˇcuna ˇstevila korakov, ki jih porabi algoritem na sliki 9.4. Zanka v vrsticah 2–6 porabi en korak, oziroma dva, ˇce preizkus obravnavamo kot loˇcen korak, medtem ko zanka v vrsticah 7–13 porabi toliko korakov, koliko je potrebno, da se prvotni seznam popolnoma “razveˇze” na posamezne elemente. Na vsakem koraku se vsak obstojeˇci podseznam razdeli na dva podseznama: na podseznam, ki povezuje sode elemente prvotnega podseznama in podseznam, ki povezuje lihe elemente. Torej je ˇstevilo korakov za to, da se seznam popolnoma razveˇze, enako Θ(log2 n). 9.3 Asociativni produkt zaˇ cetnih elementov seznama V naslednjem primeru bomo uporabili tehniko prestavljanja kazalcev pri nekem drugem izraˇcunu. Podana sta asociativna operacija ◦ in seznam elementov1 , x1 , x2 , . . . , xn , ki pripadajo mnoˇzici, na kateri je operacija definirana. Naloga je sestaviti seznam y1 , y2 , . . . , yn , tako da velja yi = x1 ◦ x2 ◦ . . . ◦ xi . Za vrednost xi ◦ xi+1 ◦ . . . ◦ xj bomo odslej uporabljali nekoliko uˇcinkovitejˇsi zapis [i, j]. Oˇcitno velja [i, j − 1] ◦ [j, k] = [i, k]. 1 Spomnimo se, da je neka operacija ◦ asociativna v primeru, ko velja x ◦ (y ◦ z) = (x ◦ y) ◦ z pri vseh x, y, z. Z drugimi besedami, neka dvojiˇska operacija je asociativna, ko vrednost “produkta” veˇ c elementov ni odvisna od postavitve oklepajev. 192 POGLAVJE 9. VZPOREDNI ALGORITMI 1 2 3 4 5 6 7 8 9 11 12 13 13 PROCEDURE ParAssocPrefix ; PARALLEL DO(i ) y[i ] :=x [i ] END ; PARALLEL WHILE ∃i next[i ] 6= NIL DO PARALLEL DO(i ) IF next[i ] 6= NIL THEN y[next[i ]] := y[i ] ◦ y[next[i ]] ; next[i ] := next[next[i ]] END END END END ParAssocPrefix ; Slika 9.6: Vzporedni algoritem za asociativni produkt zaˇcetnih elementov seznama (a) [1,1] [2,2] [3,3] [4,4] [5,5] [6,6] / (b) [1,1] [1,2] [2,3] [3,4] [4,5] / [5,6] / (c) [1,1] [1,2] [1,3] / [1,4] / [2,5] / [3,6] / (d) [1,1] / [1,2] / [1,3] / [1,4] / [1,5] / [1,6] / Slika 9.7: Asociativni produkt zaˇcetnih elementov seznama ˇ ˇ DVOJISKEGA ˇ 9.4. IZRACUN GLOBINE VOZLISˇC DREVESA 193 Nalogi pravimo asociativni produkt zaˇcetnih elementov seznama. Vzporedni algoritem zanjo je prikazan na sliki 9.6, medtem ko je primer njegovega delovanja na sliki 9.7. Algoritem je presenetljivo podoben algoritmu za razdaljo do konca seznama na sliki 9.4. Pravzaprav imata algoritma enaki zgradbi, razlikujeta se le po konˇcnih stavkih, ki se izvajajo znotraj zank. Elementi seznama imajo tri komponente: x, y in next, od katerih sta na sliki 9.7 prikazani le dve (y in next). Zopet privzemamo, da so komponente elementov zapisane v ustreznih tabelah. Tudi v tem primeru vsakemu elementu seznama priredimo nek procesor, nato pa algoritem deluje tako, da najprej zanka v vrsticah 2–4 priredi elementom y[i] vrednost x[i], za tem pa se zanka v vrsticah 5–12 izvaja, dokler se seznam popolnoma ne “razveˇze”. Jedro notranje zanke v vrsticah 8–9 se razlikuje od ustreznih stavkov v algoritmu na sliki 9.4 po tem, da medtem ko v prvem algoritmu vsak procesor prebere komponento d naslednika in jo priˇsteje k svoji komponenti d, v drugem algoritmu procesor vrednost komponente y svojega elementa primnoˇzi k isti komponenti naslednika. Podobno kot v primeru algoritma na sliki 9.4 poteka tudi dokaz pravilnosti in izpeljava ˇstevila korakov. Dokaz pravilnosti sestavimo na podlagi zanˇcne invariante, ki velja na zaˇcetku zanke v vrsticah 5–12: y[i] pred nekim korakom je enako [h + 1, i], kjer je h indeks predhodnika v seznamu, kateremu pripada i, oziroma [1, i] v primeru, ko i nima predhodnika. Na primer, na sliki 9.7(b) je indeks predhodnika elementa i = 3, 1 in zato je y[3] = [2, 3]. Dokaz veljavnosti zanˇcne invariante: pred prvim izvajanjem jedra zanke je invarianta oˇcitno veljavna. Sedaj pa denimo, da invarianta velja pred nekim izvajanjem jedra in bomo dokazali, da velja tudi potem, ko smo jedro izvrˇsili. Dokaz razpade na tri primere: procesor i ima 0, 1, ali 2 (in veˇc) predhodnikov. ˇ i nima predhodnikov, se mu komponenta y ne spremeni in torej invarianta Ce ostane v veljavi. ˇ ima i enega predhodnika, h, ima y[h] na podlagi zanˇcne invariante vrednost Ce [1, h], medtem ko ima y[i] vrednost, [h + 1, i]. V vrstici 8 torej procesor h priredi komponenti y[i] vrednost [1, h] ◦ [h + 1, i] = [1, i], v vrstici 9 pa i ostane brez predhodnika, kar pomeni, da invarianta ostane v veljavi. ˇ ima i dva ali veˇc predhodnikov, naj bo neposredni predhodnik h, predhodnik Ce predhodnika pa g. V vrstici 8 priredi procesor h komponenti y[i] vrednost [g + 1, h] ◦ [h + 1, i] = [g + 1, i], medtem ko v vrstici 9 procesor g priredi komponenti next[g] vrednost i. Ker s tem postane g predhodnik i, ostane invarianta za i veljavna. 2 9.4 Izraˇ cun globine vozliˇ sˇ c dvojiˇ skega drevesa Na koncu poglavja o vzporednem raˇcunanju bomo opisali reˇsitev problema doloˇcanja globine vozliˇsˇc dvojiˇskega drevesa. Podano je dvojiˇsko drevo in je potrebno pri vsakem vozliˇsˇcu zapisati njegovo globino, ki jo v tem primeru definiramo kot ˇstevilo vozliˇsˇc na poti do korena (gl. sliko 9.8). Glede na to, da se pri reˇsevanju naloge ne moremo izogniti obiskovanju vseh vozliˇsˇc, je najmanjˇsi ˇcas za njeno reˇsevanje z zaporednim al- 194 POGLAVJE 9. VZPOREDNI ALGORITMI 1 2 2 3 3 4 4 5 3 4 5 Slika 9.8: Drevo z vpisanimi globinami vozliˇsˇc +1 A B −1 Slika 9.9: Dva procesorja, prirejena enemu vozliˇsˇcu goritmom Θ(n), kjer je n ˇstevilo vozliˇsˇc. Kot prvo misel za vzporedni algoritem za isto nalogo si lahko predstavljamo algoritem, ki deluje tako, da vsakemu vozliˇsˇcu priredi procesor, nato koren sebi priredi globino 1 ter poˇslje sporoˇcilo o svoji globini svojima naslednikoma. Ostali procesorji delujejo tako, da ˇcakajo na sporoˇcilo od predhodnika, ˇ vsak procesor za nato vrednosti, ki jo dobijo, dodajo 1 in jo posredujejo naprej. Ce svojo operacijo potrebuje konstanten ˇcas, je ˇcas, ki ga porabi algoritem, velikostnega reda globine drevesa (dolˇzine najdaljˇse veje), kar pomeni v primeru uravnoteˇzenega drevesa Θ(log2 n), v najslabˇsem primeru (ko se drevo izrodi v eno samo vejo) pa Θ(n). Izkaˇze pa se, da je moˇzno uporabiti algoritem za asociativni produkt zaˇcetnih elementov seznama za izraˇcun globine drevesa v najslabˇsem ˇcasu Θ(log2 n). Postopamo tako, da vsakemu vozliˇsˇcu priredimo dva procesorja (slika 9.9). Procesor A opravi izraˇcun, ki ustreza vstopu v poddrevo, katerega koren je vozliˇsˇce, procesor B pa opravi izstopni izraˇcun. Vrednost, ki je prirejena procesorju A je +1, procesorju ˇ procesorje sklenemo v seznam, ki ustreza obisku vseh vozliˇsˇc drevesa od B pa −1. Ce zgoraj navzdol in od leve proti desni (gl. npr. sliko 9.10) in ˇce kot asociativno operacijo, ki se pojavlja v asociativnem produktu zaˇcetnih elementov seznama, doloˇcimo seˇstevanje (+), je [1, iA ] prav globina vozliˇsˇca, kateremu je prirejen procesor iA . Natanˇcno pravilo, kako povezujemo procesorje, je naslednje: Naj bo v neko vozliˇsˇce drevesa. Potem mu priredimo procesorja Av in Bv . 1. V primeru, ko ima v 0 naslednikov, je naslednik procesorja Av v 9.5. POVZETEK OSNOVNIH POJMOV 195 Slika 9.10: Povezanost procesorjev za primer na sliki 9.8 seznamu procesorjev, procesor Bv . V primeru pa, ko je ˇstevilo naslednikov razliˇcno od niˇc in je w prvi naslednik v (gledano z leve), je naslednik procesorja Av v seznamu procesorjev, procesor Aw . 2. V primeru, ko je v koren drevesa, Bv nima naslednikov v seznamu procesorjev, sicer pa imamo dva primera: v je drugi naslednik nekega vozliˇsˇca w (gledano z leve), ali pa je prvi naslednik. V prvem primeru (v je drugi naslednik) je naslednik procesorja Bv v seznamu procesorjev, Bw . V drugem primeru, ko je v prvi naslednik w, naj bo drugi naslednik w, vozliˇsˇce v 0 . Tedaj je naslednik procesorja Bv v seznamu procesorjev, Av0 . 9.5 Povzetek osnovnih pojmov 1. Vzporedno raˇcunanje. Model raˇcunalnika PRAM 2. Osnovni razredi vzporednih raˇcunalnikov 3. Osnovni stavki vzporednega raˇcunanja 4. Problem razdalje do konca seznama in tehnika prestavljanja kazalcev 5. Asociativni produkt zaˇcetnih elementov seznama 6. Globina drevesa. 9.6 Naloge 1. Opiˇsite algoritem vrste zbzz, ki porabi ˇcas Θ(log n) in za vsak element nekega linearnega seznama dolˇzine n ugotovi, ali je srednji element (ima indeks bn/2c). 2. Opiˇsite algoritem vrste zbzz, ki porabi ˇcas Θ(log n) in ki raˇcuna asociativni produkt zaˇcetnih elementov neke tabele a[i] pri 1 ≤ i ≤ n. V tem primeru ne uporabljajte kazalcev, temveˇc izvajajte izraˇcune neposredno nad indeksi. 196 POGLAVJE 9. VZPOREDNI ALGORITMI 3. V nekem linearnem seznamu pripadajo elementi dvema razredoma, A in B. Sestavite uˇcinkovit algoritem vrste zbzz, ki seznam razdeli na dva seznama, od katerih vsak vsebuje elemente le ene vrste. Dodatek A ˇ KRATEK PRIROCNIK JEZIKA OBERON-21 A.1 Uvod Oberon-2 je sploˇsen programski jezik, ki nadaljuje tradicijo Pascala in Module-2. Njegove najpomembnejˇse odlike so bloˇcna zgradba, modularnost, moˇznost loˇcenega prevajanja posameznih modulov, statiˇcno doloˇcanje tipov, krepko preverjanje tipov (celo preko mej modulov), moˇznost razˇsirjanja tipov ter moˇznost deklaracije procedur, ki so pridruˇzene tipom. Moˇznost razˇsirjanja tipov pomeni, da je Oberon-2 objektno usmerjen jezik. Objekt je spremenljivka, ki pripada nekemu abstraktnemu podatkovnemu tipu, in ki ji pripadajo (katere vrednost se sestoji iz) doloˇceni podatki ter procedure, ki uporabljajo te podatke. Abstraktni podatkovni tipi so deklarirani kot razˇsirljivi zapisi. Posebnost Oberona-2 je, da je veˇcina pojmov, ki se pojavljajo v objektno usmerjenih jezikih, definirana z ustaljeno terminologijo imperativnih jezikov, kar ima za posledico manjˇse ˇstevilo poimenovanj za sorodne pojme. Razdelek A.12 definira nekatere pojme, ki so potrebni za pojasnilo pravil o preverjanju tipov v Oberonu-2. Na mestih, kjer te pojme uporabljamo, jih piˇsemo v poˇsevnem tisku, zato da nakaˇzemo njihov poseben pomen. A.2 Sintaksa Sintakso jezika Oberon-2 opisujemo s tim. razˇsirjenim Backus-Naurovim formalizmom (EBNF). Osnovne lastnosti tega zapisa so, da nakazujemo alternative z navpiˇcno ˇcrto | , dele zapisa, ki niso obvezni (so pa dovoljeni) s pravokotnima oklepajema [ ter ], ponavljajoˇce se dele zapisa (pri ˇcemer je ˇstevilo ponavljanj lahko tudi 0) pa z zavitima oklepajema { ter }. Sintaktiˇcne zvrsti (oziroma nekonˇcne simbole) zapisujemo z velikimi zaˇcetnicami (npr. 1 Priˇ cujoˇ ci sestavek je prevod dela M¨ osenb¨ ock in N. Wirth [13]. Delo ni namenjeno kot uˇ cbenik jezika temveˇ c le kot priroˇ cnik. Torej je namenjeno bralcu, ki jezik ˇ ze pozna in si ˇ zeli le osveˇ ziti in natanˇ cno priklicati v spomin razliˇ cne pojme. 197 198 JEZIK OBERON-2 Statement), medtem ko simbole konˇcnega zapisa (oziroma konˇcne simbole) zapisujemo bodisi z malimi zaˇcetnicami, kadar gre za mnoˇzice (npr. ident), bodisi z znakovnimi zaporedji, ki so sestavljena iz samih velikih ˇcrk (npr. BEGIN), bodisi jih zapisujemo v obliki poljubnih znakovnih zaporedij v navednicah (npr. “:=”)2 . A.3 Osnovni elementi jezika ter njihova predstavitev Znakovna predstavitev konˇcnih simbolov jezika je definirana na podlagi znakovnega nabora ASCII. Vrste konˇcnih simbolov so naslednje: imena, ˇstevila, znakovna zaporedja, operatorji ter loˇcila. Poleg teh elementov program sestavljajo tudi ˇse komentarji. Vedno upoˇstevamo tudi naslednji pravopisni pravili: presledek ter konec vrstice se ne sme pojavljati znotraj simbola (razen pri komentarjih ter znakovnih zaporedjih). Ta dva elementa upoˇstevamo le kot loˇcila med simboli. Drugo pravilo je, da razlikujemo med velikimi in malimi ˇcrkami. 1. Imena (angl. identifiers) so zaporedja ˇcrk in ˇstevk, pri ˇcemer mora biti prvi znak ˇcrka. ident → letter { letter | digit } Primeri: x Scan Oberon2 GetSymbol firstLetter ˇ 2. Stevila (angl. Numbers) so nenegativna cela ˇstevila ali realne konstante. Tip celoˇstevilˇcne konstante je minimalen tip, ki mu konstanta pripada (gl. A.6.1). Ko je konstanta zapisana s konˇcnico H, je predstavitev ˇsestnajstiˇska, sicer desetiˇska. Realno ˇstevilo je vedno zapisano z decimalno piko. Morebiti vsebuje tudi red velikost ˇ (angl. Scale Factor) v desetiˇskem zapisu. Crki E ali D pomenita “krat 10 na potenco”. Realno ˇstevilo pripada tipu REAL, razen v primeru, ko red velikosti vsebuje ˇcrko D. V tem primeru pripada tipu LONGREAL. number → integer | real integer → digit { digit } | digit { hexDigit } “H” real → digit{ digit } “.” { digit } [ScaleF actor] ScaleFactor → ( “E” | “D” ) [ “ + ” | “ − ” ] digit { digit } hexDigit → digit | “A” | “B” | “C” | “D” | “E” | “F” digit → | “0” | “1” | “2” | “3” | “4” | “5” | “6” | “7” | “8” | “9” Primeri: 1991 0DH 12.3 4.567E8 0.57712566D − 6 INTEGER SHORTINT REAL REAL LONGREAL 1991 13 12.3 456700000 0.00000057712566 3. Znakovne konstante (angl. Character Constants) predstavljamo z zaporedno ˇstevilko znaka, ki ji sledi znak X. character → digit { hexDigit } “X” 2 Torej bodimo pozorni: konˇ cni simboli so lahko sestavljeni iz veˇ cˇ crk. A.4. DEKLARACIJE TER PRAVILA O VELJAVNOSTI OBJEKTOV 199 4. Znakovna zaporedja (angl. strings) so zaporedja znakov v enojnih navednicah ’ ali dvojnih navednicah ”. Zaˇcetna navednica mora biti enaka zakljuˇcni navednici in se ne sme pojavljati ˇ znotraj zaporedja. Stevilu znakov v zaporedju pravimo tudi dolˇzina zaporedja. Znakovno zaporedje dolˇzine 1 lahko uporabljamo povsod, kjer je dovoljena znakovna konstanta (in tudi nasprotno). string → “”” { char } “”” | “’” { char } “’” Primeri: ”Oberon-2” ”Don’t worry!” ”x” 5. Operatorji in loˇcila (angl. Operators and Delimiters) so posebni znaki, pari znakov ali rezervirane besede, ki so naˇsteti spodaj. Rezervirane besede piˇsemo izkljuˇcno z velikimi ˇcrkami in jih ne smemo uporabljati kot imena. + − ∗ / ∼ & . , ; | ( [ { := ∧ = # < > <= >= .. : ) ] } ARRAY BEGIN BY CASE CONST DIV DO ELSE ELSIF END EXIT FOR IF IMPORT IN IS LOOP MOD MODULE NIL OF OR POINTER PROCEDURE RECORD REPEAT RETURN THEN TO TYPE UNTIL VAR WHILE WITH 5. Komentarji (angl. Comments) so poljubna znakovna zaporedja, ki se priˇcenjajo z uvodnim znakovnim parom (*, konˇcujejo pa z *) in ki jih lahko postavimo med poljubnima dvema simboloma v programu. Komentarje lahko gnezdimo in ne vplivajo na pomen programa. A.4 Deklaracije ter pravila o veljavnosti objektov Vsako ime, ki se pojavlja v programu, moramo napovedati z ustrezno deklaracijo, razen, ko gre za vnaprej deklarirana imena. Z deklaracijami prav tako doloˇcamo nekatere nespremenljive lastnosti objektov, kot so, ali je konstanta, tip, spremenljivka ali procedura. Nato ime uporabljamo zato, da se sklicujemo na imenovani objekt. Objekt x je veljaven od mesta deklaracije do konca bloka (modula, procedure ali zapisa), ki mu deklaracija pripada. Pravimo, da je v tem bloku objekt x lokalen. Objekt x ni veljaven v vgnezdenih blokih, ki vsebujejo objekte z istim imenom. Pravila o veljavnosti so naslednja: 1. Na poljubnem mestu v programu eno in isto ime ne more oznaˇcevati veˇc kot en objekt (z drugimi besedami: v bloku ne moremo deklarirati nekega imena veˇckrat). 2. Na objekt se lahko sklicujemo le znotraj njegovega podroˇcja veljavnosti. 3. Nek tip T , ki ima obliko POINTER TO T1 (gl. A.6.3) lahko deklariramo pred veljavnostjo T1 , vendar mora biti T1 deklarirano kasneje v istem bloku (znotraj katerega je T lokalen). 200 JEZIK OBERON-2 4. Ime, ki oznaˇcuje komponento zapisa (gl. A.6.3) ali proceduro, ki je pridruˇzena tipu (gl. A.10.2), je veljavno le kot sestavni del oznaˇcevalca (angl. designator) zapisa. Imenu, ki je deklarirano v nekem modulu, lahko v deklaraciji dodamo oznako za izvoz (“∗” ali “−”), ki pove, da je ime izvoˇzeno (na voljo v drugih modulih). Ime x, ki ga izvaˇza modul M lahko uporabljamo v drugih modulih, pod pogojem, da le-ti uvaˇzajo M (gl. razdelek A.11). V modulih, ki uvaˇzajo x, se nanj sklicujemo z M.x in v takem primeru pravimo, da je M.x kvalificirano ime. Spremenljivk z imeni, ki jim je v deklaraciji pripisano “−”, ne moremo spreminjati v modulih, ki taka imena uvaˇzajo (take spremenljivke dovoljujejo le branje). Qualident → [ ident “.” ] ident IdentDef → ident [ “ ∗ ” | “ − ” ] Naslednja imena so vnaprej deklarirana, njihov pomen pa je opisan v oznaˇcenih razdelkih: ABS (A.10.3) ASH (A.10.3) BOOLEAN (A.6.1) CAP (A.10.3) CHAR (A.6.1) CHR (A.10.3) COPY (A.10.3) DEC (A.10.3) A.5 ENTIER (A.10.3) EXCL (A.10.3) FALSE (A.6.1) HALT (A.10.3) INC (A.10.3) INCL (A.10.3) INTEGER (A.6.1) LEN (A.10.3) LONG (A.10.3) LONGINT (A.10.3) LONGREAL (A.6.1) MAX (A.10.3) MIN (A.10.3) NEW (A.10.3) ODD (A.10.3) ORD (A.10.3) REAL (A.6.1) SET (A.6.1) SHORT (A.10.3) SHORTINT (A.6.1) SIZE (A.10.3) TRUE (A.6.1) Deklaracije konstant Deklaracija konstante pripiˇse nekemu imenu konstantno vrednost. ConstantDeclaration → IdentDef “=” ConstExpression ConstExpression → Expression Konstanten izraz (ConstExpression) je nek izraz, ki ga lahko izraˇcunamo le z branjem programa, ne da bi programa bilo potrebno izvajati. Operandi izraza so lahko konstante (razdelek A.8) ali vnaprej deklarirane funkcije, ki jih je moˇzno izraˇcunati v ˇcasu prevajanja. Sledijo primeri konstantnih izrazov: N = 100 limit = 2 ∗ N − 1 f ullSet = {M IN (SET )..M AX(SET )} A.6 Deklaracije tipov Podatkovni tip doloˇca mnoˇzico vrednosti, kateri lahko pripadajo vrednosti spremenljivk tega tipa, kakor tudi operatorje, ki so dovoljeni. Deklaracija tipa pripiˇse tipu ime, v primeru strukturiranih tipov (tabel in zapisov) pa tudi doloˇca zgradbo spremenljivk. TypeDeclaration → IdentDef “=” T ype Type → Qualident | ArrayT ype | RecordT ype | P ointerT ype | P rocedureT ype Primeri: Table = ARRAY N OF REAL Tree = POINTER TO Node A.6. DEKLARACIJE TIPOV 201 Node = RECORD key: INTEGER; left,right: Tree END CenterTree = POINTER TO CenterNode CenterNode = RECORD (Node) width: INTEGER; subnode: Tree END Function = PROCEDURE(x : INTEGER):INTEGER A.6.1 Osnovni tipi Osnovne tipe oznaˇcujemo z vnaprej deklariranimi imeni. Ustrezni operatorji so definirani v razdelku A.8.2, ustrezne vnaprej deklarirane procedure oziroma funkcija pa v razdelku A.10.3. Elementi (vrednosti) osnovnih tipov so definirane takole: 1. 2. 3. BOOLEAN CHAR SHORTINT 4. INTEGER 5. LONGINT 6. 7. REAL LONGREAL 8. SET logiˇcni vrednosti TRUE (resniˇcno) in FALSE (neresniˇcno) znaki razˇsirjenega znakovnea nabora ASCII (0X..0FFX) cela ˇstevila v mejah od MIN(SHORTINT) do MAX(SHORTINT) cela ˇstevila v mejah od MIN(INTEGER) do MAX(INTEGER) cela ˇstevila v mejah od MIN(LONGINT) do MAX(LONGINT) realna ˇstevila v mejah od MIN(REAL) do MAX(REAL) realna ˇstevila v mejah od MIN(LONGREAL) do MAX(LONGREAL) podmnoˇzice ˇstevil v mejah od 0 do MAX(SET) Tipi od 3 do 5 so celoˇstevilˇcni tipi, tipa 6 in 7 sta realna tipa, skupaj pa predstavljajo ˇstevilˇcne tipe. Slednji tipi tvorijo hierarhijo, v smislu, da manjˇsi tipi predstavljajo podmnoˇzice veˇcjih: LONGREAL ⊇ REAL ⊇ LONGINT ⊇ INTEGER ⊇ SHORTINT A.6.2 Tabelariˇ cni tipi Tabela je struktura, ki je sestavljena iz elementov (komponent), ki pripadajo istemu tipu, ˇ ki mu pravimo tip elementa tabele. Stevilu elementov v tabeli pravimo tudi dolˇzina tabele. Elemente tabele oznaˇcujemo z indeksi, ki so ˇstevila od 0 do dolˇzina tabele minus 1. ArrayType → “ARRAY” “[” Length { “, ” Length } “]” “OF” T ype Length → ConstExpression Zapis, ki ima obliko ARRAY L0, L1, . . . , Ln OF T pravzaprav prestavlja okrajˇsavo za ARRAY L0 OF ARRAY L1 OF 202 JEZIK OBERON-2 ... ARRAY Ln OF T Tabelam, ki so deklarirane brez dolˇzine, pravimo odprte tabele. Uporabljamo jih lahko le na mestu osnovnega tipa kazalca (gl. A.6.4), tipa elementa odprte tabele (gl. A.10.1) ali kot tip formalnega parametra (gl. A.10.1). Primeri so naslednji: ARRAY OF INTEGER ARRAY OF CHAR A.6.3 Tipi zapisov Zapis3 je struktura, ki se sestoji iz vnaprej doloˇcenega ˇstevila elementov, ki jim pravimo komponente (angl. field), ki pa lahko pripadajo razliˇcnim tipom. Deklaracija tipa zapisa doloˇca ime in tip vsake komponente. Imena komponent so veljavna od mesta deklaracije do konca deklaracije tipa zapisa, vendar jih je moˇzno uporabljati (“so vidna”) tudi kot sestavne dele oznaˇcevalcev, ki oznaˇcujejo komponente spremenljivk zapisnega tipa (gl. A.8). V primeru, ko se zapisni tip izvaˇza, se morajo komponente, ki naj bi bile vidne izven modula, ki vsebuje deklaracijo zapisnega tipa, oznaˇciti. Takim komponentam pravimo, da so javno dostopne, ostale so zasebne. RecordType → “RECORD” [ “(” BaseT ype “)” ]F ieldList { “; ” F ieldList } “END” BaseType → Qualident FieldList → [IdentList “:” T ype] Zapisni tipi so razˇsirljivi. Z drugimi besedami, nek tip lahko deklariramo kot razˇsiritev oziroma podaljˇsek drugega tipa. V naslednjem primeru: T0 = RECORD x : INTEGER END T1 = RECORD (T0 ) y: REAL END je T 1 (neposredna) razˇsiritev T 0, T 0 pa je (neposredni) osnovni tip za T 1 (gl. razdelek A.12). Razˇsirjen tip T 1 se sestoji iz vseh komponent svojega osnovnega tipa, kakor tudi iz komponent, ki so deklarirane v T 1 (gl. razdelek A.6). Vsa imena, ki so deklarirana v razˇsirjenem zapisu morajo biti razliˇcna od imen v zapisih osnovnega tipa (ali osnovnih tipov). Primeri deklaracij zapisnih tipov: RECORD day, month, year : INTEGER END RECORD name, firstname: ARRAY 32 OF CHAR; age: INTEGER; salary: REAL END 3 Opozarjamo na nevarnost dvoumnosti v slovenˇ sˇ cini, kjer uporabljamo besedo zapis tako za prevod angleˇskega izraza record kot tudi za prevod angleˇskega izraza notation. A.7. DEKLARACIJE SPREMENLJIVK A.6.4 203 Tipi kazalcev Vrednosti spremenljvik kazalˇcnega tipa P so kazalci na spremenljivke nekega tipa T . Tipu T pravimo osnovni tip kazalˇcnega tipa P , mora pa biti bodisi tabelariˇcen ali zapisni tip. Na kazalˇcne tipe se prenaˇsa relacija razˇsirjenosti osnovnega tipa: v primeru, ko je T1 razˇsiritev tipa T , P1 pa je kazalec na T1 , je tudi P1 razˇsiritev P . PointerType → “POINTER” “TO” T ype ˇ je p spremenljivka tipa POINTER TO T , ima klic vnaprej deklarirane procedure Ce NEW(p) (gl. A.10.3) uˇcinek, da se nova spremenljivka tipa T generira v prostem delu pomnilnika. V primeru, ko p pripada zapisnemu ali tabelariˇcnemu tipu, ima klic obliko NEW(p), ko pa p pripada tipu odprte tabele, ima klic obliko NEW(p, e0 , . . . , en−1 ), kjer so dolˇzine generirane tabele po ustreznih dimenzijah enake vrednostim izrazov e0 , . . . , en−1 . V obeh primerih se kazalec na novo spremenljivko priredi spremenljivki p, ki pripada tipu P . Spremenljivka p∧, na katero kaˇze p (izgovarja se kot “cilj kazalca p”), je tipa T . Spremenljivka nekega poljubnega kazalˇcnega tipa ima lahko tudi vrednost NIL, ki predstavlja odsotnost cilja (spremenljivka vsebuje prazen kazalec). A.6.5 Tipi procedur ˇ Vrednosti spremenljivk, ki pripadajo tipom procedur, so bodisi procedure, bodisi NIL. Ce spremenljivki procedurnega tipa T priredimo proceduro P , morata biti seznama formalnih parametrov (gl. razdelek A.10.1) P in T skladna (gl. razdelek A.12). Za P velja tudi, da ne more biti vnaprej deklarirana procedura, niti je lahko procedura, ki je lokalno deklarirana v ˇ velja slednja lastnost pravimo, da je P globalna procedura. drugi proceduri. Ce ProcedureType → “PROCEDURE” [ F ormalP arameters ] A.7 Deklaracije spremenljivk Z deklaracijami spremenljivk izbiramo imena spremenljivk kakor tudi njihov podatkovni tip. VariableDeclaration → IdentList “:” T ype Spremenljivke zapisnega ali kazalˇcnega tipa imajo tako statiˇcni tip (tip, ki se pojavlja v deklaraciji – pravimo mu tudi kar spremenljivkin tip), kakor tudi dinamiˇcni tip (tip kateremu pripadajo v danem trenutku med izvajanjem). Pri spremenljivkah zapisnega ali kazalˇcnega tipa je lahko dinamiˇcni tip razˇsiritev njihovega statiˇcnega tipa. Statiˇcni tip doloˇca, katere komponente zapisa so dostopne. Dinamiˇcni tip pa se uporablja za klice procedur, ki so pridruˇzene tipom (gl. A.10.2). Sledijo primeri deklaracij spremenljivk (nanaˇsajo se na primere iz razdelka A.6): i, j , k : INTEGER x , y: REAL p, q: BOOLEAN s: SET F : Function a: ARRAY 100 OF REAL w : ARRAY 16 OF RECORD name: ARRAY 32 OF CHAR; ccount: INTEGER END 204 JEZIK OBERON-2 t, c: Tree A.8 Izrazi Izrazi so jezikovne oblike, ki predstavljajo raˇcunska navodila, na podlagi katerih se uporabljajo operatorji ter funkcijske procedure s konstantami ter trenutnimi vrednostmi spremenljivk za izraˇcun novih vrednosti. Izrazi so sestavljeni iz operandov in operatorjev, uporabljamo pa lahko tudi oklepaja, da nakaˇzemo, katerim operatorjem so operandi pridruˇzeni. A.8.1 Operandi Z izjemo konstruktorjev mnoˇzic ter dobesednih konstant (ˇstevil, znakovnih konstant ter zankovnih zaporedij) se na operande sklicujemo z oznaˇcevalci (angl. designator). Oznaˇcevalec ima za osnovo ime neke konstante, spremenljivke ali procedure, ki je morebiti kvalificirano z imenom nekega modula (gl. razdelke A.4 in A.11), dodamo pa mu lahko ˇse selektorje v primeru, ko je oznaˇceni objekt komponenta neke strukture. Designator → Qualident { “.” ident | “[”ExpressionList “]” | “∧” | “(”Qualident “)”} ExpressionList → Expression { “, ” Expression } V primeru, ko a oznaˇcuje tabelo, oznaˇcuje a[e] komponento tabele z indeksom, ki je enak trenutni vrednosti izraza e. e mora pripadati celoˇstevilˇcnemu tipu. Oznaˇcevalec oblike a[e0 , e1 , . . . , en ] predstavlja a[e0 ][e1 ] . . . [en ]. V primeru, ko je r zapis, oznaˇcuje r.f komponento f zapisa r ali proceduro f , ki je pridruˇzena dinamiˇcnemu tipu r (gl. A.10.2). V primeru, ko p oznaˇcuje kazalec, oznaˇcuje p∧ spremenljivko, na katero kaˇze p (cilj spremenljivke p). Oznaˇcevalca p∧.f in p∧[e] lahko skrajˇsamo v p.f in p[e], z drugimi besedami pri zapisnem ali tabelariˇcnem selektorju je operacija premika do cilja (angl. dereferencing) privzeta. V primeru, ko je a ali r dosegljivo le za branje, velja isto tudi za a[e] ter r.f . Tipska zahteva v(T ) predstavlja zahtevo, naj bo dinamiˇcni tip v enak T (ali razˇsiritvi T ), v nasprotnem se izvajanje programa nasilno prekine (angl. abort). V oznaˇcevalcu, kjer se pojavlja v(T ), pa je privzeto, da ima v statiˇcni tip T . Tipsko zahtevo lahko uporabljamo v primeru, ko je v 1. procedurni referenˇcni parameter ali, ko je kazalec ter je 2. T razˇsiritev statiˇcnega tipa v. V primeru, ko je oznaˇceni objekt konstanta ali spremenljivka, se oznaˇcevalec nanaˇsa na njeno trenutno vrednost. V primeru, ko je oznaˇceni objekt procedura, se oznaˇcevalec nanaˇsa na proceduro, razen, ko mu sledi (morebiti prazen) seznam parametrov, kar pomeni klic procedure in v takem primeru se oznaˇcevalec nanaˇsa na vrednost, ki je rezultat klica procedure. Stvarni parametri v klicu procedure morajo ustrezati formalnim parametrom, kot to velja pri klicih nefunkcijskih procedur (gl. A.10.1). Sledijo primeri oznaˇcevalcev (gl. primere iz razdelka A.7): i a[i] w[3].name[i] t.lef t.right t(CenterN ode).subnode (INTEGER) (REAL) (CHAR) (Tree) (Tree) 205 A.8. IZRAZI A.8.2 Operatorji V izrazih razlikujemo ˇstiri skupine operatorjev na podlagi njihove prednosti (pri delovanju na operande). Operator ∼ se izvaja prvi, nato sledijo operatorji podobni mnoˇzenju, nato operatorji podobni seˇstevanju in konˇcno operatorji, ki predstavljajo relacije. Operatorji, ki so enako postavljeni na prednostni lestvici, se izvajajo od leve proti desni. Na primer x−y−z predstavlja (x − y) − z. Expression → SimpleExpression [ Relation SimpleExpression ] SimpleExpression → [ “ + ” | “ − ” ] T erm { AddOperator T erm } Term → F actor { M ulOperator F actor } Factor → Designator [ ActualP arameters ] | number | character | string | NIL | Set | “(” Expression “)” | “∼” F actor Set → “{” [ Element { “, ” Element}] “}” Element → Expression [ “..” Expression ] ActualParameters → “(” ExpressionList “)” Relation → ““=”” | “#” | “ < ” | “ <= ” | “ > ” | “ >= ” | “IN” | “IS” AddOperator → “ + ” | “ − ” | “OR” MulOperator → “ ∗ ” | “/” | “DIV” | “MOD” | “&” Razpoloˇzljivi operatorji so naˇsteti v tabelah, ki sledijo. Nekateri operatorji se lahko uporabljajo z veˇc tipi operandov, kar pomeni, da predstavljajo veˇc operacij. V takem primeru je operacija doloˇcena ˇsele s tipom operanda. V vsakem primeru morajo biti operandi skladni z operatorji za uporabo v izrazih (angl. expression compatible). Gl. razdelek A.12. Logiˇ cni operatorji OR logiˇcna disjunkcija p OR q & logiˇcna konjunkcija p&q ∼ logiˇcna negacija ∼p “ˇce je p resniˇcno, je vrednost TRUE, sicer je vrednost enaka q” “ˇce je p resniˇcno, je vrednost q, sicer je vrednost enaka FALSE” “ˇce je p resniˇcno, je vrednost FALSE, sicer je vrednost TRUE Naˇsteti operatorji se uporabljajo z logiˇcnimi vrednostmi (tipa BOOLEAN) in dajejo tudi vrednosti tipa BOOLEAN. Aritmetiˇ cni operatorji + − ∗ / DIV MOD vsota razlika produkt realni kvocient celoˇstevilˇcni kvocient ostanek po modulu Operatorji +, −, ∗ in / delujejo na operande numeriˇcnih tipov. Tip rezultata je veˇcji (obseˇznejˇsi) od tipov operandov, razen pri /, kjer je tip rezultata najmanjˇsi realen tip, ki ˇ − ali + uporabljamo kot eniˇski operator, prvi uˇcinkuje kot zamevsebuje oba operanda. Ce njava predznaka, drugi pa kot identiteta. Operanda DIV in MOD delujeta le na celoˇstevilˇcne operande in sta povezana z naslednjima formulama, pri ˇcemer je x poljubno, y pa pozitivno: 206 JEZIK OBERON-2 x = (x DIV y) ∗ y + (x MOD y) 0 ≤ (x MOD y) < y Primeri: x 5 −5 y 3 3 x DIV y 1 −2 x MOD y 2 1 Operatorji na mnoˇ zicah + − ∗ / unija razlika mnoˇzic (x − y = x ∩ y) presek simetriˇcna razlika mnoˇzic (x/y = (x − y) ∪ (y − x)) Operatorji na mnoˇzicah uporabljajo operande, ki pripadajo tipu SET, istemu tipu pa pripada tudi rezultat. Eniˇski minus oznaˇcuje komplement mnoˇzice x, torej je −x podmnoˇzica elementov med 0 in MAX(SET), ki ne pripadajo mnoˇzici x. Operatorji na mnoˇzicah niso asociativni ((a + b) − c 6= a + (b − c)). Konstruktor mnoˇzice definira mnoˇzico tako, da naˇsteje njene elemente znotraj zavitih oklepajev. Elementi so lahko cela ˇstevila v mejah med 0 in MAX(SET). Razpon a..b predstavlja vsa cela ˇstevila v intervalu [a, b]. Relacije = # < <= > >= IN IS enako neenako manjˇse od manjˇse ali enako kot veˇcje od veˇcje ali enako kot pripadnost mnoˇzici preverjanje tipa Rezultat uporabe relacije pripada tipu BOOLEAN. Relacije =, #, <, <=, > ter >= so smiselne pri operandih, ki pripadajo numeriˇcnim tipom, tipu CHAR, ali tabelariˇcnim tipom s tipom elementov CHAR, pod pogojem, da je znakovno zaporedje zakljuˇceno z znakom 0X. Relaciji = ter # sta definirani tudi na tipih BOOLEAN ter SET, kakor tudi na kazalˇcnih ter procedurnih tipih (ki vkljuˇcuje vrednost NIL). x IN s predstavlja predikat “x pripada mnoˇzici s”, pri ˇcemer x pripada celoˇstevilˇcnemu tipu, s pa tipu SET. v IS T predstavlja predikat “dinamiˇcni tip v je T (ali razˇsiritev T )” in mu pravimo preizkus tipa (angl. type test). Uporabiti ga je moˇzno 1. kadar je v spremenljivka zapisnega ali kazalˇcnega tipa in je 2. T razˇsiritev statiˇcnega tipa v. Primeri izrazov so naslednji (gl. primere iz razdelka 7): 207 A.9. STAVKI 1991 i DIV 3 ∼p OR q (i + j) ∗ (i − j) s − {8, 9, 13} i+x a[i + j] ∗ a[i − j] (0 <= i)&(i < 10) t.key = 0 k IN {i..j − 1} w[i].name <= ”John” t IS CenterN ode A.9 INTEGER INTEGER BOOLEAN INTEGER SET REAL REAL BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN Stavki Stavki oznaˇcujejo dejanja. Razlikujemo med elementarnimi in sestavljenimi stavki. Elementarni stavki ne vsebujejo delov, ki bi sami bili stavki, so pa naslednji: prirejanje (angl. assignment), klic procedure, vrnitev iz klica (angl. return) ter izhod iz bloka (angl. exit). Sestavljeni stavki vsebujejo dele, ki so tudi sami stavki, uporabljamo pa jih za oznaˇcevanje zaporedja dejanj ter za pogojno, izbirno in ponavljajoˇce se izvajanje dejanj. Stavek je lahko tudi prazen, kar pomeni odsotnost dejanj. Prazen stavek se pojavlja za to, da doseˇzemo veˇcjo enostavnost sintaksnih pravil za zaporedja stavkov. Statement → [ Assignment | P rocedureCall | If Statement | CaseStatementW hileStatement | RepeatStatement | F orStatement | LoopStatementW ithStatement | “EXIT” | “RETURN” [ Expression ] ] A.9.1 Prirejanje Prirejanje nadomesti trenutno vrednost spremenljivke z novo vrednostjo, ki jo doloˇca nek izraz. Izraz mora biti skladen s spremenljivko za uporabo v prirejanju (gl. razdelek A.12). Zapis za operator prirejanja je “:=”, izgovarjamo pa ga kot “postane”. Assignment → Designator “ := ” Expression Ko se izraz e, katerega vrednost pripada tipu Te , priredi spremenljivki v, ki pripada tipu Tv , se zgodi naslednje: 1. v primeru, ko sta Tv in Te zapisna tipa, se priredijo le tiste komponente Te , ki pripadajo tudi tipu Tv (projekcija). Dinamiˇcni tip v mora biti isti kot statiˇcni tip v in se ne spremeni kot posledica prirejanja; 2. ko sta Tv in Te kazalˇcna tipa, postane dinamiˇcni tip v enak dinamiˇcnemu tipu e; 3. ko je Tv enak ARRAY n OF CHAR in je e znakovni niz dolˇzine m < n, postane v[i] enako e[i], pri 0 ≤ i ≤ m − 1, v[m] pa postane enako 0X. Sledijo primeri prirejanj (gl. primere iz razdelka A.7): i := 0 p := i = j x := i + 1 k := log2(i + j) 208 JEZIK OBERON-2 F := log2 s := {2, 3, 5, 7, 11, 13} a[i] := (x + y) ∗ (x − y) t.key := i w[i + 1].name := ”John” t := c A.9.2 Klici procedur Klic procedure sproˇzi njeno delovanje. Klic procedure morebiti vsebuje tudi seznam stvarnih parametrov, ki nadomestijo ustrezne formalne parametre, ki jih doloˇca deklaracija procedure (gl. razdelek A.6.5). Sovpadanje med stvarnimi in formalnimi parametri se ugotavlja na podlagi njihovega poloˇzaja na seznamu stvarnih oziroma formalnih parametrov. Obstajata dve vrsti parametrov: referenˇcni in vrednostni. ˇ je nek formalni parameter referenˇcni, mora biti ustrezni stvarni parameter oznaˇcevalec Ce neke spremenljivke. V primeru, ko se oznaˇcevalec nanaˇsa na neko strukturirano spremenljivko, se ustrezni selektorji izraˇcunajo v trenutku, ko se opravi zamenjava formalnih parameˇ je nek formalni parameter vrednostni, trov s stvarnimi, torej pred sproˇzitvijo procedure. Ce mora biti ustrezni stvarni parameter nek izraz, ki se izraˇcuna pred sproˇzitvijo procedure, vrednost izraza pa se priredi formalnemu parametru (gl. razdelek A.10.1). ProcedureCall → Designator [ ActualP arameters ] Primeri: WriteInt(i ∗ 2 + 1) INC(w[k].count) t.Insert(”John”) A.9.3 Stavˇ cna zaporedja Stavˇcna zaporedja nakazujejo zaporedje dejanj, ki jih doloˇcajo stavki, ki tvorijo zaporedje. Meje med stavki nakazujemo s podpiˇcji. StatementSequence → Statement { “; ” Statement } A.9.4 Pogojni stavki IfStatement → “IF” Expression “THEN” StatementSequence { “ELSIF” Expression “THEN” StatementSequence } [ “ELSE” StatementSequence ] Pogojni stavki doloˇcajo pogojno izvajanje stavˇcnih zaporedij v primeru, ko je izpolnjen ustrezni pogoj. Pogoju, ki stoji pred stavˇcnim zaporedjem, pravimo kar pogoj za izvajanje stavˇcnega zaporedja (angl. statement sequence guard). Pogoji stavˇcnih zaporedij se raˇcunajo po vrsti, dokler ne pridemo do nekega z vrednostjo TRUE, po ˇcemer se izvaja ustrezno stavˇcno zaporedje. V primeru, ko noben pogoj nima vrednosti TRUE, se izvaja stavˇcno zaporedje, ki sledi loˇcilu ELSE (v kolikor se navedeno loˇcilo sploh pojavlja). Primer: IF (ch >= “A” & (ch <= “Z”) THEN ReadIdentifier ELSIF (ch >= “0”) & (ch <= “9”) THEN ReadNumber ELSIF (ch = 0 ”0 ) OR (ch = “0 ”) THEN ReadString A.9. STAVKI 209 ELSE SpecialCharacter END A.9.5 Izbirni stavek Izbirni stavek predpisuje izbiro ter izvajanje nekega stavˇcnega zaporedja na podlagi vrednosti nekega izraza. Najprej se izraˇcuna vrednost izraza, nato pa se izvaja stavˇcno zaporedje, katerega seznam oznak (angl. case label list) vsebuje vrednost izraza. Izbirni izraz pripada bodisi celoˇstevilˇcnemu tipu, ki vsebuje vse vrednosti, ki se pojavljajo v seznamih oznak ali pa morajo izbirni izraz in vrednosti v seznamih oznak pripadati tipu CHAR. Izbirne oznake so konstante in vsaka se lahko pojavlja le enkrat v vseh seznamih oznak. V primeru, ko se vrednost izbirnega izraza ne pojavlja v seznamih oznak, se izvaja stavˇcno zaporedje, ki sledi loˇcilu ELSE, ˇce pa tega loˇcila ni, se program nasilno prekine. CaseStatement → “CASE” Expression “OF” Case { “ | ” Case } [ “ELSE” StatementSequence ] “END” Case → [ CaseLabelList “:” StatementSequence ] CaseLabelList → CaseLabels { “, ” CaseLabels } CaseLabels → ConstExpression [ “..” ConstExpression ] Primer: CASE ch OF “A” .. “Z”: ReadIdentifier | “0” .. “9”: ReadNumber | “0 ” , 0 ”0 : ReadString ELSE SpecialCharacter END A.9.6 Stavek While Stavek While ali zanka s izstopnim pogojem na zaˇcetku, predpisuje ponavljanje izvajanja nekega stavˇcnega zaporedja dokler ima nek pogoj v obliki logiˇcnega izraza, ki mu pravimo pogoj stavka While, vrednost TRUE. Vrednost pogoja se izraˇcuna pred vsakim izvajanjem stavˇcnega zaporedja. WhileStatement → “WHILE” Expression “DO” StatementSequence “END” Primeri: WHILE i > 0 DO i:=i DIV 2 ; k :=k +1 END WHILE (t # NIL) & (t.key # i) DO t:=t.left END A.9.7 Stavek Repeat Stavek Repeat ali zanka z izstopnim pogojem na koncu predpisuje ponavljanje izvajanja nekega stavˇcnega zaporedja do trenutka, ko postane vrednost nekega pogoja v obliki logiˇcnega izraza, ki mu pravimo pogoj stavka Repeat, enaka TRUE. Stavˇcno zaporedje se izvaja najmanj enkrat. 210 JEZIK OBERON-2 RepeatStatement → “REPEAT” StatementSequence “UNTIL” Expression A.9.8 Stavek For Stavek For ali zanka s kontrolno spremenljivko predpisuje veˇckratno izvajanje nekega stavˇcnega zaporedja, pri ˇcemer je ˇstevilo izvajanj vnaprej doloˇceno, neki celoˇstevilˇcni spremenljivki (ki ji pravimo kontrolna spremenljivka stavka For) pa pri vsakem zvajanju priredimo novo vrednost, tako da se zaporedne vrednosti razlikujejo za enak prirastek. ForStatement → “FOR” ident “ := ” Expression “TO” Expression [ “BY” ConstExpression ] “DO” StatementSequence “END” Stavek FOR v := beg TO end BY step DO statements END ima isti uˇcinek kot temp := end ; v := beg ; IF step > 0 THEN WHILE v <= temp DO statements ; v := v +step END ELSE WHILE v >= temp DO statements ; v := v +step END END; temp pripada istemu tipu kot v. step je neniˇcelen konstantni izraz. V primeru, ko je step odsotno, je privzeta vrednost 1. Primera: FOR i := 0 TO 79 DO k := k + a[i] END FOR i := 79 TO 1 BY −1 DO a[i] := a[i−1] END A.9.9 Stavek Loop Stavek Loop ali enostavna zanka predpisuje ponavljanje nekega stavˇcnega zaporedja, ki se prekine po izvrˇsitvi nekega stavka Exit znotraj stavˇcnega zaporedja (gl. A.9.10). LoopStatement → “LOOP” StatementSequence “END” Primer: LOOP ReadInt(i); IF i < 0 THEN EXIT END; WriteInt(i) END Namen stavka For je zapis ponavljanj, ki vsebujejo veˇc izhodov ali pa je izhod postavljen v sredi ponavljanja. A.10. DEKLARACIJE PROCEDUR A.9.10 211 Stavka Return ter Exit Stavek Return ali izhod iz procedure nakazuje zakljuˇcek neke procedure. Oznaˇcuje ga simbol RETURN, ki mu sledi izraz v primeru, ko gre za funkcijsko proceduro. Izraz mora pripadati tipu, ki je skladen glede prirejanja (gl. razdelek A.12) s tipom rezultata funkcijske procedure, kot je zapisan v naslovu procedure (gl. razdelek A.10). Funkcijske procedure morajo vsebovati stavek Return, ki doloˇca vrednost procedure. ˇ Pri nefunkcijskih procedurah je obiˇcajni zakljuˇcek procedure na koncu jedra procedure. Ce obstajajo dodatni stavki Return, le-ti nakazujejo dodatne (in verjetno izredne) zakljuˇcke delovanja procedure. Stavek Exit ali izhod iz zanke zapiˇsemo s simbolom EXIT, nakazuje pa prenehanje ponavljanj, ki jih doloˇca stavek Loop, znotraj katerega je postavljen stavek Exit, in nadaljevanje programa s stavkom, ki sledi stavku Loop. Stavek Exit je povezan z vsebujoˇcim stavkom Loop, ˇceprav slednji ne predpisuje kakega posebnega mesta, kjer naj se stavek Exit pojavlja. A.9.11 Stavek With Stavek With ali tipska kretnica predpisuje izraˇcun nekega stavˇcnega zaporedja, ki je odvisno od rezultata nekega preizkusa tipa. Vsak primerek spremenljivke, katere tip reizkuˇsamo, je opremljen z ustrezno tipsko zahtevo. WithStatement → “WITH” Guard “DO” StatementSequence { “ | ” “DO” StatementSequence } [ “ELSE” StatementSequence ] “END Guard → Qualident “:” Qualident V primeru, ko je v spremenljivka zapisnega ali kazalˇcnega tipa in ˇce pripada statiˇcnemu tipu T0 , ima stavek WITH v : T1 DO S1 | v : T2 DO S2 ELSE S3 END naslednji uˇcinek: ˇce je dinamiˇcni tip v enak T1 , se izvaja stavek S1 , pri ˇcemer se v obravnava, kot da pripada statiˇcnemu tipu T1 , ˇce pa je dinamiˇcni tip v enak T2 , se izvaja stavek S2 , pri ˇ nobeden od zapisanih pogojev ˇcemer se v obravnava, kot da pripada statiˇcnemu tipu T2 . Ce ˇ nobeno preverjanje tipa ne da rezultata resniˇcno, se program ni izpoljnjen, se izvaja S3 . Ce nasilno prekine. Primer: WITH t: CenterTree DO i:=t.width; c:=t.subnode END A.10 Deklaracije procedur Deklaracija procedure je sestavljena iz procedurinega naslova (angl. heading) in njenega jedra. Naslov doloˇca ime procedure ter njene formalne parametre, v primeru procedur, ki so pridruˇzene tipu, pa naslov doloˇca ˇse glavni parameter (angl. receiver). Jedro procedure vsebuje deklaracije ter stavke, deklaracija procedure pa je zakljuˇcena s svojim imenom. Imamo dve vrsti procedur: nefunkcijske ali prave procedure ter funkcijske procedure. Slednje procedure sproˇzi funkcijski oznaˇcevalec, ki je del nekega izraza, imajo pa rezultat, 212 JEZIK OBERON-2 ki deluje kot operand izraza. Prave procedure pa se sproˇzijo s klicem procedure. Neka procedura je funkcijska v kolikor njeni formalni parametri doloˇcajo tip rezultata. Jedro funkcijske procedure mora vsebovati stavek Return, ki doloˇca rezultat klica procedure. Vse konstante, tipi, spremenljivke ter procedure, ki so deklarirani znotraj procedure so lokalni v proceduri. Ker lahko tudi druge procedure deklariramo kot lokalne objekte, imamo lahko opravka z gnezdenimi procedurami. Klic neke procedure v lastnem jedru, povzroˇci rekurzivno izvajanje. Objekti, ki so deklarirani v procedurinem okolju, so veljavni v vseh delih procedure, kjer njihova veljavnost ni bila izniˇcena z deklaracijo lokalnega objekta z istim imenom. ProcedureDeclaration → P rocedureHeading “; ” P rocedureBody ident ProcedureHeading → “PROCEDURE” [ Receiver ] IdentDef [ F ormalP arameters ] ProcedureBody → DeclarationSequence [ “BEGIN” StatementSequence ] “END” DeclarationSequence → { “CONST” { ConstantDeclaration “; ” } | “TYPE” { T ypeDeclaration “; ” } | “VAR” { V ariableDeclaration “; ” } } | { P rocedureDeclaration “; ” | F orwardDeclaration “; ” } ForwardDeclaractgion → “∧”“PROCEDURE”[Receiver]IdentDef [F ormalP arameters] V kolikor ima procedura glavni parameter, se ˇsteje, da je pridruˇzena ustreznemu tipu (gl. A.10.2). Predhodna deklaracija procedure (angl. forward declaraction) omogoˇca uporabo procedure pred mestom njene deklaracije. Seznama formalnih parametrov predhodne deklaracije in procedurine deklaracije morata biti skladna (gl. razdelek A.12). A.10.1 Formalni paramteri Formalni parametri so imena, ki so deklarirana v procedurinem seznamu formalnih parametrov. Ti parametri ustrezajo stvarnim parametrom v procedurinem klicu. Povezava med stvarnimi in formalnimi parametri se ugotovi ob procedurinem klicu. Obstajata dve vrsti parametrov, vrednostni in referenˇcni parametri, ki se razlikujeta po odsotnosti ali prisotnosti loˇcila VAR. Vrednostni parametri so pravzaprav lokalne spremenljivke, ki se jim priredi zaˇcetna vrednost enaka vrednosti stvarnega parametra. Referenˇcni parametri pa predstavljajo stvarne parametri, ki so spremenljivke. Podroˇcje veljavnosti formalnih parametrov je do konca procedurinega bloka, v katerem je formalni parameter deklariran. Funkcijska procedura brez parametrov ima obvezno prazen seznam parametrov (samo predklepaj in zaklepaj), kliˇce pa se prav tako s funkcijskim oznaˇcevalcem, ki ima prazen seznam parametrov. Rezultat funkcijske procedure ne more biti niti zapis niti tabela. FormalParameters → “(” [ F P Section { “; ” F P Section} ] “)” [ “:” Qualident ] FPSection → “VAR” ident { “, ” ident } “:” T ype Naj bo Tf tip nekega formalnega parametra f (privzemamo, da Tf ni odprta tabela) in ˇ je f referenˇcni parameter, mora biti Ta naj bo Ta tip ustreznega stvarnega parametra. Ce ˇ je f vrednostni parameter, isti tip kot Tf ali pa je Tf tip zapisa, Ta pa njegova razˇsiritev. Ce mora biti a skladen z f glede prirejanja (gl. razdelek A.12). V primeru, ko pa je Tf odprta tabela, mora biti a skladen z f kot tabela (gl. razdelek A.12). Dolˇzine f so enake ustreznim dolˇzinam a. Primeri deklaracij procedur: PROCEDURE ReadInt(VAR x : INTEGER); VAR i: INTEGER; ch: CHAR; A.10. DEKLARACIJE PROCEDUR BEGIN i := 0; Read (ch); WHILE (“0” <= ch) & (ch <= “9”) DO i:= 10∗i + (ORD(ch)−ORD(“0”)); Read (ch) END; x := i; END ReadInt PROCEDURE WriteInt(x : INTEGER); (∗ 0 <= x <= 100000 ∗) VAR i: INTEGER; buf : ARRAY 5 OF INTEGER; BEGIN i := 0; REPEAT buf [i] := x MOD 10; x := x DIV 10; INC (i) UNTIL x = 0; REPEAT DEC (i); Write(CHR(buf [i] + ORD(“0”)) UNTIL i = 0; END WriteInt PROCEDURE WriteString(s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i := 0; WHILE (i < LEN (s)) & (s[i] # 0X) DO Write(s[i]); INC (i) END END WriteString PROCEDURE log2 (x : INTEGER): INTEGER; VAR y: INTEGER; (∗ assume x >0 ∗) BEGIN y := 0; WHILE x > 1 DO x := x DIV 2; INC (y) END; RETURN y END log2 213 214 JEZIK OBERON-2 A.10.2 Procedure, ki so pridruˇ zene tipom Globalne procedure (torej take, ki niso deklarirane lokalno znotraj druge procedure) lahko pridruˇzimo nekemu tipu zapisa, ki je deklariran znotraj istega modula. Take procedure so pridruˇzene temu tipu. Povezavo s tipom nakazuje tip glavnega parametra v naslovu deklaracije procedure. Glavni parameter (angl. receiver) je bodisi referenˇcni parameter zapisnega tipa T ali vrednostni parameter, ki pripada tipu POINTER TO T , pri ˇcemer je T zapisnega tipa. Procedura je pridruˇzena tipu T in se obravnava, kot da je lokalna temu tipu. Receiver → “(” [ “VAR” ] ident “:” ident “)” (sintaktiˇcna kategorija Receiver se uporablja v definiciji kategorije ProcedureHeading, ki je zapisana na zaˇcetku razdelka A.10.) V kolikor je procedura P pridruˇzena tipu T0 , je implicitno pridruˇzena tudi vsakemu tipu T1 , ki je razˇsiritev T0 . Vendar lahko tipu T1 tudi izrecno pridruˇzimo proceduro P 0 , ki ima isto ime kot P , in tako prekliˇcema povezavo med P in T1 . Na ta naˇcin posane P 0 spremenjena definicija P za T1 . Formalni parametri P in P 0 morajo skladen (gl. razdelek A.12). V primeru, ko se P in T1 izvaˇzata, se mora tudi P 0 izvaˇzati. ˇ je v oznaˇcevalec in je P procedura, ki je pridruˇzena tipu, oznaˇcuje v.P tisto proceduro Ce P , ki je pridruˇzena dinamiˇcnemu tipu v (dinamiˇcna vezava). Pozorni bodimo, da je to lahko razliˇcna procedura od procedure, ki je pridruˇzena statiˇcnemu tipu v. v se prenaˇsa kot glavni parameter P po pravilih za prenos parametrov, ki so bili opisani v razdelku A.10.1. ˇ je r glavni parameter, ki pripada statiˇcnemu tipu T , oznaˇcuje r.P ∧ (predefinirano) Ce proceduro P , ki je pridruˇzena osnovnemu tipu tipa T . ˇ ima procedura, ki je pridruˇzena tipu, predhodno deklaracijo, mora biti glavni paCe rameter v predhodni deklaraciji istega tipa kot glavni parameter v deklaraciji. Seznama formalnih parametrov obeh deklaracij morata biti skladna (gl. razdelek A.12)). Primeri: PROCEDURE (t: Tree) Insert (node: Tree); VAR p, father : Tree; BEGIN p := t; REPEAT father := p; IF node.key = p.key THEN RETURN END; IF node.key < p.key THEN p := p.left ELSE p := p.right END UNTIL p = NIL; IF node.key < father .key THEN father .left := node ELSE father .right := node END node.left := NIL; node.right := NIL END Insert; PROCEDURE (t: CenterTree) Insert (node: Tree); (∗ redefinition ∗) BEGIN WriteInt(node(CenterTree).width); t.Insert↑(node) 215 A.10. DEKLARACIJE PROCEDUR (∗ calls the Insert procedure bound to Tree ∗) END Insert; A.10.3 Vnaprej deklarirane procedure Naslednji tabeli naˇstevata vse vnaprej deklarirane procedure. Nekatere izmed njih so generiˇcne, z drugimi besedami uporabne so z operandi veˇc tipov. v pomeni spremenljivko, x in n izraz, T pa tip. Funkcijske procedure Ime ABS(x) ASH(x, n) CAP(x) tip argumenta numeriˇcen tip x, n : celoˇst. tip CHAR tip rezultata isti kot x LONGINT CHAR CHR(x) ENTIER(x) celoˇst. tip realni tip CHAR LONGINT LEN(v, n) LONGINT MAX(T ) v: tabela; n: celoˇst. konst. v: tabela SHORTINT INTEGER REAL T = osnovni tip LONGINT INTEGER LONGINT LONGREAL T MIN(T ) T = SET T = osnovni tip INTEGER T T = SET celoˇst. tip CHAR LONGINT INTEGER LONGREAL poljuben tip INTEGER BOOLEAN INTEGER INTEGER SHORTINT REAL INTEGER LEN(v) LONG(x) ODD(x) ORD(x) SHORT(x) SIZE(T ) uˇ cinek absolutna vrednost aritmetiˇcni pomik (x ∗ 2n ) x je ˇcrka: ustrezna velika ˇcrka; sicer je vrednost x x-ti znak po vrsti najveˇcje celo ˇstevilo, ki ne presega x dolˇzina v po dimenziji n prva dimenzija = 0 iskot kot LEN(v, 0) identiteta najveˇcja vrednost, ki pripada tipu T najveˇcji element mnoˇzice najmanjˇsa vrednost, ki pripada tipu T 0 x MOD 2 = 1 poloˇzaj znaka x identiteta (moˇzna je tudi izguba informacije) ˇstevilo bajtov za predstavitev T 216 JEZIK OBERON-2 Prave procedure Ime ASSERT(x) tipi argumentov x: izraz tipa BOOLEAN ASSERT(x, n) x: izraz tipa BOOLEAN COPY(x, v) x: znakovno zaporedje ali tabela ;v: znakovna tabela v: celoˇstevilˇcni tip v, n: celoˇstevilˇcni tip v: SET; x: celoˇst. tip n: celoˇst. tip v: celoˇstevilˇcni tip v, n: celoˇstevilˇcni tip v: SET; x: celoˇst. tip v: kazalec na zapis ali tabelo fiksne dolˇzine kazalec na odprto tabelo xi : celoˇst. tip DEC(v) DEC(v, n) EXCL(v, x) HALT(n) INC(v) INC(v, n) INCL(v, x) NEW(v) NEW(v, x0 , . . . , xn ) uˇ cinek prekinitev programa, ˇce x ni resniˇcno prekinitev programa, ˇce x ni resniˇcno v := x v := v − 1 v := v − n v := v − x program se konˇca v := v + 1 v := v + n v := v + x nova spremenljivka v ∧ nova spremenljivka v ∧ z dolˇzinami x0 , . . . , xn Procedura COPY omogoˇca prirejanje znakovnega zaporedja ali znakovne tabele, ki vseˇ je potrebno, se prirejena vrednost buje zakljuˇcni znak 0X neki drugi znakovni tabeli. Ce skrajˇsa na dolˇzino, ki je enaka dolˇzini ciljne tabele minus 1. Ciljna tabela bo vedno vsebovala zakljuˇcni znak 0X. Pri procedurah ASSERT(x, n) ter HALT(n) je interpretacija n prepuˇsˇcena konkretnemu prevajalniku. A.11 Moduli Modul je zbirka deklaracij konstant, tipov, spremenljivk in procedur, skupaj z zaporedjem stavkov, katerih namen je prirejanje zaˇcetnih vrednosti spremenljivkam. Modul predstavlja neko besedilo (program), ki ga je moˇzno prevajati kot celoto, loˇceno od drugih programov. Module → “MODULE” ident [ ImportList ] DeclarationSequence [ “BEGIN” StatementSequence ] “END” ImportList → “IMPORT” Import { “, ” Import } “; ” Import → [ Ident “ := ” ] ident Seznam uvoˇzenih modulov (ImportList) naˇsteva module, ki jih deklarirani modul uvaˇza. ˇ modul M uvaˇza modul A in ˇce A izvaˇza ime x, se na x v M sklicujemo z A.x. Ce ˇ pa je Ce navedba uvoˇzenega modula A v obliki B := A, ima oznaˇcevalec obliko B.x. Slednje omogoˇca uporabo okrajˇsav za uvoˇzene module. Modul ne more uvaˇzati samega sebe. Imena, ki so namenjena izvozu, morajo biti v svoji deklaraciji opremljena z izvozno oznako (gl. razdelek A.4). Stavˇcno zaporedje, ki sledi simbolu BEGIN se izvaja v trenutku, ko je modul prikljuˇcen k sistemu (ko se naloˇzi), kar se zgodi po tem, ko se prikljuˇcijo uvoˇzeni moduli. Iz tega sledi, da cikliˇcni uvoz ni dovoljen. Posamezne (brezparametrske in izvoˇzene) procedure je moˇzno sproˇziti iz operacijskega sistema, predstavljajo pa ukaze slednjega. Primer: MODULE Trees; (∗ exports: A.11. MODULI Tree, Node, Insert, Search, Write, NewTree ∗) (∗ exports read−only: Node.name ∗) IMPORT Texts, Oberon; TYPE Tree∗ = POINTER TO Node; Node∗ = RECORD name−: POINTER TO ARRAY OF CHAR; left, right: Tree END; VAR w : Texts.Write; PROCEDURE (t: Tree) Insert∗ (name: ARRAY OF CHAR); VAR p, father : Tree; BEGIN p := t; REPEAT father := p; IF name = p.name↑ THEN RETURN END; IF name < p.name↑ THEN p := p.left ELSE p := p.right END UNTIL p = NIL; NEW (p); p.left := NIL ; p.right := NIL; NEW (p.name,LEN (name)+1); COPY (name,p.name↑); IF name < father .name↑ THEN father .left := p ELSE father .right := p END; END Insert; PROCEDURE (t: Tree) Search∗ (name: ARRAY OF CHAR): Tree; VAR p: Tree; BEGIN p := t; WHILE (p # NIL) & (name # p.name↑) DO IF name < p.name↑ THEN p := p.left ELSE p := p.right END END; RETURN p END Search; PROCEDURE (t: Tree) Write∗; BEGIN IF t.left # NIL THEN t.left.Write END; Texts.WriteString(w , t.name↑); 217 218 JEZIK OBERON-2 Texts.WriteLn(w ); Texts.Append (Oberon.Log, w .buf ); IF t.right # NIL THEN t.right.Write END END Write; PROCEDURE NewTree∗ (): Tree; VAR t: Tree; BEGIN NEW (t); NEW (t.name,1); t.name[0] := 0X; t.left := NIL; t.right := NIL RETURN t END NewTree; BEGIN Texts.OpenWriter (w ) END Trees. A.12 Definicije pojmov Celoˇ stevilˇ cni tipi: SHORTINT, INTEGER, LONGINT Realni tipi: REAL, LONGREAL ˇ Stevilˇ cni tipi: celoˇstevilˇcni in realni tipi Istovetnost tipov Dve spremenljivki, a in b, ki pripadata tipoma Ta in Tb , sta istega tipa, v kolikor 1. Ta in Tb predstavlja isto ime ali 2. je Ta deklarirano kot enako Tb z deklaracijo oblike Ta = Tb ali 3. a in b se pojavljata na istem seznamu imen v deklaraciji spremenljivke, komponente zapisa ali formalnih parametrov, pri ˇcemer smeta biti odprti tabeli. Enakost tipov Tipa Ta in Tb sta enaka, v kolikor sta 1. Ta in Tb ista tipa ali sta 2. Ta in Tb tipa odprtih tabel in sta tipa njunih elementov enaka ali sta 3. Ta in Tb procedurna tipa, katerih formalni parametri si ustrezajo. Vsebovanost tipov ˇ Stevilˇ cni tipi vsebujejo (vrednosti) manjˇsih ˇstevilˇcnih tipv na podlagi naslednje hierarhije: LONGREAL ⊇ REAL ⊇ LONGINT ⊇ INTEGER ⊇ SHORTINT A.12. DEFINICIJE POJMOV 219 Razˇ siritev tipov (osnovni tip) Pri deklaraciji tipa Tb = RECORD (Ta ) . . . END pravimo, da je Tb neposredna razˇsiritev tipa Ta , Tb pa je neposredni osnovni tip tipa Tb . Nek tip Tb je razˇsiritev tipa Ta (Ta je osnovni tip tipa Tb ), v kolikor 1. sta Ta in Tb ista tipa ali pa 2. je Tb neposredna razˇsiritev neke razˇsiritve Ta . ˇ je Pa = POINTER TO Ta in Pb = POINTER TO Tb pravimo, da je Pb razˇsiritev Pa Ce (Pa je osnovni tip za Pb ), v primeru, ko je Tb razˇsiritev Ta . Skladnost glede prirejanja Nek izraz e, ki pripada tipu Te , je skladen glede prirejanja s spremenljivko v, ki pripada tipu Tv , ˇce je resniˇcen vsaj en od naslednjih pogojev: 1. Te in Tv sta ista tipa; 2. Te in Tv sta ˇstevilˇcna tipa in Tv vsebuje Te ; 3. Te in Tv sta zapisna tipa, Te je razˇsiritev Tv in dinamiˇcni tip v je Tv ; 4. Tv je kazalˇcni ali procedurni tip, e pa ima vrednost NIL; 5. Tv je enak ARRAY n OF CHAR, e je znakovno zaporedje dolˇzine m in velja m < n; 6. Tv je procedurni tip, e pa je ime procedure, ˇcigar formalni parametri ustrezajo formalnim parametrom Tv . Tabelariˇ cna skladnost Stvarni parameter a tipa Ta je tabelariˇcno skladen s formalnim parametrom f tipa Tf , kadar 1. sta Tf in Ta ista tipa ali 2. Tf je odprta tabela, Ta je poljubna tabela, njuna tipa elementov pa sa tabelariˇcno skladen ali 3. Tf je enak ARRAY OF CHAR, a pa je znakovno zaporedje. Skladnost glede uporabe v izrazih Za podani operator sta tipa njegovih operandov skladen glede uporabe v izrazih, kadar ustrezajo naslednji tabeli (ki dodatno prikazuje tip vrednosti izraza). Znakovne tabele, ki jih primerjamo, morajo imeti zakljuˇcni znak 0X. V vseh primerih mora biti T1 razˇsiritev T0 . 220 JEZIK OBERON-2 operator +−∗ prvi operand ˇstevilˇcni drugi operand ˇstevilˇcni / ˇstevilˇcni ˇstevilˇcni +-*/ DIV MOD SET celoˇstev. SET celoˇstev. OR & ∼ = # <<=>>= BOOLEAN ˇstevilˇcni CHAR znakovna tabela ali zn. zaporedje BOOLEAN SET NIL, POINTER TO T0 ali T1 proced. tip T ali NIL INTEGER kazalec zapis BOOLEAN ˇstevilˇcni CHAR znakovna tabela ali zn. zaporedje BOOLEAN SET NIL, POINTER TO T0 ali T1 proced. tip T ali NIL SET kazalec zapis =# IN IS A.12.1 tip rezultata najmanjˇsi ˇstevilˇcni tip, ki vsebuje oba operanda najmanjˇsi realen tip, ki vsebuje oba operanda SET najmanjˇsi celoˇstev. tip, ki vsebuje oba operanda BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN BOOLEAN Skladnost seznamov formalnih parametrov Dva seznama formalnih parametrov sta skladna v kolikor 1. vsebujeta isto ˇstevilo parametrov in 2. imata isti tip rezultata ali nimata rezultata in 3. so istoleˇzni parametri enakih tipov in 4. so istoleˇzni parametri bodisi oboji referenˇcnega tipa ali oboji vrednostnega tipa. A.13 Sintaksa jezika Oberon-2 Module → “MODULE” ident [ ImportList ] DeclSeq [ “BEGIN” StatementSeq ] “END” ImportList → “IMPORT” [ Ident “ := ” ] { “, ” [ Ident “ := ” ] } “; ” DeclSeq → { “CONST” { ConstDecl “; ” } | “TYPE” { T ypeDecl “; ” } | “VAR” { V arDecl “; ” } }| { P rocDecl “; ” | F orwardDecl “; ” } ConstDecl → IdentDef “=” ConstExpr TypeDecl → IdentDef “=” T ype VarDecl → IdentList “:” T ype ProcDecl → “PROCEDURE” [ Receiver ] IdentDef [ F ormalP ars ] “; ” DeclSeq [ “BEGIN” StatementSeq ] “END” ident A.13. SINTAKSA JEZIKA OBERON-2 ForwardDecl → “∧” “PROCEDURE” [ Receiver ] IdentDef [ F ormalP ars ] FormalPars → “(” [ F P Section { “; ” F P Section} ] “)” [ “:” Qualident ] FPSection → “VAR” ident { “, ” ident } “:” T ype Receiver → “(” [ “VAR” ] ident “:” ident “)” Type → Qualident | “ARRAY” “[” ConstExpr { “, ” ConstExpr } “]” “OF” T ype | “RECORD” [ “(” Qualident “)” ] F ieldList { “; ” F ieldList } “END” | “POINTER” “TO” T ype | “PROCEDURE” [ F ormalP ars ] FieldList → [IdentList “:” T ype] StatementSeq → Statement { “; ” Statement } Statement → [ Designator “ := ” Expression | Designator [ “(” ExprList “)” ] | “IF” Expr “THEN” StatementSeq { “ELSIF” Expr “THEN” StatementSeq } [ “ELSE” StatementSeq ] “END” | “CASE” Expr “OF” Case { “ | ” Case } [ “ELSE” StatementSeq ] “END” | “WHILE” Expr “DO” StatementSeq “END” | “REPEAT” StatementSeq “UNTIL” Expr | “FOR” ident “ := ” Expr “TO” Expr [ “BY” ConstExpr ] “DO” StatementSeq “END” | “LOOP” StatementSeq “END” | “WITH” Guard “DO” StatementSeq { “ | ” “DO” StatementSeq } [ “ELSE” StatementSeq ] “END” | “EXIT” | “RETURN” [ Expression ] ] Case → [ CaseLabels { “, ” CaseLabels } “:” StatementSeq ] CaseLabels → ConstExpr [ “..” ConstExpr ] Guard → Qualident “:” Qualident ConstExpr → Expr Expr → SimpleExpr [ Relation SimpleExpr ] SimpleExpr → [ “ + ” | “ − ” ] T erm {AddOp T erm } Term → F actor { M ulOp F actor } Factor → Designator [ “(” [ ExprList ] “)” ] | number | character | string | “NIL” | set | “(” Expr “)” | “∼” F actor Set → “{” [ Element { “, ” Element } ] “}” Element → Expr [ “..” Expr ] Relation → “ = ” | “#” | “ < ” | “ <= ” | “ > ” | “ >= ” | “IN” | “IS” AddOp → “ + ” | “ − ” | “OR” MulOp → “ ∗ ” | “/” | “DIV” | “MOD” | “&” Designator → Qualident { “.” ident | “[” ExprList “]” | “∧” | “(” Qualident “)” } ExprList → Expr { “, ” Expr } 221 222 JEZIK OBERON-2 IdentList → IdentDef { “, ” IdentDef } Qualident → [ ident “.” ] ident IdentDef → ident [ “ ∗ ” | “ − ” ] A.14 Modul SYSTEM Modul z imenom SYSTEM vsebuje doloˇcene tipe in procedure, ki so potrebni za realizacijo niskoravenskih operacij, ki so lastne posameznemu raˇcunalniku ali prevajalniku. S takimi elementi na primer omogoˇcamo naslavljanje razliˇcnih naprav, ki so prikljuˇcene na raˇcunalnik, kakor tudi ne upoˇstevanje pravil skladnosti tipov, ki jih predpisuje definicija jezika. Priporoˇcljivo je, da se uporaba teh zmogljivosti omeji le na doloˇcene module (ki jim pravimo niskoravenski moduli). Taki moduli po svoji naravi niso prenosljivi med raˇcunalniˇskimi sistemi in med razliˇcnimi izvedbami prevajalnikov za Oberon-2, so pa enostavno razpoznavni zaradi naziva modula SYSTEM v svojem seznamu uvoˇzenih modulov. Naslednje specifikacije veljajo za realizacijo Oberona-2 na raˇcunalniku Ceres. Modul SYSTEM izvaˇza tip BYTE, ki ima naslednje lasnosti: Spremenljivkam tipa BYTE lahko prirejamo vrednosti tipov CHAR ali SHORTINT. V primeru, ko je formalni parameter tipa ARRAY OF BYTE, je lahko ustrezni stvarni parameter poljubnega tipa. Modul SYSTEM izvaˇza tudi tip PTR. Spremenljivkam tega tipa lahko prirejamo vrednosti poljubnega kazalˇcnega tipa. V primeru, ko je formalni parameter tipa PTR, je lahko ustrezni stvarni parameter poljubnega kazalˇcnega tipa. Procedure modula SYSTEM so naˇstete v naslednih tabelah. Veˇcina je realizirana z enim samim strojnim ukazom, ki je postavljen v ukazno zaporedje, brez dejanskega klica procedure. Podrobnosti lahko bralec izve v priroˇcniku za ustrezni procesor. V tabelah predstavlja v spremenljivko, x, y, a in n predstavljajo izraze in T predstavlja tip. Funkcijske procedure Ime ADR(v) BIT(a, n) CC(n LSH(x, n) ROT(x, n) VAL(T, x) tipi argumentov poljuben a: LONGINT n: celo ˇst. celoˇst. konst. x: celoˇst. tip, CHAR, BYTE n: celoˇst. tip x: celoˇst. tip, CHAR, BYTE n: celoˇst. tip T, x: polubna tipa tip rezultata LONGINT BOOLEAN opis naslov v n-ti bit Mem[a] BOOLEAN isti tip kot x pogoj n (0 ≤ n ≤ 15) logiˇcni pomik isti tip kot x rotacija T vrednost x interpretirana kot, da pripada T 223 A.14. MODUL SYSTEM Prave procedure Ime GET(a, v) PUT(a, x) GETREG(n, v) PUTREG(n, x) MOVE(a0 , a1 , n) NEW(v, n) tipi argumentov a: LONGINT ; v: poljubnega osnovnega, procedurnega ali kazal. tipa a: LONGINT ; v: poljubnega osnovnega, procedurnega ali kazal. tipa n: celoˇst. tip ; v: poljubnega osnovnega, procedurnega ali kazal. tipa n: celoˇst. tip ; v: poljubnega osnovnega, procedurnega ali kazal. tipa a0 , a1 : LONGINT n: INTEGER v: poljuben kazal. tip n: celoˇstev. tip opis v := Mem[a] Mem[a] := v v := Register n Register n := v Mem[a1 . . . a1 + n − 1] := Mem[a0 . . . a0 + n − 1] dodeli se blok celic dolˇzine n bajtov, naslov se priredi v 224 JEZIK OBERON-2 Dodatek B ˇ PREDIKATNI RACUN PRVEGA REDA V priˇcujoˇcem delu privzemamo, da je bralec seznanjem s predikatnim raˇcunom prvega reda, namen tega kratkega poglavja pa je le ponovitev osnovnih pojmov. Snov je v glavnem sestavljena na podlagi dela Mendelson [12], je pa dostopna tudi iz ˇstevilnih drugih virov (gl. tudi Batagelj [2]). Logiˇcnim trditvam v predikatnem raˇcunu prvega reda pravimo stavki. Vsak stavek predikatnega raˇcuna prvega reda je zapis, ki je sestavljen po natanˇcnih pravilih. Pravimo, da ima predikatni raˇcun prvega reda natanˇcno doloˇceno sintakso. Simboli, ki jih uporabljamo za sestavljanje stavkov, pa so: 1. ˇstevno neskonˇcno ˇstevilo znakov za spremenljivke (x1 , x2 , x3 . . .); seveda je vsaka posamezna trditev konˇcne dolˇzine in lahko vsebuje le konˇcno ˇstevilo spremenljivk. V praktiˇcnih primerih ponavadi uporabljamo razliˇcne simbole za razliˇcne spremenljivke, npr. x, y, . . .; 2. konˇcno ali ˇstevno neskonˇcno ˇstevilo znakov za predikate (npr. An j , n, j > 0, kjer n predstavlja ˇstevilo argumentov predikata, j pa njegov indeks). V praktiˇcnih primerih najpogosteje opuˇsˇcamo zapis ˇstevila argumentov in prav tako kot v primeru spremenljivk uporabljamo razliˇcne znake za razliˇcne predikate, namesto da bi jih razlikovali z indeksi; 3. ˇstevno neskonˇcno ali konˇcno ˇstevilo (vˇstevˇsi niˇc) znakov za funkcije (npr. fjn , n, j > 0) (n in j imata podoben pomen kot pri predikatih in prav tako velja pripomba, da se izogibamo uporabi indeksov ter raje uporabljamo razliˇcne simbole za razliˇcne funkcije); 4. ˇstevno neskonˇcno ali konˇcno ˇstevilo (vˇstevˇsi ˇstevilo niˇc) znakov za posamezne konstante (npr. ai , i > 0); 5. sintaktiˇcna simbola ( in ), logiˇcna znaka ⊃ in ¬ ter univerzalnostni kvantifikator ∀. Neki stavek predikatnega raˇcuna prvega reda je sestavljen iz izrazov in elementarnih stavkov . 1. Izraz je posamezna konstanta ali spremenljivka, ali 2. izraz je fjn (e1 , e2 , . . . , en ), pri ˇcemer je fjn funkcijski znak, e1 , e2 , . . . , en pa so izrazi; 225 226 ˇ DODATEK B. PREDIKATNI RACUN PRVEGA REDA 3. izraz lahko pridobimo le na podlagi toˇck 1 in 2. ˇ sta a1 in a2 posamezni konstanti, x, y in z spremenljivke in f 2 , g 3 znaka B.1 Primer Ce za funkciji, so a1 , f 2 (a2 , f 2 (a1 , y)), g 3 (a1 , x, z), (B.1) izrazi. Elementarni stavki so zgrajeni iz izrazov in znakov za predikate. Torej ima elementaren stavek obliko An kjer je An znak za predikat, e1 , e2 , j (e1 , e2 , . . . , en ), j . . . , en pa so izrazi. ˇ uporabljamo simbole iz primera B.1 ter simbol A3 za predikat, sta elemenB.2 Primer Ce tarna stavka: A3 (x, y, z), A3 (a1 , f 2 (a2 , f 2 (a1 , y)), g 3 (a1 , x, z)) (B.2) In konˇcno so stavki predikatnega raˇcuna prvega reda1 zgrajeni iz elementarnih stavkov na podlagi naslednjih pravil: 1. vsak elementaren stavek je stavek predikatnega raˇcuna; 2. naj bosta A in B stavka predikatnega raˇcuna, x pa spremenljivka. Tedaj so ¬A, A ⊃ B in ∀xA stavki predikatnega raˇcuna; 3. stavki predikatnega raˇcuna so sestavljeni le na podlagi toˇck 1 in 2. B.3 Primer Stavek predikatnega raˇcuna je ∀y(∀xA21 (x, y) ⊃ A12 (y)). Pri zapisovanju stavkov predikatnega raˇcuna pogosto uporabljamo doloˇcene okrajˇsave in spremembe v zapisu. Na primer, funkcije in predikate pogosto raje zapisujemo v infiksnem zapisu namesto v nekoliko manj preglednem funkcijskem zapisu. Denimo, da A2 predstavlja dobro znani predikat enakosti. Tedaj ustrezni elementarni stavek raje piˇsemo kot t1 = t2 namesto A2 (t1 , t2 ). Podobno piˇsemo x + y namesto f 2 (x, y) v primeru, ko f 2 predstavlja seˇstevanje (in podobno za druge funkcije). Naslednja pomembna okrajˇsava je uporaba eksistenˇcnega kvantifikatorja. Namesto ¬(∀x¬A(x)) piˇsemo ∃xA(x) (torej “Ni resniˇcno, da pri vseh x velja negacija A(x)” je enakovredno “eksistira tak x, da velja A(x)”). Predikatni raˇcun rabi za opisovanje trditev o elementih neke poljubne mnoˇzice, vendar nas bo tu zanimala predvsem mnoˇzica celih ˇstevil . Torej moramo sedaj opisati, kako nekemu stavku predikatnega raˇcuna pripiˇsemo pomen. Najprej opiˇsemo pojem mnoˇzice prostih spremenljivk , ki pripada nekemu izrazu in nato ˇ je X bodisi izraz ali stavek, ki ne vsebuje kvantifikatorja stavku predikatnega raˇcuna. Ce ∀, je njegova mnoˇzica prostih spremenljivk, V (X), definirana povsem obiˇcajno kot mnoˇzica ˇ pa je X stavek in ima obliko ∀xA, je V (∀xA) = spremenljivk, ki se pojavljajo v X. Ce V (A)−{x}. Z drugimi besedami, uˇcinek kvantifikatorja ∀ je, da spremenljivko, ki jo imenuje, izloˇci iz mnoˇzice prostih spremenljivk izraza, v katerem se pojavlja. Pravimo tudi, da ∀x veˇze spremenljivko x (in torej ni veˇc prosta). Pri stavku ∀xA moramo upoˇstevati, da se kvantifikator ∀ vedno nanaˇsa na stavek A in da se znotraj svojega obmoˇcja nanaˇsa na tiste primerke spremenljivke x, ki niso v obmoˇcju nekega drugega kvantifikatorja znotraj A. 1 Prilastek “prvega reda” se nanaˇ sa na dejstvo, da izraz, ki je argument predikata, predstavlja le posamezne elemente mnoˇ zic in ne more predstavljati nekega drugega predikata ali funkcije. Odslej bomo prilastek “prvega reda” veˇ cinoma opustili. 227 L1. L2. L3. L4. L5. A ⊃ (B ⊃ A) (A ⊃ (B ⊃ C)) ⊃ ((A ⊃ B) ⊃ (A ⊃ C)) (¬B ⊃ ¬A) ⊃ ((¬B ⊃ A) ⊃ B) ∀xA(x) ⊃ A(t) pri pogoju, da je spremenljivka x v stavku A(x) zamenljiva s t2 . Ta pogoj je izpolnjen med ostalim tudi pri t = x, s ˇcimer dobimo aksiom ∀xA(x) ⊃ A(t). ∀x(A ⊃ B) ⊃ (A ⊃ ∀xB) v primeru, ko je A stavek brez proste spremenljivke x. Slika B.1: Logiˇcni aksiomi predikatnega raˇcuna B.4 Primer V stavku ∀x∀z(A(x, y) ⊃ ∀x(B(x, z))) se prvi kvantifikator nanaˇsa na argument predikata A, ne pa na argument predikata B. Spremenljivke z istim imenom, na katere delujejo razliˇcni kvantifikatorji, ˇstejemo za razliˇcne spremenljivke. V zgornjem primeru sta spremenljivki x v predikatih A in B razliˇcni. Spremenljivki, na katero deluje neki kvantifikator, pravimo, da je vezana, sicer je prosta. V zgornjem primeru sta x in z vezani spremenljivki, y pa je prosta spremenljivka. Stavke predikatnega raˇcuna lahko interpretiramo kot stavke, ki se nanaˇsanjo na cela ˇstevila, tako da vsem konstantam, funkcijskim simbolom in predikatnim simbolom pripiˇsemo ustrezen pomen, nato pa logiˇcna simbola ¬ in ⊃ interpretiramo na obiˇcajen naˇcin. Na primer, ˇce pri stavku ∀x∀y∀z[A(x, y) ⊃ (A(y, z) ⊃ A(x, z))] interpretiramo A kot predikat enakosti =, se zapisani stavek spremeni v obiˇcajno pravilo tranzitivnosti. V primeru, ko stavek vsebuje proste spremenljivke, je stavek resniˇcen ali neresniˇcen, odvisno od vrednosti, ki jih pripiˇsemo prostim spremenljivkam. Na primer: stavek ∀x[x · y = 0] je resniˇcen pri y = 0 in neresniˇcen sicer. B.5 Primer Omenili smo ˇze, da predikatni raˇcun uporabljamo za izraˇzanje trditev ali lastnosti, ki se nanaˇsajo na cela ˇstevila (ali poljubno drugo mnoˇzico). Kot primer vzemimo: “r je ostanek po deljenju x z y.” To lastnost izrazimo z naslednjim stavkom predikatnega raˇcuna: ∃q[x = y · q + r ∧ 0 ≤ r < y], pri ˇcemer simbol ∧ predstavlja logiˇcno operacijo konjunkcije, oziroma, ˇce jo izrazimo z osnovnima simboloma ¬ in ⊃, ¬(A ⊃ ¬B). Osnovna lastnost predikatnega raˇcuna je, da omogoˇca dokazovanje izpeljanih resnic iz preprostejˇsih, osnovnih resnic, ki jim pravimo aksiomi. Aksiome delimo na logiˇcne, ki se nanaˇsajo na predikatni raˇcun, ne glede na podroˇcje uporabe, in posebne, ki upoˇstevajo neko posebno podroˇcje uporabe. Logiˇcni aksiomi predikatnega raˇcuna so prikazani na sliki B.1, medtem ko sta posebna aksioma, ki se nanaˇsata na predikat enakosti, prikazana na sl. B.2. 2 Spremenljivka x v stavku A je zamenljiva s t, v primeru, ko noben prost primerek x v A ni v obmoˇ cju nekega kvantifikatorja s spremenljivko y, ki se pojavlja v t. ˇ DODATEK B. PREDIKATNI RACUN PRVEGA REDA 228 E1. E2. ∀x[x = x] (refleksivnost enakosti) x = y ⊃ [A(x, x) ⊃ A(x, y)] (zamenljivost nekaterih, ne pa nujno vseh, prostih primerkov enakih spremenljivk) Slika B.2: Posebna aksioma za predikat enakosti S1. S2. iz A in A ⊃ B sledi B (pravilo modus ponens) iz A sledi ∀xA (pravilo posploˇsitve) Slika B.3: Pravila sklepanja za predikatni raˇcun 1. 2. 3. 4. 5. (A ⊃ ((A ⊃ A) ⊃ A)) ⊃ ((A ⊃ (A ⊃ A)) ⊃ (A ⊃ A)) (primerek aksioma L2) A ⊃ ((A ⊃ A) ⊃ A) (primerek L1) (A ⊃ (A ⊃ A)) ⊃ (A ⊃ A)(iz 1 in 2 na podlagi S1) A ⊃ (A ⊃ A) (primerek L1) A⊃A (iz 3 in 4 na podlagi S1) Slika B.4: Dokaz stavka A ⊃ A ˇ imamo neko zaporedje stavkov S, katerega zadnji Naj bo Γ neka mnoˇzica stavkov. Ce element je A, in ki ima lastnost, da vsak element zaporedja bodisi pripada Γ ali pa sledi iz predhodnih elementov zaporedja na podlagi pravil sklepanja (slika B.3), pravimo, da smo A izpeljali iz Γ. S simboli to zapiˇsemo kot Γ |−A. Za stavek A pravimo, da smo ga dokazali, ko velja Λ |−A, pri ˇcemer je Λ mnoˇzica aksiomov, ki vsebuje tako logiˇcne aksiome (slika B.1) kot morebitne posebne aksiome. Obiˇcajno simbola Λ ne piˇsemo in ima predhodni zapis obliko kar |−A. Na sliki B.4 je prikazan primer dokaza oˇcitnega stavka A ⊃ A, z drugimi besedami utemeljitev zapisa |−A ⊃ A. Naj na tem mestu povzamemo osnovne ideje, ki se nanaˇsajo na predikatni raˇcun in dokazovanje: 1. trditve zapisujemo z nizi simbolov, ki so zgrajeni po natanˇcno doloˇceni sintaksi; 2. izhajamo iz nekega konˇcnega nabora trditev, za katere privzemamo, da so resniˇcne (aksiomi); 3. pri neki doloˇceni izibiri aksiomov lahko trditve interpretiramo kot trditve, ki se nanaˇsajo na ustrezne strukture, kot je npr. kolobar celih ˇstevil; 4. obstaja mehaniˇcen postopek, ki preverja, ali neka trditev sledi iz predhodnih3 ; 5. ˇce zadnja trditev posredno sledi iz aksiomov, pravimo, da smo jo “dokazali”. Spuˇsˇcanje v podrobnosti predikatnega raˇcuna (prvega reda) daleˇc presega okvir te knjige. Naˇs namen pri predhodnem izvajanju je bil le obuditi spomin na doloˇceno terminologijo in 3 Neki postopek je mehaniˇ cen, ko si lahko predstavljamo, da ga je sposoben izvajati raˇ cunalnik. ˇ B.1. DODATNI PRIMERI UPORABE PREDIKATNEGA RACUNA 229 priklicati bralcu v spomin pojem dokaza ter idejo, kako s predikatnim raˇcunom izraˇzamo matematiˇcne resnice. Za bralca, ki ga to zanima, pa je v dodatku B.1 prikazanih nekaj dodatnih primerov izpeljav in dokazov. B.1 Dodatni primeri uporabe predikatnega raˇ cuna V poglavju 1.2 smo opisali osnovne pojme predikatnega raˇcuna in dokazovanja stavkov. Vsak dokaz je zaporedje stavkov, pri ˇcemer je vsak element zaporedja bodisi aksiom ali pa sledi iz predhodnih stavkov na podlagi pravil sklepanja B.3. V praksi pa je potrebno osnovna pravila sklepanja dopolniti z novimi, izpeljanimi pravili zato, da se izognemo pretirano dolgim in zapletenim izpeljavam. Kot elementaren primer izpeljanega pravila navajamo naslednji izrek B.6 Izrek Naj velja |−A1 ⊃ (A2 ⊃ (. . . ⊃ An ) . . .)) in |−A1 , . . . |−An−1 . V tem primeru velja tudi |−An . Dokaz. (Vaja). V matematiˇcnem razmiˇsljanju pogosto postopamo tako, da privzamemo resniˇcnost nekega stavka A, nato izpeljemo iz stavka A drug stavek B in konˇcno povzamemo, da “iz A sledi B”, oziroma “ˇce velja A, velja tudi B”. Ta naˇcin razmiˇsljanja povzema naslednji izrek. B.7 Izrek (Izrek o implikaciji) Naj bo Γ mnoˇzica stavkov, A, B sta stavka in naj velja Γ, A |−B. V tem primeru velja tudi Γ |−A ⊃ B. Dokaz. Naj bo B1 , B2 , . . . , Bn izpeljava B iz Γ ∪ {A}, pri ˇcemer je Bn = B. Z indukcijo po i bomo dokazali, da velja Γ |−A ⊃ Bi pri 1 ≤ i ≤ n. Najprej primer i = 1. B1 pripada mnoˇzici Γ, ali je logiˇcni aksiom ali pa je A. B1 ⊃ (A ⊃ B1 ) je primerek L1 in Γ |−A ⊃ B1 velja v prvih dveh primerih na podlagi pravila modus ponens. V tretjem primeru (B1 = A) velja |−A ⊃ B1 na podlagi A ⊃ A (gl. sl. B.4 ) in torej velja tudi Γ |−A ⊃ B1 . S tem smo opravili s primerom i = 1. Denimo sedaj, da velja Γ |−A ⊃ Bk , pri k < i. Bi je bodisi aksiom, ali pripada mnoˇzici Γ ali je enako A ali pa Bi sledi na podlagi modus ponens iz nekih stavkov Bj in Bm , pri j, m < i in ko ima Bm obliko Bj ⊃ Bi . V prvih treh primerih razmiˇsljamo kot v primeru i = 1 zgoraj. V zadnjem primeru velja na podlagi induktivne hipoteze Γ |−A ⊃ Bj ter Γ |−A ⊃ (Bj ⊃ Bi ). Vendar imamo na podlagi aksioma L2 (sl. B.1) |−(A ⊃ (Bj ⊃ Bi )) ⊃ ((A ⊃ Bj ) ⊃ (A ⊃ Bi )). Torej imamo, na podlagi modus ponens Γ |−(A ⊃ Bj ) ⊃ (A ⊃ Bi ) in (zopet na podlagi modus ˇ ponens, Γ |−A ⊃ Bi ). S tem je induktivni dokaz konˇcan. Zeleni rezultat ustreza primeru i = n. 2 Na tem mestu bomo za vajo prikazali izpeljavo treh preprostih posledic aksiomov E1 in E2 (sl. B.2). B.8 Trditev V vsaki teoriji prve stopnje z enakostjo veljajo 1. ˇce je t poljuben izraz, velja |−t = t (refleksivnost); 2. |−x = y ⊃ y = x (simetrija); 3. |−x = y ⊃ (y = z ⊃ x = z) (tranzitivnost). 230 ˇ DODATEK B. PREDIKATNI RACUN PRVEGA REDA Dokaz. (1) Na podlagi E1 velja |−∀x1 [x1 = x1 ], nato pa na podlagi L4 (sl. B.1), |−t = t. (2) Naj bo stavek A(x, x) enak x = x, A(x, y) pa x = y. V tem primeru velja na podlagi E2 |−x = y ⊃ (x = x =⊃ y = x). Ker pa iz 1 sledi x = x, je 2 posledica tavtologije B ⊃ ((A ⊃ (B ⊃ C)) ⊃ (A ⊃ C)). (3) Naj bo stavek A(y, y) enak y = z, A(y, x) pa x = z. Iz E2 (pri zamenjanima x in y) izpeljemo |−y = x ⊃ (y = z ⊃ x = z). Vendar na podlagi (2) |−x = y ⊃ y = x. Sedaj pa s pomoˇcjo tavtologije (A ⊃ B) ⊃ ((B ⊃ C) ⊃ (A ⊃ C)) pridobimo |−x = y ⊃ (y = z ⊃ x = z). 2 Literatura [1] Alfred V. Aho, John E. Hopcroft, and Jeffrey D. Ullman. The Design and Analysis of Computer Algorithms. Computer Science and Information Processing. Addison-Wesley, 1974. ISBN 0-07-039910-7. [2] Vladimir Batagelj. Diskretne Strukture, zapiski predavanj, 1. zvezek. V. Batagelj, samozaloz;ba, Ljubljana, 1995. [3] I. N. Bronstein, K. A. Semendjajew, G. Musiol, and H. M¨ uhlig. Matematiˇcni priroˇcnik (prevod iz nemˇsˇcine). Tehniˇska zaloˇzba Slovenije, 1997. ISBN 86-365-0216-0. [4] Viljan Mahniˇc. Programiranje v Oberonu. BI–TIM d.o.o., Ljubljana, 1996. ISBN 9616046-04-7. [5] Thomas H. Cormen, Charles E. Leiserson, and Ronald L. Rivest. Introduction to Algorithms. The MIT Press and McGraw-Hill Book Company, 1992. Eighth Printing, ISBN 0-262-03141-8. [6] LLC Excelsior. Native xds-x86. Version 2.45, http://www.excelsior-usa.com/xdsx86.html [7] John G. Kemeny, J. Laurie Snell, and Gerald L. Thompson. Introduction to Finite Mathematics. Prentice-Hall, Inc., 1974. Third Edition. [8] Donald E. Knuth. The Art of Computer Programming, Vol. 3. Computer Science and Information Processing. Addison-Wesley, 1973. [9] Igor Kononenko. Naˇcrtovanje podatkovnih struktur in algoritmov. Zaloˇzba FER in FRI, Ljubljana, 1996. ISBN 961-6209-02-7. [10] Jernej Kozak. Podatkovne strukture in algoritmi. Druˇstvo matematikov, fizikov in astronomov SRS, Ljubljana, 1986. [11] Zohar Manna. Mathematical Theory of Computation. McGraw-Hill Computer Science Series. McGraw-Hill, 1974. [12] Elliott Mendelson. Introduction to Mathematical Logic. D. Van Nostrand Company, Inc., Princeton, New Jersey, U.S.A., 1964. [13] H. M¨ osenb¨ ock and N. Wirth. The programming language oberon-2. Technical report, Institut f¨ ur Computersysteme, ETH Z¨ urich, October 1993. [14] Martin Reiser and Niklaus Wirth. Programming in Oberon — Steps Beyond Pascal and Modula. ACM Press, Addison-Wesley Publishing Company, 1992. [15] Ivan Vidav. Viˇsja matematika II. Drˇzavna zaloˇzba Slovenije, Ljubljana, 1979. 231 232 LITERATURA [16] Niklaus Wirth. Systematic Programming: An Introduction. Prentice-Hall, 1973. [17] Niklaus Wirth. Raˇcunalniˇsko programiranje, 1. del. DMFA SRS, Ljubljana, 1979. [18] Niklaus Wirth. Programming in Modula-2. Springer-Verlag, third, corrected edition, 1985. [19] Niklaus Wirth. Raˇcunalniˇsko programiranje, 2. del. DMFA SRS, Ljubljana, 1985. [20] Niklaus Wirth. Algorithms & Data Structures. Prentice/Hall International, 1986. Stvarno kazalo Ω, 20 ⇒, 7 Σ∗ , 177 Θ, 20 ⊃, 7 λ, 184 dxe, 43, 150 bxc, 43 ¬, 7 ω, 84 π, 179 ≺, 27, 29, 142 <, 177 =, 177 ε, 177 ∨, 7 ∧, 7 a ∗ b, 88 a ⊗ b, 88 a2b, 90 a3b, 90 a ◦ b, 87 O, 19 Dijkstrov, 145, 147 dvojiˇskega iskanja, 2, 14 rekurzivni, 23 dvojiˇskega vstavljanja, 35 Floyd-Warshallov, 150 iskanja k-tega elementa, 54 izmeniˇcnih zamenjav, 38 KMP, 178, 181 Knutha, Morrisa in Pratta (gl. tudi algoritem, KMP), 178, 181 leksikografskega urejanja, 30 navadnega izbiranja, 36 navadnega vstavljanja, 33 navadnega zlivanja, 57 navadnih zamenjav, 37 pogrezanja, 44 porazdeljevanja, 47, 57 poˇzreˇsni, 101, 102 Shellov, 42 simpleksni, 125, 128 urejanja izboljˇsani, 30 navadni, 30 tabel, 38 urejanja s kopico, 44 urejanja s porazdelitvami, 47 vstavljanja s ˇcuvajem, 34 vzporedni, 187 z razvejitvijo in omejitvijo, 172 z rekurzivnim razcepom, 81 za 0-1 hahrbtnik, 135 za deljenje, 9, 10 za DFT iterativni, 92, 98 rekurzivni, 91 za iskanje po znakovnih zaporedjih navadni, 177, 178 za maksimalni pretok, 115, 116 ABS, 214 aksiom, 7 za pogojni stavek, 7, 8 za prirejanje, 8 za stekaliˇsˇce, 7 AL, 56 algebra linearna, 81 sploˇsna, 81 Algol, 2 algoritem, 1 Bellman-Fordov, 147 posploˇseni, 149 Boyer in Mooreov, 181, 182 233 234 za mnoˇzenje, 14 za mnoˇzenje dveh ˇstevil, 8, 9 za mnoˇzenje matrik po Strassenu, 83 po Winogradu, 82 za najveˇcjo skupno mero, 17, 18 za razvrˇsˇcanje poslov v delavnici z enim strojem, 108 za topoloˇsko urejanje, 145 antecedens, 7, 12 APItem, 32, 56 ArrSort, 32 ASH, 214 asimptotiˇcna rast, 19 avtomat konˇcni, 1 Bellman, 141 beseda pomnilniˇska, 2 BinaryIns, 35 BinSearch, 3 BubbleSort, 37 CAP, 214 cena, 101 CHR, 214 cikel, 141 negativni, 141 CompareStr, 30 COPY, 215 CopyKseq, 58, 59 CopyRun, 56, 73, 76, 77 CopyRunCoroutine, 73, 76, 77 CrCoroutine, 73, 76 ˇcas, 33 povpreˇcni, 49 ˇceta, 57 navidezna, 67 datoteka, 30 zaporedna, 30 DEC, 215 deljenje, 81 DFT, 84, 86 diagram poteka, 6 dinamiˇcno zaseganje prostora, 19 STVARNO KAZALO diskretna Fourierjeva transformacija (gl. tudi DFT), 84 Distribute, 57, 60, 68 dokaz, 5 dokazovanje pravilnosti, 6, 12 dokumentacija programa, 14 dopustnost, 101 drevo neobetavno, 167 sledi, 3, 40 Empty, 52 enaˇcba Bellmanova, 132 enaˇcbe Bellmanove, 141 ENTIER, 214 Exch, 38 EXCL, 215 FCmpType, 28 FCompareType, 29 FCpyRun, 56 FCrCoroutine, 73 FileSort, 56, 73, 76 FinalSwitch, 73, 76, 77 Find, 54 FNoParam, 56 FOR, 20 FPPSort, 56, 76 FRdItem, 56 FSeqInit, 56 funkcija, 19 ciljna, 118 odsekoma konstantna, nepadajoˇca, 133 OKN, 133 preskakovalna, 179, 180 FWrItem, 56 generiranje spremenljivke, 202 globina vozliˇsˇc dvojiˇskega drevesa, 193 goto, 39 graf, 111, 140 acikliˇcen, 143 usmerjen, 140 z uteˇzenimi povezavami, 140 235 STVARNO KAZALO HALT, 215 halt, 39 HeapPS, 73, 76 HeapSort, 44 hiperravnina, 119 mejna, 119 if a[i]Ra[j] then S, 39 INC, 215 INCL, 215 Init, 52, 74 InitInnerLoop, 69 InitLoop, 57, 63 InitProg, 60, 62, 68 InitRd, 56, 73 InitWr, 56, 73 InsertionSort, 33 IntSort, 56, 73 invarianta, 32 zanˇcna, 11, 14 inverz, 85 iskanje k-tega elementa, 53 po zaporedjih znakovnih, 177 Item, 28, 29 izbiranje navadno, 36 izhod, 1 izraˇcun, 40 izraz, 223 izrek o celoˇstevilˇcnosti maksimalnega pretoka, 115 o lokalnem pogoju za globalni maksimum ciljne funkcije na KPM, 124 o maksimumu ciljne funkcije na KPM, 122 o max pretoku pri min prerezu, 115 o zasiˇcenih poteh, 114 o zgornji meji za vrednost pretoka, 113 izvor, 111 jedro, 32 jezik formalne logike, 5 programski, 12 kapaciteta, 111 prereza, 113 karakteristika, 85 kljuˇc, 29 kolobar, 86 komutativnost, 82, 83 konsekvens, 7, 12 konstanta procedurna, 51 konveksnost, 120 konvolucija, 87 negativna ovita, 90 pozitivna ovita, 90 kopica, 45 KPM, 120 neomejena, 120 omejena, 120 kvantifikator eksistenˇcni, 224 univerzalnostni, 223 LastRun, 75, 77 LEN, 214 ListRank, 190 LONG, 214 MakeCoroutine, 76 matrika, 118 MAX, 214 meja spodnja, 37 Mendelson, 223 Merge, 58 MergeNonEmptyRuns, 69 MergeRun, 58, 60, 63 MergeSort, 57 metoda prestavljanja kazalcev, 189 MIN, 215 mnoˇzenje, 81 matrik spremenjeno, 150 mnoˇzenje matrik, 81 po Strassenu, 83 po Winogradu, 82 mnoˇzica konveksna poliedrska (gl. tudi KMP), 119 236 modul, 28, 55 ArrSort, 32 FileSort, 76 HeapPS, 76 NRQuickSort, 51 Skakac, 160 Sort, 28, 29 Modula-2, 2 n-ti primitivni koren enote, 84 najcenejˇsa pot med vsemi pari vozliˇsˇc, 141, 149 od zaˇcetnega vozliˇsˇca, 141 NatBal2MergeSort, 60 NatBalMultiMergeSort, 62 negacija, 224 nesingularnost, 121 NEW, 215 NextToLastRun, 75, 77 NonEmptyInput, 74, 77 NRQuickSort, 51, 52 Oberon, 2 obseg, 84, 118 ocenjevanje vrst, 21 ODD, 215 odˇstevanje, 81 omreˇzje, 111 operacija osnovna, 39 ORD, 215 PAL, 56 PAPItem, 56 PARALLEL DO, 188 PARALLEL WHILE, 188 parameter, 32 ParListRank, 190 Partition, 47, 49, 54 Pascal, 2 permutacija, 40 PItem, 28, 32, 44, 60 podatki, 1 pogoj izstopni, 15 pogrezanje, 45 polieder, 119 polinom, 86 STVARNO KAZALO polprostor odprt, 119 zaprt, 119 poltrak, 120 PolyphaseSort, 68 pomnilnik, 2 notranji, 30 zunanji, 30 ponor, 111 Pop, 52 poraba ˇcasa, 2, 17 najmanjˇsa, 33 najveˇcja, 33 povpreˇcna, 33 prostora, 2, 17 porazdeljevanje, 47 poskus, 158 pot, 112 neusmerjena, 112 zasiˇcena, 112 pot (v grafu), 141 povezava, 111 negativna, 112 pozitivna, 112 zasiˇcena, 112 PRAM, 187 pravilnost, 16 pravilo dobrih koncev podzaporedij, 183 hevristiˇcno, 181, 183 sklepanja, 11 “slabih znakov”, 183 predikatni raˇcun prvega reda, 5, 223 predstavitev polinoma koeficientna, 86 vrednostna, 86 prerez, 113 prestavljanje kazalcev, 189 pretok, 111 prevedba, 151 0-1 nahrbtnika na najcenejˇse poti, 151 preverjanje pravilnosti, 3, 4 s poskusi, 4 z logiˇcno analizo, 4, 5 prirejanje, 12 problem 237 STVARNO KAZALO 0-1 nahrbtnika, 132, 172 maksimalnega pretoka, 111 najcenejˇsih poti, 140 optimizacijski, 101, 111, 131 osmih dam, 161 razdalje do konca seznama, 189 skakaˇcevega obhoda, 158 trdnih zakonov, 164 procedura samostojna, 73 procesor, 1 zaporedni, 17 produkt asociativni zaˇcetnih elementov seznama, 191 skalarni, 118 program raˇcunalniˇski, 1 programiranje dinamiˇcno, 131, 140 linearno, 111, 117 prostor pomnilni, 33, 51 vektorski, 84, 118 PSeq, 56, 60, 62 PStack, 52 Push, 52 QuickSort, 47 raˇcunalnik, 1 veˇcprocesorski, 187 vzporedni, 187 raˇcunanje vzporedno, 187 RAM, 17 razcep na podprobleme, 132 rekurzivni, 131 razvrˇsˇcanje poslov v delavnici z enim strojem, 105 zapisov na magnetnem traku, 102 Rd, 56, 73 recept za izraˇcun, 1 rekurzija, 51 relacija rekurenˇcna, 22 REPEAT, 11–13 rezultat, 1 Select, 68, 70, 71 SelectionSort, 36 Seq, 56 sestopanje, 157 seˇstevanje, 81 ShakerSort, 38 Shellovo urejanje, 41 ShellSort, 42 SHORT, 215 Sift, 44, 73 SimpleCopyRun, 59 sintaksa, 223 sistem veˇcprocesorski krepko povezan, 187 ˇsibko povezan, 187 SIZE, 215 Skakac, 160 sklad, 19, 51 sled, 3 Sort, 28, 29, 47, 52, 76 SortStruc, 52 SpecInit, 56, 76 spremenljivka prosta, 8, 224 vezana, 224 Stack, 52 StackRec, 52 stavek, 223 elementarni, 223 pogojni, 13 sestavljeni, 13 stopnica, 133 Strassen V., 83 stroj abstrakten, 1 Turingov, 1 z enakopravnim dosegom do pomnilnika, 1 SwitchCoroutine, 73, 75–77 SwitchTapes, 64 SYSTEM ADR, 221 BIT, 221 CC, 221 238 GET, 222 GETREG, 222 LSH, 221 MOVE, 222 NEW, 222 PUT, 222 PUTREG, 222 ROT, 221 VAL, 221 ˇsahovnica, 158 ˇstevilo celo, 224 Fibonaccijevo, 66 kompleksno, 85 racionalno, 85 tabela, 30 teoretiˇcni pripomoˇcek, 39 TermLoop, 69 tip tabelariˇcni, 32 toˇcka ekstremna, 120 trak (gl. tudi datoteka, zaporedna), 30 transformacija linearna, 85 trditev, 5 izhodna, 14 logiˇcna, 223 urejanje, 27 drevesno, 43 leksikografsko, 30 notranje, 30, 32 polifazno, 64 s predurejanjem, 71, 76 s kopico, 43 s porazdelitvami, 47, 131 zunanje, 30, 55 urejenost topoloˇska, 145 ustavljivost, 16 vbvz, 188 vbzz, 188 vhod, 1 vhodne in izhodne enote, 2 vir STVARNO KAZALO raˇcunski, 3 vnaprej deklarirane procedure, 214 vozliˇsˇce, 111 vrednost, 101 vrsta aritmetiˇcna, 20 geometriˇcna, 21 vstavljanje dvojiˇsko, 35 navadno, 32, 33 s ˇcuvajem, 34 WHILE, 11, 13 Winograd S., 82 Wirth, 2 Wr, 56, 73 zahtevnost ˇcasovna, 49 zakon o ohranjanju pretoka, 112 zamenjave izmeniˇcne, 38 navadne, 36, 37, 40 zanka, 20 zaokroˇzanje ˇstevil, 2 zaporedje znakovno, 177 zaporedno izvajanje operacij, 2 zbvz, 188 zbzz, 188 zlivanje naravno, 57 navadno, 55 uravnoteˇzeno, 57 dvosmerno, 59 veˇcsmerno, 61 veˇcsmerno, 57
© Copyright 2024