Z´ aklady algoritmizace Miroslav Virius
2
Obsah 1 Algoritmus 1.1 Vymezen´ı pojmu . . . . . . . . . . . . . . . . . . 1.1.1 Co je to algoritmus . . . . . . . . . . . . . 1.1.2 Pˇr´ıklad 1.1: procesor . . . . . . . . . . . . 1.1.3 Metody shora dol˚ u a zdola nahoru . . . . 1.1.4 Z´ akladn´ı sloˇzky algoritmu . . . . . . . . . 1.1.5 Algoritmus a data . . . . . . . . . . . . . ˇ 1.1.6 Casov´ a a pamˇet’ov´ a n´ aroˇcnost algoritm˚ u. 1.2 Popis algoritm˚ u. . . . . . . . . . . . . . . . . . . 1.2.1 Jazyk pro popis program˚ u. . . . . . . . . 1.2.2 Struktogramy . . . . . . . . . . . . . . . . 1.2.3 Jacksonovy diagramy . . . . . . . . . . . 1.2.4 V´ yvojov´e diagramy . . . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
9 9 9 9 10 11 12 12 12 12 13 13 13
2 Datov´ e struktury 2.1 Z´ akladn´ı datov´e struktury . . . . . . . . . . 2.1.1 Promˇenn´ a . . . . . . . . . . . . . . . 2.1.2 Pole . . . . . . . . . . . . . . . . . . 2.1.3 Z´ aznam (struktura) . . . . . . . . . 2.1.4 Objekt . . . . . . . . . . . . . . . . . 2.2 Odvozen´e datov´e struktury: seznam a strom 2.2.1 Seznam . . . . . . . . . . . . . . . . 2.2.2 Strom . . . . . . . . . . . . . . . . . 2.3 Dalˇs´ı odvozen´e datov´e struktury . . . . . . 2.3.1 B-strom . . . . . . . . . . . . . . . . 2.3.2 Z´ asobn´ık . . . . . . . . . . . . . . . 2.3.3 Fronta . . . . . . . . . . . . . . . . . 2.3.4 Tabulka . . . . . . . . . . . . . . . . 2.3.5 Grafy . . . . . . . . . . . . . . . . . 2.3.6 Mnoˇziny . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
15 15 15 16 18 18 19 19 25 34 34 36 38 39 43 44
3 Metody n´ avrhu algoritm˚ u 3.1 Rozdˇel a panuj . . . . . . . . . . . . . . . . . . . 3.2 Hladov´ y algoritmus . . . . . . . . . . . . . . . . . 3.3 Dynamick´e programov´ an´ı . . . . . . . . . . . . . 3.4 Metoda hled´ an´ı s n´ avratem (backtracking) . . . . ´ 3.4.1 Uvod . . . . . . . . . . . . . . . . . . . . 3.4.2 Podrobnˇejˇs´ı formulace pro zvl´ aˇstn´ı pˇr´ıpad 3.5 Obecn´e metody prohled´ av´ an´ı stavov´eho stromu .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
45 45 46 49 52 52 54 55
4 Rekurze 4.1 Rekurzivn´ı algoritmy a podprogramy 4.1.1 Rekurze v programu . . . . . 4.1.2 Kdy se rekurzi vyhnout . . . 4.2 Jak odstranit rekurzi . . . . . . . . . 4.3 Dalˇs´ı pˇr´ıklady . . . . . . . . . . . . . 4.3.1 Syntaktick´ a anal´ yza . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
57 57 58 58 60 61 62
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . . . . . . . . . . .
. . . . . . 3
. . . . . . . . . . . . . . .
. . . . . .
. . . . . .
4
OBSAH 4.3.2
Ackermannova funkce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5 Tˇ r´ıdˇ en´ı 5.1 Vnitˇrn´ı tˇr´ıdˇen´ı . . . . . . . . . . . . . . . . . . 5.1.1 Tˇr´ıdˇen´ı pˇr´ım´ ym vkl´ ad´ an´ım . . . . . . . 5.1.2 Tˇr´ıdˇen´ı bin´ arn´ım vkl´ ad´ an´ım . . . . . . . 5.1.3 Tˇr´ıdˇen´ı pˇr´ım´ ym v´ ybˇerem . . . . . . . . 5.1.4 Bublinkov´e tˇr´ıdˇen´ı a tˇr´ıdˇen´ı pˇretˇra ´s´ an´ım 5.1.5 Shellovo tˇr´ıdˇen´ı (tˇr´ıdˇen´ı se zmenˇsov´ an´ım 5.1.6 Stromov´e tˇr´ıdˇen´ı a tˇr´ıdˇen´ı haldou . . . . 5.1.7 Rychl´e tˇr´ıdˇen´ı (quicksort) . . . . . . . . 5.2 Hled´ an´ı k-t´eho prvku podle velikosti . . . . . . 5.2.1 Hoar˚ uv algoritmus . . . . . . . . . . . . 5.3 Vnˇejˇs´ı tˇr´ıdˇen´ı . . . . . . . . . . . . . . . . . . . 5.3.1 Pˇr´ım´e sluˇcov´ an´ı . . . . . . . . . . . . . . 5.3.2 Tˇr´ıdˇen´ı pˇrirozen´ ym sluˇcov´ an´ım . . . . . 5.4 Nˇekter´e dalˇs´ı metody tˇr´ıdˇen´ı . . . . . . . . . . 5.4.1 Pˇrihr´ adkov´e tˇr´ıdˇen´ı . . . . . . . . . . . . 5.4.2 Lexikografick´e tˇr´ıdˇen´ı . . . . . . . . . . 5.4.3 Topologick´e tˇr´ıdˇen´ı . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . kroku) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
65 65 66 68 69 70 73 74 78 82 83 84 84 86 91 91 93 94
6 Pouˇ zit´ı bin´ arn´ıho stromu 6.1 Vyv´ aˇzen´e stromy . . . . . . . . . . . . . . . . . . 6.1.1 Pˇrid´ av´ an´ı vrchol˚ u do vyv´ aˇzen´eho stromu 6.2 Vyhled´ av´ an´ı v bin´ arn´ım stromu . . . . . . . . . . 6.2.1 Anal´ yza vyhled´ av´ an´ı v bin´ arn´ım stromu . 6.2.2 Bin´ arn´ı vyhled´ avac´ı stromy . . . . . . . . 6.3 Zpracov´ an´ı aritmetick´eho v´ yrazu . . . . . . . . . 6.3.1 Z´ apis v´ yrazu pomoc´ı stromu . . . . . . . 6.3.2 Obr´ acen´ y polsk´ y z´ apis . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
101 101 101 105 105 107 111 111 112
7 Seminumerick´ e algoritmy 7.1 Poziˇcn´ı ˇc´ıseln´e soustavy . . . . . . . . . . . . . 7.2 Cel´ a ˇc´ısla . . . . . . . . . . . . . . . . . . . . . 7.2.1 Reprezentace cel´ ych ˇc´ısel se znam´enkem 7.2.2 Sˇc´ıt´ an´ı cel´ ych ˇc´ısel . . . . . . . . . . . . 7.2.3 Odeˇc´ıt´ an´ı cel´ ych ˇc´ısel . . . . . . . . . . 7.2.4 Opaˇcn´e ˇc´ıslo . . . . . . . . . . . . . . . 7.2.5 N´ asoben´ı cel´ ych ˇc´ısel . . . . . . . . . . . 7.2.6 Dˇelen´ı cel´ ych ˇc´ısel . . . . . . . . . . . . 7.3 Re´ aln´ a ˇc´ısla . . . . . . . . . . . . . . . . . . . . 7.3.1 Zobrazen´ı re´ aln´ ych ˇc´ısel . . . . . . . . . 7.3.2 Sˇc´ıt´ an´ı a odeˇc´ıt´ an´ı re´ aln´ ych ˇc´ısel . . . . 7.3.3 Normalizace re´ aln´eho ˇc´ısla . . . . . . . . 7.3.4 N´ asoben´ı a dˇelen´ı re´ aln´ ych ˇc´ısel . . . . . 7.3.5 Pˇrevod cel´eho ˇc´ısla na re´ aln´e a naopak . 7.4 Pˇresnost aritmetiky re´ aln´ ych ˇc´ısel . . . . . . . . 7.4.1 Z´ akladn´ı u ´vahy . . . . . . . . . . . . . . 7.4.2 M´ıra nepˇresnosti . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
115 115 115 116 117 118 118 119 119 120 120 123 123 124 124 125 125 127
8 Nˇ ekter´ e dalˇ s´ı algoritmy 8.1 Rozklad grafu na komponenty . . . . . 8.2 Tranzitivn´ı uz´ avˇer orientovan´eho grafu 8.3 N´ asoben´ı matic: Strassen˚ uv algoritmus 8.3.1 Rozdˇelen´ı matice na bloky . . . 8.3.2 Strassen˚ uv algoritmus . . . . . 8.4 V´ ypoˇcet hodnoty polynomu . . . . . . 8.5 Diskr´etn´ı Fourierova transformace . . ´ 8.5.1 Uvodn´ ıu ´vahy . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
131 131 132 134 134 135 135 136 136
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
OBSAH
5 8.5.2 8.5.3
Rychl´ a Fourierova transformace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 Sloˇzitost rychl´e Fourierovy transformace . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
9 Softwarov´ y projekt ˇ 9.1 Zivotn´ ı cyklus softwarov´eho produktu 9.2 Definice probl´emu . . . . . . . . . . . 9.3 Pˇredbˇeˇzn´e poˇzadavky . . . . . . . . . 9.3.1 Kontrola seznamu poˇzadavk˚ u . 9.4 N´ avrh architektury . . . . . . . . . . . 9.4.1 Kontrola n´ avrhu architektury . 9.4.2 Programovac´ı jazyk . . . . . . 9.5 Dalˇs´ı kroky . . . . . . . . . . . . . . .
. . . . . . . .
10 N´ avrh architektury zaloˇ zen´ y na anal´ yze 10.1 Diagramy toku dat . . . . . . . . . . . . 10.2 Jacksonova metoda . . . . . . . . . . . . 10.2.1 Jacksonovy diagramy . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
141 141 141 142 142 144 147 148 148
poˇ zadavk˚ u 151 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
11 Objektovˇ e orientovan´ y n´ avrh 11.1 Z´ akladn´ı pojmy objektovˇe orientovan´eho programov´ an´ı . 11.1.1 Tˇr´ıda . . . . . . . . . . . . . . . . . . . . . . . . 11.1.2 Sloˇzky instanc´ı a sloˇzky tˇr´ıd . . . . . . . . . . . . 11.1.3 Pozn´ amka k pouˇz´ıv´ an´ı dˇediˇcnosti . . . . . . . . . 11.2 Objektovˇe orientovan´ y n´ avrh . . . . . . . . . . . . . . . 11.2.1 Pˇr´ıklad: jednoduch´ y grafick´ y editor . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
161 161 162 165 166 167 170
6
OBSAH
Pˇ redmluva ˇ Toto skriptum je urˇceno posluchaˇc˚ um prvn´ıho roˇcn´ıku FJFI CVUT se softwarov´ ym zamˇeˇren´ım. Navazuje na pˇredn´ aˇsku Z´ aklady algoritmizace. Algoritmy jsou nerozluˇcnˇe spjaty s datov´ ymi strukturami. Proto se hned na poˇca ´tku zab´ yv´ ame nejˇcastˇeji pouˇz´ıvan´ ymi datov´ ymi strukturami a z´ akladn´ımi operacemi s nimi. V dalˇs´ıch kapitol´ ach se pak sezn´ am´ıme s nˇekter´ ymi metodami n´ avrhu algoritm˚ u. Samostatn´e kapitoly jsou vˇenov´ any zn´ am´ ym a ˇcasto pouˇz´ıvan´ ym algoritm˚ um, jako jsou algoritmy pro tˇr´ıdˇen´ı pol´ı a soubor˚ u nebo z´ akladn´ı algoritmy pro pr´ aci s bin´ arn´ımi stromy. V kapitole o seminumerick´ ych algoritmech se ˇcten´ aˇr sezn´ am´ı se z´ akladn´ımi algoritmy pro pr´ aci s ˇc´ısly. Pˇr´ıklady k prob´ıran´e l´ atce jsou naps´ any pˇrev´ aˇznˇe v Turbo Pascalu, kter´ y posluchaˇci jiˇz znaj´ı. V nˇekolika pˇr´ıpadech se vˇsak uk´ azalo vhodnˇejˇs´ı pouˇz´ıt programovac´ı jazyk C++, nebot’ ten nab´ız´ı moˇznost elegantnˇejˇs´ıho a pˇrehlednˇejˇs´ıho z´ apisu. (Tak tomu je napˇr. ve v´ ykladu o rychl´e Fourierovˇe transformaci, kde potˇrebujeme komplexn´ı ˇc´ısla. V C++ m˚ uˇzeme pouˇz´ıt datov´ y typ, definovan´ y ve standardn´ı knihovnˇe, a operace m˚ uˇzeme zapisovat pomoc´ı bˇeˇzn´ ych aritmetick´ ych oper´ ator˚ u.) Vzhledem k omezen´emu rozsahu pˇredn´ aˇsky obsahuje toto skriptum pouze nejz´ akladnˇejˇs´ı informace. Anal´ yze algoritm˚ u, tj. stanoven´ı jejich ˇcasov´e a pamˇet’ov´e n´ aroˇcnosti, vˇenujeme pouze okrajovou pozornost. V nˇekter´ ych pˇr´ıpadech pouze uv´ ad´ıme zn´ am´e v´ ysledky a nedokazujeme je, nebot’ podrobn´e odvozov´ an´ı by ˇcasto pˇresahovalo znalosti posluchaˇc˚ u 1. roˇcn´ıku. Ostatnˇe anal´ yza algoritm˚ u je pˇredmˇetem zvl´ aˇstn´ı pˇredn´ aˇsky ve vyˇsˇs´ıch roˇcn´ıc´ıch. Tak´e numerick´e algoritmy jsou pˇredmˇetem zvl´ aˇstn´ı pˇredn´ aˇsky a proto zde chyb´ı. Na z´ avˇer skripta je zaˇrazeno pov´ıd´ an´ı o softwarov´em projektu, o n´ avrhu programu na z´ akladˇe anal´ yzy poˇzadavk˚ u a u ´vod do objektovˇe orientovan´eho programov´ an´ı. Jde o velmi struˇcn´ y u ´vod do t´eto problematiky. Podrobnˇejˇs´ı informace najde ˇcten´ aˇr napˇr. v literatuˇre, uveden´e v odkazech. Na z´ avˇer bych chtˇel podˇekovat vˇsem, kteˇr´ı sv´ ymi radami a pˇripom´ınkami pˇrispˇeli ke zd´ arn´emu dokonˇcen´ı tohoto d´ıla, zejm´ena vˇsak recenzentovi, RNDr. Januˇsi Dr´ ozdovi, kter´ y skriptum velice peˇclivˇe pˇreˇcetl a mˇel k nˇemu ˇradu podnˇetn´ ych pˇripom´ınek. M. Virius
7
8
OBSAH
Kapitola 1
Algoritmus V pˇrev´ aˇzn´e ˇca ´sti tohoto skripta se budeme zab´ yvat algoritmy a metodami pro jejich n´ avrh. Zaˇcneme ale t´ım, ˇze se dohodneme, co to vlastnˇe algoritmus je a jak jej budeme zapisovat.
1.1
Vymezen´ı pojmu
Ve vˇetˇsinˇe publikac´ı, vˇenovan´ ych u ´vodu do algoritmizace nebo programov´ an´ı, se pojem algoritmus nezav´ ad´ı autor prostˇe pˇredpokl´ ad´ a, ˇze ˇcten´ aˇr rozum´ı, o co jde. N´ asleduj´ıc´ı vymezen´ı tohoto pojmu jsme pˇrevzali z [9].
1.1.1
Co je to algoritmus
Algoritmus je z´ akladn´ı matematick´ y pojem. To znamen´ a, ˇze jej nelze definovat - mus´ıme se uch´ ylit k opisu, podobnˇe jako u dalˇs´ıch element´ arn´ıch pojm˚ u, jak´ ymi jsou napˇr. bod nebo mnoˇzina. Algoritmus je v podstatˇe n´ avod, jak prov´est urˇcitou ˇcinnost; v pˇr´ıpadˇe programov´ an´ı p˚ ujde zpravidla o transformaci mnoˇziny vstupn´ıch dat na mnoˇzinu v´ ystupn´ıch dat. Ovˇsem ne kaˇzd´ y n´ avod pˇredstavuje algoritmus. Jako algoritmus budeme oznaˇcovat n´ avod, kter´ y m´ a n´ asleduj´ıc´ı vlastnosti: 1. Je element´ arn´ı. To znamen´ a, ˇze se skl´ ad´ a z koneˇcn´eho poˇctu jednoduch´ ych, snadno realizovateln´ ych ˇcinnost´ı, kter´e budeme oznaˇcovat jako kroky. (D´ ale si pov´ıme, co mysl´ıme tou jednoduchou“ ˇcinnost´ı.) ” 2. Je determinovan´y, tj. po kaˇzd´em kroku lze urˇcit, zda popisovan´ y proces skonˇcil, a pokud neskonˇcil, kter´ ym krokem m´ a algoritmus pokraˇcovat. 3. Je koneˇcn´y. Poˇcet opakov´ an´ı jednotliv´ ych krok˚ u algoritmu je vˇzdy koneˇcn´ y. Algoritmus tedy mus´ı skonˇcit po koneˇcn´em poˇctu krok˚ u. 4. Je rezultativn´ı. Vede ke spr´ avn´emu v´ ysledku. 5. Je hromadn´y. To znamen´ a, ˇze algoritmus m˚ uˇzeme pouˇz´ıt k ˇreˇsen´ı cel´e (velk´e) skupiny podobn´ ych u ´loh. V souvislosti s algoritmy se pro oznaˇcen´ı objektu (m˚ uˇze to b´ yt stroj nebo i ˇclovˇek), kter´ y bude prov´ adˇet popisovanou ˇcinnost, pouˇz´ıv´ a term´ın procesor. Je jasn´e, ˇze pˇri formulaci algoritmu mus´ıme zn´ at procesor. Pˇresnˇeji ˇreˇceno mus´ıme vˇedˇet, jak vypadaj´ı element´ arn´ı kroky, kter´e m˚ uˇze n´ avod obsahovat - a ty zˇrejmˇe z´ avis´ı na povaze procesoru. Pozn´ amka: Pojem algoritmus se d´ a formalizovat napˇr. pomoc´ı matematick´e konstrukce, oznaˇcovan´e jako Turing˚ uv stroj, nebo pomoc´ı teorie parci´ alnˇe rekurzivn´ıch funkc´ı. V´ yklad o nich vˇsak pˇresahuje r´ amec naˇseho skripta.
1.1.2
Pˇ r´ıklad 1.1: procesor
Jako pˇr´ıklad vezmeme ˇreˇsen´ı kvadratick´e rovnice, zadan´e koeficienty a, b a c. Budeme-li za procesor povaˇzovat napˇr. CPU poˇc´ıtaˇce PC, budou element´ arn´ı krok pˇredstavovat jednotliv´e instrukce strojn´ıho k´ odu a algoritmus se bude skl´ adat z pokyn˚ u tvaru pˇresuˇ n obsah promˇenn´e c do registru ST“, vyn´ asob obsah ST(1) a ST“ apod.1 ” ” 1 ST
resp. ST(i) jsou registry matematick´eho koprocesoru ix87.
9
10
KAPITOLA 1. ALGORITMUS
Budeme-li vˇsak uvaˇzovat o poˇc´ıtaˇci, vybaven´em pˇrekladaˇcem jazyka Pascal, budou element´ arn´ı kroky pˇredstavovat jednotliv´e pˇr´ıkazy Pascalu. Algoritmus se pak bude skl´ adat z pˇr´ıkaz˚ u tvaru d := sqr(b) - 4*a*c; Bude-li procesorem ˇclovˇek, obezn´ amen´ y se z´ aklady stˇredoˇskolsk´e matematiky, postaˇc´ı mu instrukce vyˇreˇs ” kvadratickou rovnici s koeficienty a, b a c“.
1.1.3
Metody shora dol˚ u a zdola nahoru
Algoritmus je tedy z´ apisem postupu, pouˇziteln´eho pro ˇreˇsen´ı urˇcit´e tˇr´ıdy probl´em˚ u. Jak dospˇet k formulaci, kter´ y bude splˇ novat v´ yˇse uveden´e podm´ınky? Samozˇrejmˇe nejprve mus´ıme dan´ y probl´em umˇet vyˇreˇsit. V n´ asleduj´ıc´ıch kapitol´ ach se sezn´ am´ıme s nˇekter´ ymi postupy, kter´e hled´ an´ı ˇreˇsen´ı usnadˇ nuj´ı, a s ˇradou pˇr´ıklad˚ utedy vyˇreˇsen´ ych probl´em˚ u. Jestliˇze jiˇz ˇreˇsen´ı zn´ ame, potˇrebujeme je zapsat jako algoritmus. Pˇritom postupujeme obvykle tak, ˇze postup ˇreˇsen´ı rozkl´ ad´ ame na jednoduˇsˇs´ı operace, aˇz dospˇejeme k element´ arn´ım krok˚ um. Tento postup n´ avrhu algoritmu se obvykle oznaˇcuje jako metoda shora dol˚ u“. ”
Pˇ r´ıklad 1.2: kvadratick´ a rovnice Z˚ ustaˇ nme u kvadratick´e rovnice. Budeme cht´ıt napsat program, kter´ y bude ˇreˇsit rovnice, jejichˇz koeficienty jsou v uloˇzeny souboru A. V´ ysledek se m´ a uloˇzit do souboru B. Nejhrubˇs´ı formulace m˚ uˇze vypadat takto: Dokud nenaraz´ıˇs na konec souboru A, ˇreˇs rovnice, urˇcen´e koeficienty uloˇzen´ymi v souboru A a v´ysledky zapisuj do souboru B. Nyn´ı potˇrebujeme upˇresnit v´ yznam fr´ aze ˇreˇs rovnice, urˇcen´e...“ - mus´ıme ji rozloˇzit na jednoduˇsˇs´ı kroky. Vedle ” toho si ale mus´ıme uvˇedomit, ˇze kaˇzd´ y program obsahuje nezbytn´e (ale v z´ apisech algoritm˚ u ˇcasto opom´ıjen´e) u ´vodn´ı a z´ avˇereˇcn´e operace, jako je otev´ır´ an´ı a zav´ır´ an´ı soubor˚ u, inicializace pomocn´ ych promˇenn´ ych aj.). Ani n´ aˇs program nebude v´ yjimkou, a proto je do algoritmu zahrneme, i kdyˇz jejich pˇresn´ y v´ yznam urˇc´ıme pozdˇeji. Dostaneme n´ asleduj´ıc´ı formulaci: 1. Proved’ u ´vodn´ı operace. 2. Dokud nenaraz´ıˇs na konec souboru A, opakuj kroky 3 - 5, potom pˇrejdi na krok 6. 3. Pˇreˇcti se souboru koeficienty a, b a c. 4. Vyˇreˇs kvadratickou rovnici s koeficienty a, b, c. 5. Zapiˇs v´ysledky do souboru B a vrat’ se na 2. 6. Proved’ z´ avˇereˇcn´e operace. Nyn´ı je tˇreba zpˇresnit body 1, 4 a 6. My se pod´ıv´ ame jen na bod 4, ostatn´ı si m˚ uˇzete zkusit sami. Postup ˇreˇsen´ı kvadratick´e rovnice jistˇe zn´ ate, takˇze budeme struˇcn´ı. 4a.
Vypoˇcti d = b2 − 4ac.
4b. 4c.
Je-li d < 0, pokraˇcuj bodem 4f, jinak pokraˇcuj bodem 4c. √ Poloˇz d = d.
4d.
Rovnice m´ a koˇreny x1,2 =
4e.
Jdi na 5.
−b±d 4ac
.
1.1. VYMEZEN´I POJMU √
11
−d .
4f.
Poloˇz d =
4g.
Rovnice m´ a koˇreny x1,2 =
−b±id 4ac
, kde i je imagin´ arn´ı jednotka.
D´ ale si mus´ıme ujasnit, co budeme dˇelat v pˇr´ıpadˇe chyby (nepodaˇr´ı se otevˇr´ıt soubor, budou v nˇem chybn´ a data atd.), v jak´em form´ atu jsou uloˇzena vstupn´ı data (zda to budou napˇr. cel´ a, re´ aln´ a nebo komplexn´ı ˇc´ısla), v jak´em form´ atu budeme zapisovat v´ ysledky (jak budeme zapisovat komplexn´ı ˇc´ısla atd.). Kromˇe metody shora dol˚ u“ se obˇcas setk´ ame i s metodou, oznaˇcovanou jako n´ avrh zdola nahoru“. Pˇri postupu ” ” zdola si postupnˇe z element´ arn´ıch krok˚ u vytv´ aˇr´ıme prostˇredky, kter´e nakonec umoˇzn´ı zvl´ adnout poˇzadovan´ y probl´em. S trochou nads´ azky lze tvrdit, ˇze pˇri metodˇe zdola nahoru si vytv´ aˇr´ıme nov´ y procesor t´ım, ˇze ho uˇc´ıme nov´e operace (pˇresnˇeji: uˇc´ıme ho ch´ apat skupiny element´ arn´ıch operac´ı jako nov´e element´ arn´ı kroky.) Obvykle se kombinuje postup shora dol˚ u s postupem zdola nahoru. Postup shora dol˚ u, tedy rozklad postupu ˇreˇsen´ı na element´ arn´ı kroky, dopln´ıme ˇca ´steˇcn´ ym krokem“ zdola nahoru t´ım, ˇze napˇr. pouˇzijeme pˇrekladaˇce ” nˇekter´eho vyˇsˇs´ıho programovac´ıho jazyka, knihovny procedur a funkc´ı nebo syst´emu pro vytv´ aˇren´ı program˚ u (CASE).
1.1.4
Z´ akladn´ı sloˇ zky algoritmu
V algoritmech se setk´ av´ ame se tˇremi z´ akladn´ımi konstrukcemi, kter´e oznaˇcujeme jako posloupnost (sekvenci) pˇr´ıkaz˚ u, s cyklus (iteraci) a s podm´ınˇenou operaci (selekci, v´ ybˇer). Posloupnost (sekvence) je tvoˇrena jedn´ım nebo nˇekolika kroky, kter´e se provedou pr´ avˇe jednou v dan´em poˇrad´ı. Pˇritom nemus´ı j´ıt o kroky element´ arn´ı; pˇri dalˇs´ım zjemˇ nov´ an´ı se souˇca ´sti sekvence mohou rozpadnout na souˇca ´sti, kter´e samy budou tvoˇrit posloupnosti, cykly nebo podm´ınky. V Pascalu m˚ uˇze b´ yt posloupnost vyj´ adˇrena sloˇzen´ ym pˇr´ıkazem, v C´eˇcku blokem. Za pˇr´ıklad posloupnosti m˚ uˇzeme povaˇzovat kroky 4c - 4e v pˇr´ıkladu 1.2. Cyklus (iterace) pˇredstavuje ˇca ´st algoritmu, kter´ a se opakuje, dokud je splnˇena podm´ınka opakov´ an´ı. Cyklus se vˇzdy skl´ ad´ a z podm´ınky opakov´ an´ı a z tˇela cyklu, tedy z operac´ı, kter´e se opakuj´ı. Podm´ınka se m˚ uˇze vyhodnocovat pˇred proveden´ım tˇela cyklu (v Pascalu nebo v C´eˇcku pˇr´ıkazy while nebo for), po skonˇcen´ı tˇela cyklu (v Pascalu pˇr´ıkaz repeat, v C´eˇcku pˇr´ıkaz do - while) nebo i uvnitˇr tˇela cyklu (pokud bychom takov´ y pˇr´ıkaz potˇrebovali v tradiˇcn´ım Pascalu, museli bychom si jej vytvoˇrit pomoc´ı podm´ınˇen´eho skoku; v C´eˇcku - a tak´e v Turbo Pascalu 7.0 - lze pouˇz´ıt kombinaci podm´ınky a pˇr´ıkazu break). V pˇr´ıkladu 1.2 tvoˇr´ı kroky 2 - 5 cyklus s podm´ınkou na poˇca ´tku. Podm´ınˇen´ a operace (selekce) pˇredstavuje vˇzdy vˇetven´ı algoritmu. Je tvoˇrena podm´ınkou a jednou, dvˇema nebo v´ıce v´ ybˇerov´ ymi sloˇzkami. Nejprve se vˇzdy vyhodnot´ı podm´ınka a ta urˇc´ı, zda se bude prov´ adˇet nˇekter´ a z v´ ybˇerov´ ych sloˇzek - a pokud ano, kter´ a. Nemus´ı se tedy prov´est ˇza ´dn´ a z v´ ybˇerov´ ych sloˇzek. Pokud se jedna z nich zvol´ı, provede se jednou. Pro vyj´ adˇren´ı selekce slouˇz´ı v Pascalu pˇr´ıkazy if (´ upln´e nebo ne´ upln´e) a case. V C´eˇcku m´ ame k dispozici pˇr´ıkazy if a switch (v kombinaci s pˇr´ıkazem break). V pˇr´ıkladu 1.2 je podm´ınˇen´ y pˇr´ıkaz 4b. Jestliˇze se urˇcit´ a ˇca ´st algoritmu opakuje na nˇekolika m´ıstech (m˚ uˇze pˇritom pouˇz´ıvat r˚ uzn´ a data), staˇc´ı rozloˇzit ji na element´ arn´ı kroky pouze jednou (napˇr. kdyˇz na ni naraz´ıme poprv´e). Na ostatn´ıch m´ıstech se na ni pak odvol´ ame jako na d´ılˇc´ı algoritmus nebo podprogram. V bˇeˇzn´ ych programovac´ıch jazyc´ıch odpov´ıdaj´ı podprogram˚ um procedury a funkce.
12
1.1.5
KAPITOLA 1. ALGORITMUS
Algoritmus a data
Jak jsme si jiˇz ˇrekli, vyjadˇruj´ı poˇc´ıtaˇcov´e algoritmy zpravidla n´ avody, jak transformovat mnoˇzinu vstupn´ıch dat v mnoˇzinu jinou v´ ystupn´ıch dat. Proto je samozˇrejm´e, ˇze struktura vstupn´ıch, v´ ystupn´ıch, ale (ˇcasto pˇredevˇs´ım) vnitˇrn´ıch dat spoluurˇcuje strukturu algoritmu. Je jasn´e, ˇze pˇri zpracov´ an´ı vstupn´ıch pˇr´ıp. v´ ystupn´ıch dat bude posloupnosti datov´ ych poloˇzek r˚ uzn´ ych druh˚ u zpravidla odpov´ıdat posloupnost r˚ uzn´ ych pˇr´ıkaz˚ u. Pokud se opakuj´ı datov´e poloˇzky t´ehoˇz druhu (iterace), bude jejich zpracov´ an´ı vyˇzadovat nejsp´ıˇse pouˇzit´ı cyklu. Pokud se na jednom urˇcit´em m´ıstˇe v datech (vstupn´ıch nebo v´ ystupn´ıch) mohou vyskytnout u ´daje nˇekolika r˚ uzn´ ych druh˚ u, pouˇzijeme pˇri jejich zpracov´ an´ı selekci (podm´ınˇen´ y pˇr´ıkaz). Ukazuje se tak´e, ˇze pˇrirozen´ ym prostˇredkem pro zpracov´ an´ı rekurzivn´ıch datov´ ych struktur, jako jsou stromy nebo seznamy, jsou obvykle rekurzivn´ı algoritmy.
1.1.6
ˇ Casov´ a a pamˇ et’ov´ a n´ aroˇ cnost algoritm˚ u
Pˇri anal´ yze algoritm˚ u n´ as zaj´ım´ a nejen spr´ avnost (zda dostaneme spr´ avn´ y v´ ysledek) a v pˇr´ıpadˇe numerick´ ych algoritm˚ u pˇresnost, ale tak´e doba, kterou budeme k proveden´ı algoritmu potˇrebovat, a mnoˇzstv´ı operaˇcn´ı pamˇeti, kter´e bude potˇrebovat program, realizuj´ıc´ı algoritmus. Pˇri hodnocen´ı ˇcasov´e n´ aroˇcnosti m˚ uˇzeme vych´ azet z celkov´eho poˇctu element´ arn´ıch krok˚ u, tedy napˇr. instrukc´ı strojov´eho k´ odu, kter´e mus´ıme prov´est, v z´ avislosti na rozsahu vstupn´ıch pˇr´ıp. v´ ystupn´ıch dat. Pˇri hodnocen´ı ˇcasov´e n´ aroˇcnosti mus´ıme vz´ıt v u ´vahu, ˇze doby, potˇrebn´e pro proveden´ı r˚ uzn´ ych instrukc´ı, se mohou drasticky odliˇsovat. Napˇr. na procesoru Intel 80486 trv´ a seˇcten´ı dvou re´ aln´ ych ˇc´ısel v pr˚ umˇeru 10 tik˚ u“, zat´ımco v´ ypoˇcet absolutn´ı hodnoty zabere 3 tiky“. V´ ypoˇcet sinu (tak´e jedna instrukce procesoru Intel ” ” 80486) zabere v pr˚ umˇeru 291 tik˚ u. Pˇritom 1 tik“ pˇri hodinov´e frekvenci procesoru 33 MHz trv´ a cca 3.10 −8 s. ” Na druh´e stranˇe pˇreˇcten´ı jednoho sektoru na pevn´em disku trv´ a v souˇcasn´e dobˇe napˇr. 12 - 15 ms, tedy pˇribliˇznˇe o 3 ˇra ´dy d´ele neˇz proveden´ı tˇech nejn´ aroˇcnˇejˇs´ıch instrukc´ı procesoru. Proto se ve skuteˇcnosti zpravidla staˇc´ı orientovat se podle vybran´ ych skupin instrukc´ı, kter´e jsou ˇcasovˇe nejn´ aroˇcnˇejˇs´ı.
1.2
Popis algoritm˚ u
Algoritmy lze vyjadˇrovat mnoha r˚ uzn´ ymi zp˚ usoby. Nˇekter´e se op´ıraj´ı pouze o slovn´ı vyj´ adˇren´ı, jin´e pouˇz´ıvaj´ı grafick´ ych prostˇredk˚ u. Volba vhodn´eho prostˇredku se m˚ uˇze liˇsit podle charakteru ˇreˇsen´e u ´lohy a podle osobn´ıch zvyklost´ı program´ atora. Uk´ aˇzeme si nˇekter´e ˇcasto pouˇz´ıvan´e.
1.2.1
Jazyk pro popis program˚ u
Tento zp˚ usob popisu algoritm˚ u se dnes pouˇz´ıv´ a patrnˇe nejˇcastˇeji. Jeho oznaˇcen´ı jazyk pro popis program˚ u je doslovn´ ym pˇrekladem anglick´eho term´ınu Program Description Language (PDL). Vych´ az´ı se slovn´ıho popisu algoritmu. Zachycuje postupn´ y rozklad algoritmu na jemnˇejˇs´ı kroky pˇri n´ avrhu metodou shora dol˚ u. Pˇr´ıklad 1.2 ukazuje postupn´e vytv´ aˇren´ı popisu algoritmu pro ˇreˇsen´ı kvadratick´ ych rovnic. Pˇritom je ˇcasto rozumn´e pˇri postupn´em zpˇresˇ nov´ an´ı ponechat i p˚ uvodn´ı r´ amcov´ y popis vˇetˇs´ıho“ kroku, nebot’ usnadˇ nuje ” ˇcten´ aˇri orientaci. Poznamenejme, ˇze slovn´ı popis algoritmu se m˚ uˇze st´ at z´ akladem dobr´eho koment´ aˇre k v´ ysledn´emu programu. Vedle toho ˇcasto k z´ apisu algoritm˚ u pouˇz´ıv´ ame lehce upraven´ y programovac´ı jazyk - napˇr. Pascal. Pˇri takov´emto z´ apisu ˇcasto v z´ ajmu pˇrehlednosti a srozumitelnosti poruˇsujeme syntaktick´ a pravidla: v identifik´ atorech pouˇz´ıv´ ame p´ısmena s diakritick´ ymi znam´enky, vkl´ ad´ ame slovn´ı popis operac´ı apod. S touto formou z´ apisu se zde budeme setk´ avat pomˇernˇe ˇcasto a budeme ji tak´e oznaˇcovat jako Pseudopascal . Poznamenejme, ˇze tradiˇcn´ım jazykem pro popis algoritm˚ u byl programovac´ı jazyk Algol 60. Setk´ ame se s n´ım bˇeˇznˇe v publikac´ıch ze 60. a 70. let, dnes jiˇz sp´ıˇse v´ yjimeˇcnˇe. (Jednou takovou v´ yjimkou je napˇr. [26].)
1.2. POPIS ALGORITM˚ U
13
B
Sekvence
Sekvence
B
E
Iterace
E
Moˇznost 1 Moˇznost 2
....
....
....
....
D Obr. 1.1: Moˇzn´e tvary struktogram˚ u Vzhledem ke znaˇcn´e podobnosti mezi Algolem a Pascalem je obvykle z´ apis algoritmu v Algolu srozumiteln´ yi pro ˇcten´ aˇre, kter´ y zn´ a jen Pascal.2
1.2.2
Struktogramy
Struktogramy graficky zn´ azorˇ nuj´ı strukturu algoritmu. Pouˇz´ıvaj´ı tvarovˇe (nebo i barevnˇe) odliˇsn´e vyj´ adˇren´ı pro z´ akladn´ı algoritmick´e struktury (posloupnost, cyklus, podm´ınka). Z´ akladem struktogramu je vˇzdy obd´eln´ık, v jehoˇz z´ ahlav´ı je oznaˇcen´ı algoritmu nebo d´ılˇc´ı operace. Uvnitˇr obd´eln´ıku jsou vyps´ any kroky, kter´e algoritmus tvoˇr´ı; mohou do nˇej b´ yt vnoˇreny i dalˇs´ı struktogramy. Struktogramy m˚ uˇzeme pouˇz´ıvat jak pˇri rozboru u ´lohy na nejvyˇsˇs´ı u ´rovni tak pˇri vlastn´ım programov´ an´ı. Poskytuj´ı tak´e dobrou dokumentaci postupu pˇri n´ avrhu.
Pˇ r´ıklad 1.3: opˇ et kvadratick´ a rovnice Vr´ at´ıme se jeˇstˇe jednou k programu na ˇreˇsen´ı kvadratick´ ych rovnic. Struktogramy, kter´e vyjadˇruj´ı postup ˇreˇsen´ı, vid´ıte na obr´ azku 1.2. Vzhledem k tomu, ˇze struktogram pro operaci Vyˇreˇs rovnici s dan´ymi koeficienty“ nelze ” dost dobˇre vloˇzit dovnitˇr struktogramu, popisuj´ıc´ıho cyklus ˇcten´ı dat ze souboru, zakresl´ıme jej vedle.
1.2.3
Jacksonovy diagramy
Dalˇs´ı moˇznost´ı, kterou lze pouˇz´ıt pˇri popisu algoritm˚ u, jsou Jacksonovy diagramy, se kter´ ymi se setk´ ame v kapitole 10.2.1.
1.2.4
V´ yvojov´ e diagramy
Klasick´ ym prostˇredkem pro zn´ azornˇen´ı algoritmu jsou v´ yvojov´e diagramy. Zn´ azorˇ nuj´ı tok ˇr´ızen´ı“ v algoritmu. ” Znaˇcky v nich, pouˇz´ıvan´e u n´ as, jsou upraveny normou [10]. Od pouˇz´ıv´ an´ı v´ yvojov´ ych diagram˚ u pˇri programov´ an´ı se dnes upouˇst´ı (pˇresnˇeji: prakticky se nepouˇz´ıvaj´ı), ve starˇs´ıch publikac´ıch se s nimi ovˇsem lze st´ ale jeˇstˇe setkat. Zpravidla se jim vyt´ yk´ a, ˇze sp´ıˇse neˇz logickou strukturu programu zd˚ urazˇ nuj´ı druh operac´ı. Hlavn´ım probl´emem v´ yvojov´ ych diagram˚ u ovˇsem je, ˇze jde o graficky znaˇcnˇe n´ aroˇcn´ y zp˚ usob dokumentace, kter´ y ˇcasto zachycuje i jednotliv´e pˇr´ıkazy. Pˇri pozdˇejˇs´ıch zmˇen´ ach programu - i nepatrn´ ych - se zpravidla v´ yvojov´ y diagram jiˇz neaktualizuje, takˇze velice rychle zastar´ av´ a. Jako pˇr´ıklad uvedeme v´ yvojov´ y diagram programu pro ˇreˇsen´ı kvadratick´ ych rovnic (viz obr. 1.3).
2 Tot´ eˇz vˇsak nelze ˇr´ıci o programu v Algolu. Jazyk Algol obsahuje nˇekter´e konstrukce, se kter´ ymi se v ostatn´ıch programovac´ıch jazyc´ıch setk´ ame jen v´ yjimeˇcnˇe - napˇr. pˇred´ av´ an´ı parametr˚ u procedur a funkc´ı jm´enem. Tato ponˇekud problematick´ a konstrukce se naˇstˇest´ı pˇri popisu algoritmu zpravidla nevyuˇz´ıv´ a.
14
KAPITOLA 1. ALGORITMUS
ˇ sen´ı kvadratickych rovnic Reˇ
Vyˇreˇs rovnici s dan´ ymi koeficienty
´ Uvodn´ ı operace
E
Vypoˇcti diskriminant d
Dokud nen´ı konec souboru A E
B
Pˇreˇcti koeficienty a, b, c Vyˇreˇs rovnoci s koeficienty a, b, c V´ ysledky uloˇz do souboru B
ano: d=
ne: √
d=
d
vypoˇcti dva realn´e koˇreny
D
Start
´ Uvodn´ ı operace
ne ˇ Cti a, b, c ze souboru A ˇ s rovnici Reˇ s r˚ uzn´ ymi koeficienty
√ −d
vypoˇcti dva komplexnˇe sdruˇzen´e koˇreny
Obr. 1.2: Struktogram ˇreˇsen´ı kvadratick´e rovnice
Konec A?
B
Je d ≥ 0
ano
Z´avˇereˇcn´e operace
Konec
Zapiˇs v´ ysledky do B Obr. 1.3: Hrub´ y v´ yvojov´ y diagram ˇreˇsen´ı kvadratick´e rovnice
Kapitola 2
Datov´ e struktury Pov´ıd´ an´ı o datov´ ych struktur´ ach je nezbytnou souˇca ´st´ı jak´ehokoli v´ ykladu o algoritmech; podobnˇe pˇri v´ ykladu o datov´ ych struktur´ ach nelze pominout algoritmy, kter´e se pouˇz´ıvaj´ı pˇri pr´ aci s nimi. V t´eto kapitole si pov´ıme o nejˇcastˇeji pouˇz´ıvan´ ych datov´ ych struktur´ ach. Pro pˇrehlednost si je rozdˇel´ıme na z´ akladn´ı a odvozen´e.
2.1
Z´ akladn´ı datov´ e struktury
Jako z´ akladn´ı datov´e struktury budeme oznaˇcovat promˇennou, pole, z´ aznam, a objekt. S tˇemito datov´ ymi strukturami - snad aˇz na objekt - se setk´ ame ve vˇetˇsinˇe funkcion´ alnˇe orientovan´ ych programovac´ıch jazyk˚ u. Odd´ıl, vˇenovan´ y z´ akladn´ım datov´ ym struktur´ am, doplˇ nuje a upˇresˇ nuje vˇedomosti, kter´e z´ıskal ˇcten´ aˇr v z´ akladn´ım kurzu programov´ an´ı.
2.1.1
Promˇ enn´ a
Promˇenn´ a pˇredstavuje vlastnˇe pojmenovan´e m´ısto v pamˇeti poˇc´ıtaˇce. Vytvoˇr´ı se na z´ akladˇe deklarace, ve kter´e sdˇel´ıme jej´ı jm´eno (obvykle identifik´ ator) a typ. (Pˇripomeˇ nme si, ˇze specifikac´ı typu urˇcujeme mnoˇzinu hodnot, kter´e do dan´e promˇenn´e budeme smˇet ukl´ adat, a mnoˇzinu operac´ı, kter´e s danou promˇennou budeme moci prov´ adˇet. Nepˇr´ımo t´ım obvykle tak´e urˇcujeme velikost promˇenn´e, tj. mnoˇzstv´ı pamˇeti, kter´e bude promˇenn´ a zab´ırat.) Poˇc´ıtaˇc zach´ az´ı s promˇennou prostˇrednictv´ım jej´ı adresy. V programu je tato adresa vyj´ adˇrena jm´enem promˇenn´e. Pouˇzit´ı promˇenn´e v programu m˚ uˇze m´ıt dva ponˇekud odliˇsn´e v´ yznamy. Pod´ıvejme se na n´ asleduj´ıc´ı pˇriˇrazovac´ı pˇr´ıkaz v Pascalu: var i: integer; ... i := i+1; Z´ apis promˇenn´e i na prav´e stranˇe pˇriˇrazovac´ıho pˇr´ıkazu znamen´ a odkaz na hodnotu typu integer, uloˇzenou na m´ıstˇe, oznaˇcen´em jm´enem i. Z´ apis t´eto promˇenn´e na lev´e stranˇe pˇriˇrazovac´ıho pˇr´ıkazu vˇsak znamen´ a pouze odkaz na m´ısto v pamˇeti (na kter´e se uloˇz´ı v´ ysledek, tedy hodnota prav´e strany). S podobn´ ym rozd´ılem se setk´ av´ ame i pˇri pˇred´ av´ an´ı parametr˚ u procedur a funkc´ı hodnotou nebo odkazem. Budou-li f a g dvˇe procedury s hlaviˇckami procedure f(k: integer); procedure g(var k: integer); znamen´ a z´ apis f (i) vol´ an´ı procedury, pˇri kter´em se bude parametr pˇred´ avat hodnotou. To znamen´ a, ˇze procedura f dostane hodnotu v´ yrazu, zapsan´eho jako skuteˇcn´ y parametr, tedy hodnotu, uloˇzenou v pamˇeti na m´ıstˇe jm´enem i. 15
´ STRUKTURY KAPITOLA 2. DATOVE
16
Na druh´e stranˇe z´ apis g (i) pˇredstavuje vol´ an´ı procedury, pˇri kter´em se bude parametr pˇred´ avat odkazem. To znamen´ a, ˇze procedura g dostane odkaz na promˇennou i, zapsanou jako skuteˇcn´ y parametr, tj. jej´ı adresa. Procedura g m˚ uˇze vyuˇz´ıt bud’ hodnotu, uloˇzenou ve skuteˇcn´em parametru, nebo m´ısto v pamˇeti, kter´e tento parametr zab´ır´ a. Nˇekter´e programovac´ı jazyky - napˇr. C++ - tak´e umoˇzn ˇuj´ı, aby funkce vracely vypoˇctenou hodnotu odkazem (referenˇcn´ı funkce). Tak´e v takov´em pˇr´ıpadˇe se vrac´ı odkaz na m´ısto, kde je v´ ysledek uloˇzen, tedy adresa v´ ysledku. Podobnˇe jako pˇri pˇred´ av´ an´ı parametr˚ u odkazem i zde m˚ uˇzeme vyuˇz´ıt bud’ vr´ acenou hodnotu nebo m´ısto v pamˇeti, kter´e vr´ acen´ y odkaz oznaˇcuje. Druhy promˇ enn´ ych Ve vˇetˇsinˇe programovac´ıch jazyk˚ u se setk´ ame se tˇremi z´ akladn´ımi druhy promˇenn´ ych, kter´e se liˇs´ı zp˚ usobem alokace (pˇridˇelen´ı pamˇeti): Glob´ aln´ı promˇ enn´ e se vytvoˇr´ı pˇri spuˇstˇen´ı programu a existuj´ı po celou dobu bˇehu. To znamen´ a, ˇze maj´ı v programu st´ alou adresu. Napˇr. v Pascalu jsou to promˇenn´e, deklarovan´e na u ´rovni programu nebo jednotek, v C´eˇcku promˇenn´e deklarovan´e mimo tˇela funkc´ı nebo promˇenn´e s pamˇet’ovou tˇr´ıdou static. Lok´ aln´ı promˇ enn´ e (v C´eˇcku se oznaˇcuj´ı jako automatick´e ) jsou promˇenn´e, deklarovan´e v procedur´ ach nebo funkc´ıch. Vznikaj´ı v okamˇziku vol´ an´ı podprogramu, pˇri ukonˇcen´ı podprogramu zanikaj´ı. Pˇri rekurzivn´ım vol´ an´ı podprogramu ve vytvoˇr´ı zvl´ aˇstn´ı instance lok´ aln´ıch promˇenn´ ych pro kaˇzdou aktivaci. Pˇri r˚ uzn´ ych vol´ an´ıch t´ehoˇz podprogramu m˚ uˇze b´ yt jedna lok´ aln´ı promˇenn´ a uloˇzena na r˚ uzn´ ych m´ıstech v pamˇeti poˇc´ıtaˇce. Dynamick´ e promˇ enn´ e vznikaj´ı za bˇehu programu na z´ akladˇe pˇr´ıkaz˚ u (obvykle vol´ an´ı procedur pro alokaci pamˇeti - napˇr. v Pascalu procedury New, v C´eˇcku funkce malloc). Podobnˇe na z´ akladˇe pˇr´ıkaz˚ u programu i zanikaj´ı. Prostor pro nˇe ˇcerp´ a program z voln´e pamˇeti. Existence dynamick´ ych promˇenn´ ych nen´ı v´ az´ ana na zaˇca ´tek nebo konec ˇza ´dn´eho podprogramu nebo bloku.
2.1.2
Pole
Pole je posloupnost promˇenn´ ych stejn´eho typu (sloˇzek), uloˇzen´ ych v pamˇeti v nepˇretrˇzit´e ˇradˇe za sebou, a ch´ apan´ ych jako jeden celek. V deklaraci pole urˇcujeme jeho jm´eno, tj. jm´eno, kter´e oznaˇcuje danou posloupnost jako celek, a typ poˇcet sloˇzek. Poˇcet sloˇzek je d´ an rozsahem index˚ u. Chceme-li pracovat s jednotliv´ ymi prvky, urˇcujeme je pomoc´ı index˚ u. Jednorozmˇ ern´ e pole Jako jednorozmˇern´ a oznaˇcujeme pole, jejichˇz prvky jiˇz nejsou pole - tedy pole s jedn´ım indexem. Pod´ıvejme se na pˇr´ıklad deklarace jednorozmˇern´eho pole: var a: array [m .. n] of typ_sloˇ zky; Pole a se skl´ ad´ a z n − m + 1 sloˇzek typu typ sloˇzky. Pˇritom adresa prvn´ıho prvku, a[m], je totoˇzn´ a s adresou cel´eho pole a. Adresu i-t´eho prvku, a[i], vypoˇcteme pomoc´ı tzv. indexovac´ı funkce t (i) = a + (i − m) v, kde v je velikost typu typ sloˇzky, tj. poˇcet adresovateln´ ych jednotek pamˇeti 1 , kter´e zab´ır´ a jedna sloˇzka pole, a a je adresa poˇca ´tku pole. V tomto vzorci zach´ az´ıme s adresami jako s cel´ ymi ˇc´ısly.2 1 Na PC je adresovatelnou jednotkou pamˇ eti 1 byte, slabika velikosti 8 bit ; na jin´ ych poˇc´ıtaˇc´ıch to mohou b´ yt slova r˚ uzn´e velikosti - napˇr.16 nebo 32 bit . 2 Nepouˇ z´ıv´ ame tedy napˇr. adresov´e aritmetiky jazyka C.
´ ´I DATOVE ´ STRUKTURY 2.1. ZAKLADN
17
V´ıcerozmˇ ern´ e pole Pole s n indexy oznaˇcujeme jako n-rozmˇern´e. Je-li n > 1, ch´ ape se n-rozmˇern´e pole zpravidla jako pole jednorozmˇern´e, jehoˇz prvky jsou (n − 1)-rozmˇern´ a pole. Proto napˇr. jsou v Pascalu n´ asleduj´ıc´ı dvˇe deklarace ekvivalentn´ı: var b: array [m1..n1, m2..n2, ... mk..nk] of typ_sloˇ zek; var b: array [m1..n1] of array [m2..n2] of ... ... of array [mk..nk] of typ_sloˇ zek; Sloˇzky v´ıcerozmˇern´eho pole jsou v pamˇeti ukl´ ad´ any zpravidla tak, ˇze se nejrychleji mˇen´ı posledn´ı index (zapsan´ y nejv´ıce vpravo)3. To znamen´ a, ˇze dvourozmˇern´e pole, tj. matice, je uloˇzeno po ˇra ´dc´ıch. Indexovac´ı funkce pro v´ıcerozmˇern´e pole je podstatnˇe sloˇzitˇejˇs´ı neˇz v pˇr´ıpadˇe pole jednorozmˇern´eho. Adresa prvku b [i1 , i2 , . . . , ik ] bude t (i1 , . . . , ik ) = b + [(i1 − m1 ) (n2 − m2 + 1) · · · (nk − mk + 1) + (i2 − m2 ) (n3 − m3 + 1) · · · · · · (nk − mk + 1) (ik − mk )] v Zde b znamen´ a adresu poˇca ´tku pole b a v pˇredstavuje opˇet velikost jednotliv´ ych sloˇzek pole. (Tak´e v tomto vzorci zach´ az´ıme s adresami jako s cel´ ymi ˇc´ısly.) Pˇri v´ ypoˇctu hodnoty adresovac´ı funkce t pro k -rozmˇern´e pole potˇrebujeme k sˇc´ıt´ an´ı a k n´ asoben´ı (v´ yrazy (ns − ms + 1) · · · (nk − mk + 1) , s = 2, . . . , k − 1 jsou pro dan´e pole konstantn´ı a lze je spoˇc´ıtat pˇredem). Pro pˇr´ıstup ke sloˇzk´ am v´ıcerozmˇern´ ych pol´ı se tak´e nˇekdy pouˇz´ıvaj´ı pˇr´ıstupov´e (Iliffovy) vektory. V pˇr´ıpadˇe dvourozmˇern´ ych pol´ı to jsou pole ukazatel˚ u na ˇra ´dky; pro v´ıcerozmˇern´ a pole to mohou b´ yt pole ukazatel˚ u na pole ukazatel˚ u na jednorozmˇern´ a pole apod. Pod´ıvejme se na pˇr´ıklad, ve kter´em deklarujeme a pouˇzijeme pˇr´ıstupov´ y vektor pro dvourozmˇern´e pole: const n = 10; type Pole = array [1..n] of integer; var c: array [1..n, 1..n] of integer; pv: array [1..n] of ^Pole; { ... } procedure init; var i: integer; begin for i := 1 to n do pv[i] := @c[i]; end; var i: integer; { ... } init; for i := 1 to n do pv[i]^[i] := i; Posledn´ı pˇr´ıkaz znamen´ a tot´eˇz jako cyklus for i := 1 to n do c[i,i] := i; Viz t´eˇz obr. 2.1. Na poˇc´ıtaˇc´ıch, na kter´ ych je velikost ukazatele rovna 1, tj. na kter´ ych zab´ır´ a ukazatel pr´ avˇe jednu adresovatelnou jednotku pamˇeti, znamen´ a pouˇzit´ı pˇr´ıstupov´ ych vektor˚ u zrychlen´ı v´ ypoˇctu, nebot’ odpadne vˇetˇsina n´ asoben´ı. Na PC a obecnˇe na poˇc´ıtaˇc´ıch, kde je velikost ukazatele vˇetˇs´ı neˇz 1, je pouˇzit´ı pˇr´ıstupov´ ych vektor˚ u v podstatˇe stejnˇe n´ aroˇcn´e jako v´ ypoˇcet indexovac´ı funkce. 3 Tak tomu je napˇ r. v Pascalu nebo v C/C++. Na druh´e stranˇe ve Fortranu jsou v´ıcerozmˇern´ a pole ukl´ ad´ ana tak, ˇze se nejrychleji mˇen´ı prvn´ı (nejlevˇejˇs´ı) index.
´ STRUKTURY KAPITOLA 2. DATOVE
18
PV
PV[1]
c[1,1] c[1,2]
...
c[1,i]
PV[2]
c[2,1] c[2,2]
...
c[2,i]
PV[3]
c[3,1] c[3,2]
...
c[3,i]
c[4,1] c[4,2]
...
c[4,i]
PV[4] . . . PV[n − 1] PV[n] Pˇr´ıstupov´ y vektor PV
... c[i,1] c[i,2]
...
c[i,i]
ˇ adky pole C R´
Obr. 2.1: Pouˇzit´ı pˇr´ıstupov´eho vektoru
2.1.3
Z´ aznam (struktura)
Z´ aznamy (v nˇekter´ ych programovac´ıch jazyc´ıch oznaˇcovan´e jako struktury) pˇredstavuj´ı skupinu promˇenn´ ych ch´ apanou jako jeden celek. Na rozd´ıl od pole jde ovˇsem o skupinu nehomogenn´ı, jednotliv´e sloˇzky mohou b´ yt r˚ uzn´ ych typ˚ u. Chceme-li zach´ azet s jednotliv´ ymi sloˇzkami z´ aznamu, pouˇz´ıv´ ame kvalifikace: Spolu se jm´enem promˇenn´e typu z´ aznam uvedeme i jm´eno sloˇzky. Jm´eno sloˇzky vlastnˇe znamen´ a relativn´ı adresu sloˇzky vzhledem k poˇca ´tku promˇenn´e typu z´ aznam. Sloˇzky z´ aznamu jsou uloˇzeny v pamˇeti za sebou, zpravidla v poˇrad´ı, ve kter´em jsou uvedeny v deklaraci. (Pˇresnˇeji: nˇekter´e programovac´ı jazyky vyˇzaduj´ı ukl´ ad´ an´ı sloˇzek v dan´em poˇrad´ı, jin´e dovoluj´ı, aby si pˇrekladaˇc stanovil poˇrad´ı uloˇzen´ı s´ am.) Mezi jednotliv´e sloˇzky z´ aznamu m˚ uˇze pˇrekladaˇc vloˇzit pr´ azdn´ a m´ısta, kter´ a zajist´ı, aby uloˇzen´ı jednotliv´ ych sloˇzek vyhovovalo poˇzadavk˚ um syst´emu. (M˚ uˇze se napˇr. st´ at, ˇze procesor vyˇzaduje, aby promˇenn´e urˇcit´ ych typ˚ u zaˇc´ınaly na sud´ ych adres´ ach.) To znamen´ a, ˇze velikost z´ aznamu m˚ uˇze b´ yt vˇetˇs´ı neˇz souˇcet velikost´ı jednotliv´ ych sloˇzek. Variantn´ı z´ aznamy (unie) Variantn´ı ˇca ´st pascalsk´eho z´ aznamu (unie v jazyku C) pˇredstavuje skupinu promˇenn´ ych, pˇreloˇzen´ych pˇres sebe. To znamen´ a, ˇze vˇsechny sloˇzky zaˇc´ınaj´ı na t´eˇze adrese a velikost variantn´ı ˇca ´sti je rovna velikosti nejvˇetˇs´ı sloˇzky. Ke sloˇzk´ am variantn´ıch z´ aznam˚ u pˇristupujeme podobnˇe jako ke sloˇzk´ am obyˇcejn´ ych“ z´ aznam˚ u. ”
2.1.4
Objekt
Vysvˇetlen´ı z´ akladn´ıch pojm˚ u objektovˇe orientovan´eho programov´ an´ı najdete v kapitole 11.. V definici objektov´eho typu specifikujeme jednak atributy, tedy datov´e sloˇzky, jednak metody, tj. funkˇcn´ı a procedur´ aln´ı sloˇzky. Instance, tedy promˇenn´ a objektov´eho typu, obsahuje ovˇsem pouze atributy (nav´ıc pouze atributy instanc´ı).
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
19
Atributy tˇr´ıdy jsou vlastnˇe glob´ aln´ı promˇenn´e, pouze form´ alnˇe pˇridruˇzen´e k dan´e tˇr´ıdˇe. Proto se ukl´ adaj´ı obvykle nez´ avisle na instanc´ıch tˇr´ıdy. Pro ukl´ ad´ an´ı atribut˚ u instanc´ı plat´ı podobn´ a pravidla jako pro ukl´ ad´ an´ı sloˇzek z´ aznam˚ u. Skryt´ e atributy Pˇrekladaˇc m˚ uˇze pro zajiˇstˇen´ı spr´ avn´e funkce objekt˚ u pˇridat do tˇr´ıdy skryt´e atributy. M˚ uˇze se jednat jak o atributy tˇr´ıdy tak i o atributy instanc´ı. Tyto atributy nejsou zpravidla uˇzivateli pˇr´ımo pˇr´ıstupn´e. Pˇ r´ıklad 2.1 Pˇrekladaˇce pouˇz´ıvaj´ı skryt´ ych atribut˚ u objektov´ ych typ˚ u pomˇernˇe ˇcasto - napˇr. v souvislosti s polymorfismem (virtu´ aln´ımi metodami). V Pascalu nebo v C++ zˇr´ıd´ı pˇrekladaˇc pro kaˇzdou polymorfn´ı tˇr´ıdu, tj. tˇr´ıdu, kter´ a m´ a alespoˇ n jednu virtu´ aln´ı metodu, tabulku virtu´ aln´ıch metod (VMT ). Tato tabulka obsahuje adresy vˇsech virtu´ aln´ıch metod dan´e tˇr´ıdy a umoˇzn ˇuje efektivnˇe realizovat pozdn´ı vazbu. VMT je zˇrejmˇe skryt´ ym atributem tˇr´ıdy. V kaˇzd´e instanci takov´e tˇr´ıdy pak bude skryt´ y atribut instanc´ı, obsahuj´ıc´ı adresu VMT. Pˇri vol´ an´ı libovoln´e virtu´ aln´ı metody se nejprve pomoc´ı adresy, uloˇzen´e v instanci, najde VMT. V n´ı se pak vyhled´ a adresa metody, kterou je tˇreba volat. Dalˇs´ı skryt´e atributy pouˇz´ıvaj´ı nˇekter´e pˇrekladaˇce C++ v souvislosti s probl´emy okolo v´ıcen´ asobn´eho dˇedictv´ı. 4
2.2
Odvozen´ e datov´ e struktury: seznam a strom
M´ısto o odvozen´ych datov´ ych struktur´ ach by nepochybnˇe bylo vhodnˇejˇs´ı hovoˇrit o abstraktn´ıch datov´ ych struktur´ ach. Protoˇze se vˇsak tento pojem pouˇz´ıv´ a pˇredevˇs´ım pro objektov´e typy, a datov´e struktury, o kter´ ych zde bude ˇreˇc, nemus´ı b´ yt nutnˇe implementov´ any jako objekty, budeme radˇeji hovoˇrit o struktur´ ach odvozen´ ych. Mezi nimi zauj´ımaj´ı zvl´ aˇstn´ı postaven´ı seznamy a stromy, proto s nimi zaˇcneme.
2.2.1
Seznam
Seznam (anglicky list) je datov´ a struktura, kter´ a pˇredstavuje posloupnost sloˇzek. Sloˇzky jsou uspoˇra ´d´ any podle urˇcit´eho kl´ıˇce. Jako kl´ıˇc m˚ uˇze slouˇzit hodnota dat, uloˇzen´ ych ve sloˇzk´ ach, nebo hodnota funkce, vypoˇc´ıtan´e na z´ akladˇe tˇechto dat. Jin´e uspoˇra ´d´ an´ı seznamu m˚ uˇze b´ yt d´ ano poˇrad´ım, ve kter´em byly sloˇzky do seznamu pˇrid´ av´ any. Pˇrestoˇze seznam pˇredstavuje uspoˇra ´danou datovou strukturu, nemus´ı jednotliv´e sloˇzky leˇzet v pamˇeti za sebou. Seznam lze implementovat tak´e tak, ˇze kaˇzd´ a sloˇzka bude obsahovat odkaz na n´ asleduj´ıc´ı prvek; tento odkaz (ukazatel, oznaˇcovan´ y ˇcasto jako spojka, anglicky link ) bude zajiˇst’ovat n´ avaznost sloˇzek. Prvn´ı prvek seznamu oznaˇcujeme jako hlavu seznamu (head ), zbytek seznamu po odtrˇzen´ı hlavy se nˇekdy oznaˇcuje jako ohon (tail ). Seznam m˚ uˇze b´ yt i pr´ azdn´ y, nemus´ı obsahovat ˇza ´dn´ y prvek. Seznamy obvykle pouˇz´ıv´ ame jako dynamick´e datov´e struktury. To znamen´ a, ˇze v programu deklarujeme pouze ukazatel na hlavu seznamu (na obr. 2.2 je to promˇenn´ a unh); jednotliv´e prvky seznamu podle potˇreby dynamicky alokujeme nebo ruˇs´ıme. Seznamy se (spolu se stromy) oznaˇcuj´ı jako rekurzivn´ı datov´e struktury, nebot’ kaˇzd´ y prvek seznamu obsahuje odkaz na poloˇzku stejn´eho typu. 4 Pˇ resn´ a pravidla pro uloˇzen´ı odkazu na VMT a samotn´e VMT v Turbo Pascalu najdete ve firemn´ı dokumentaci. Z´ akladn´ı informace o skryt´ ych atributech tˇr´ıd v C++ najdete ve [20]; podrobnosti o Borland C++ najdete napˇr. v [17], ˇca ´st II.
´ STRUKTURY KAPITOLA 2. DATOVE
20 Jednosmˇ ern´ y seznam
Jednosmˇern´y seznam je nejjednoduˇsˇs´ı variantou seznamu: kaˇzd´ y prvek obsahuje pouze odkaz na n´ asleduj´ıc´ı prvek. Prvky jednosmˇern´eho seznamu bychom mohli v Pascalu deklarovat n´ asleduj´ıc´ım zp˚ usobem: type uPrvek = ^Prvek; Prvek = record D: data; Dalˇ sı ´: uPrvek; end; D je sloˇzka typu data, do kter´e budeme ukl´ adat informace. Sloˇzka Dalˇs´ı bude obsahovat ukazatel na dalˇs´ı prvek seznamu nebo v pˇr´ıpadˇe posledn´ıho prvku nil. Pˇri pr´ aci s jednosmˇern´ ym seznamem ˇcasto pouˇz´ıv´ ame zar´ aˇzku: posledn´ı prvek seznamu nenese uˇziteˇcn´ a data, pouze slouˇz´ı jako zar´ aˇzka pˇri prohled´ av´ an´ı. Vedle ukazatele na hlavu seznamu si pak uchov´ av´ ame tak´e ukazatel na tuto zar´ aˇzku. Pˇ r´ıklad 2.2 Odvozen´e datov´e struktury je v´ yhodn´e deklarovat jako objektov´e typy. N´ asleduj´ıc´ı deklarace objektov´eho typu seznam navazuje na deklaraci typu prvek a ukazuje procedury pro vytvoˇren´ı pr´ azdn´eho seznamu, vloˇzen´ı prvku na konec seznamu a funkci, kter´ a vyhled´ a v seznamu prvek se zadanou hodnotou (pokud jej najde, vr´ at´ı jeho adresu, jinak vr´ at´ı nil). type
seznam = object hlava, konec: uPrvek; {ukazatel na hlavu a zar´ az ˇku} constructor Vytvoˇ rSeznam; procedure Vloˇ zNaKonec(var dd: data); function Vyhledej(var dd: data):uPrvek; { ... a dalˇ sı ´ metody ... } end;
Pod´ıvejme se na nˇekter´e bˇeˇzn´e operace se seznamem. Vytvoˇ ren´ı pr´ azdn´ eho seznamu Pr´ azdn´ y seznam, sloˇzen´ y z prvk˚ u typu Prvek, vytvoˇr´ıme takto: 1. Deklarujeme ukazatel na hlavu seznamu hlava a ukazatel na zar´ aˇzku konec jako promˇenn´e typu uPrvek (ukazatel na prvek). 2. Vytvoˇr´ıme dynamickou promˇennou typu Prvek a jej´ı adresu uloˇz´ıme do promˇenn´ ych hlava a konec. 3. Do sloˇzky hlavaˆ.Dalˇs´ı, tj. do sloˇzky Dalˇs´ı novˇe vytvoˇren´e dynamick´e promˇenn´e, vloˇz´ıme hodnotu nil. Pˇ r´ıklad 2.2 (pokraˇ cov´ an´ı) Metoda seznam.VytvoˇrSeznam, kter´ a vytvoˇr´ı pr´ azdn´ y seznam, m˚ uˇze vypadat takto: {vytvoˇ rı ´ pr´ azdn´ y seznam, obsahuj´ ıc´ ı pouze zar´ az ˇku} constructor seznam.Vytvoˇ rSeznam; begin New(hlava); konec := hlava; hlava^.Dalˇ sı ´ := nil; end;
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
21
unh Data−1
ukazatel na hlavu seznamu
Data−2
hlava seznamu nil
zar´aˇzka Obr. 2.2: Jednosmˇern´ y seznam se tˇremi prvky Pˇ rid´ an´ı nov´ eho prvku na konec seznamu Nov´ y prvek, kter´ y bude obsahovat hodnotu dd, pˇrid´ ame na konec seznamu pomoc´ı n´ asleduj´ıc´ıho algoritmu: 1. Poˇzadovan´ a data vloˇz´ıme do sloˇzky D zar´ aˇzky, tj. do konecˆ.D. 2. Alokujeme nov´ y prvek, tj. novou dynamickou promˇennou typu Prvek, a jej´ı adresu uloˇz´ıme do sloˇzky Dalˇs´ı zar´ aˇzky a do konec. Novˇe alokovan´ y prvek pˇrevezme roli zar´ aˇzky. 3. Do sloˇzky Dalˇs´ı nov´e zar´ aˇzky vloˇz´ıme hodnotu nil. Pˇ r´ıklad 2.2 (pokraˇ cov´ an´ı) Pod´ıvejme se, jak vypad´ a implementace metody seznam.VloˇzNaKonec: {vloˇz´ı prvek s hodnotou dd na konec seznamu} procedure seznam.VloˇzNaKonec(var dd: data); begin konec^.D := dd; New(konec^.Dalˇ sı ´); konec := konec^.Dalˇ sı ´; konec^.Dalˇ sı ´ := nil; end;
{vloˇ zı ´ data do zar´ az ˇky} {alokuje novou zar´ az ˇku} {aktualizuje ukazatel na zar´ az ˇku}
Vyhled´ an´ı prvku v seznamu ˇ Casto tak´e potˇrebujeme zjistit, zda je v seznamu prvek, kter´ y obsahuje danou hodnotu dd. K tomu poslouˇz´ı n´ asleduj´ıc´ı algoritmus, ve kter´em vyuˇzijeme zar´ aˇzky: 1. Do pomocn´e promˇenn´e p typu uPrvek vloˇz´ıme ukazatel na hlavu seznamu. 2. Do zar´ aˇzky vloˇz´ıme hledanou hodnotu, tj. pˇriˇrad´ıme konecˆ.D := dd ; 3. Prohled´ av´ ame postupnˇe seznam, dokud nenajdeme prvek, obsahuj´ıc´ı dd. Plat´ı-li pˆ.D=dd, skonˇc´ıme, jinak do p uloˇz´ıme adresu n´ asleduj´ıc´ıho prvku seznamu. 4. Jestliˇze po skonˇcen´ı obsahuje p adresu zar´ aˇzky, seznam hledan´ y prvek neobsahuje. V opaˇcn´em pˇr´ıpadˇe obsahuje p adresu hledan´eho prvku. Pˇ r´ıklad 2.2 (pokraˇ cov´ an´ı) Pˇri implementaci metody seznam.Vyhledej si zjednoduˇs´ıme ˇzivot a budeme pˇredpokl´ adat, ˇze pro hodnoty typu data sm´ıme pouˇz´ıvat oper´ ator <>. Vˇetˇsinou to nebude pravda; potˇrebnou u ´pravu programu jistˇe zvl´ adne ˇcten´ aˇr s´ am.
´ STRUKTURY KAPITOLA 2. DATOVE
22
{vyhled´ a prvek s hodnotou dd a vr´ at´ ı jeho adresu nebo nil} function seznam.Vyhledej(var dd: data): uPrvek; var p: uPrvek; begin p := hlava; {pomocn´ a promˇ enn´ a na poˇ ca ´tku ukazuje na 1. prvek} konec^.D := dd; {uloˇ zı ´ hledanou hodnotu do zar´ az ˇky} while p^.D <> dd do p := p^.Dalˇ sı ´; {prohled´ a seznam} if p = konec then Vyhledej := nil else Vyhledej := p; {zar´ az ˇka: dd nen´ ı v seznamu} end; Zar´ aˇzka v seznamu zaruˇcuje, ˇze se prohled´ avac´ı cyklus zastav´ı na poˇzadovan´e hodnotˇe. Kdybychom ji nepouˇzili, byla by podm´ınka, ukonˇcuj´ıc´ı prohled´ av´ an´ı seznamu, sloˇzitˇejˇs´ı. Nyn´ı jiˇz m˚ uˇzeme napsan´e metody vyzkouˇset na jednoduch´em programu, kter´ y vytvoˇr´ı seznam, uloˇz´ı do nˇej nˇejak´e hodnoty a pak je v nˇem bude hledat. Pro urˇcitost (a tak´e abychom si usnadnili z´ apis) definujeme data jako standardn´ı typ integer. type data = integer; var t: data; S: seznam; q: uPrvek; { ... } begin S.Vytvoˇ rSeznam; { ... } S.Vloˇ zNaKonec(t); q := S.Vyhledej(t); { ... } end. Vloˇ zen´ı nov´ eho prvku za dan´ y prvek Pˇredpokl´ ad´ ame, ˇze zn´ ame adresu p prvku, za kter´ y chceme vloˇzit do jednosmˇern´eho seznamu nov´ y prvek. Postup bude jednoduch´ y; vyuˇzijeme pomocnou promˇennou q typu uPrvek (viz t´eˇz obr. 1.3): 1. Alokujeme novou promˇennou typu Prvek a uloˇz´ıme do n´ı potˇrebn´ a data. Adresa nov´e promˇenn´e je uloˇzena v promˇenn´e q. 2. Do qˆ.Dalˇs´ı uloˇz´ıme adresu prvku, kter´ y bude v seznamu n´ asledovat - tedy obsah pˆ.Dalˇs´ı. 3. Do pˆ.Dalˇs´ı uloˇz´ıme adresu novˇe vloˇzen´eho prvku, tedy obsah promˇenn´e q. Pˇ r´ıklad 2.2 (pokraˇ cov´ an´ı) Tento algoritmus implementujeme jako metodu seznam.VloˇzZaPrvek : {vloˇ zı ´ nov´ y prvek s daty dd za prvek, na kter´ y ukazuje p} procedure seznam.Vloˇ zZaPrvek(p: uPrvek; var dd: data); var q: uPrvek; begin New(q); q^.D := dd; q^.Dalˇ sı ´ := p^.Dalˇ sı ´; p^.Dalˇ sı ´ := q; end; Pˇr´ıklad pouˇzit´ı: t := S.Vyhledej(2); if t <> nil then S.Vloˇ zZaPrvek(t,3);
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
23
pp
qq Obr. 2.3: Vloˇzen´ı nov´eho prvku na dan´e m´ısto v seznamu
p p
qq Obr. 2.4: Smaz´ an´ı prvku seznamu za oznaˇcen´ ym prvkem Vymaz´ an´ı prvku za dan´ ym prvkem Chceme ze seznamu odstranit prvek, kter´ y n´ asleduje za prvkem s adresou, uloˇzenou v promˇenn´e p. K tomu budeme opˇet potˇrebovat pomocnou promˇennou q. Postup bude n´ asleduj´ıc´ı: 1. Do q uloˇz´ıme pˆ.Dalˇs´ı (adresu mazan´eho prvku). 2. Do pˆ.Dalˇs´ı uloˇz´ıme pˆ.Dalˇs´ıˆ.Dalˇs´ı (adresu prvku, kter´ y leˇz´ı za mazan´ ym prvkem). 3. Zruˇs´ıme prvek, na kter´ y ukazuje q. Pˇ r´ıklad 2.2 (pokraˇ cov´ an´ı) Tento algoritmus implementujeme jako metodu seznam.SmaˇzZa: {smaˇ ze prvek seznamu, kter´ y leˇ zı ´ za prvkem s adresou p} procedure seznam.Smaˇ zZa(p: uPrvek); var q: uPrvek; begin q := p^.Dalˇ sı ´; p^.Dalˇ sı ´ := p^.Dalˇ sı ´^.Dalˇ sı ´; Dispose(q); end; Obr´ azek 2.4 naznaˇcuje, co se pˇri maz´ an´ı prvku jednosmˇern´eho seznamu dˇeje. Smaz´ an´ı dan´ eho prvku Nyn´ı chceme smazat prvek, na kter´ y ukazuje ukazatel p. Tato u ´loha se m˚ uˇze na prvn´ı pohled zd´ at sloˇzitˇejˇs´ı, nebot’ nezn´ ame prvek pˇred t´ım, kter´ y chceme smazat. Jak tedy napojit“ pˇredch´ azej´ıc´ı a n´ asleduj´ıc´ı prvek? ” Velice jednoduˇse. Vyuˇzijeme toho, ˇze um´ıme smazat prvek, kter´ y leˇz´ı za oznaˇcen´ ym prvkem. Protoˇze na datech v mazan´em prvku nez´ aleˇz´ı, pˇresuneme do nˇej obsah prvku n´ asleduj´ıc´ıho a ten pak smaˇzeme: 1. Pˇresuneme obsah pˆ.Dalˇs´ıˆ.D do pˆ.D (nyn´ı prvky pˆ a pˆ.Dalˇs´ıˆ obsahuj´ı t´ aˇz data). 2. Smaˇzeme prvek s adresou pˆ.Dalˇs´ı.
´ STRUKTURY KAPITOLA 2. DATOVE
24 Pˇ r´ıklad 2.2 (pokraˇ cov´ an´ı) Pˇri implementaci se odvol´ ame na metodu seznam.SmaˇzZa. {smaˇ ze prvek seznamu, na kter´ y ukazuje p} procedure seznam.Smaˇ zTen(p: uPrvek); begin p^.D := p^.Dalˇ sı ´^.D; Smaˇ zZa(p); end; Smaz´ an´ı cel´ eho seznamu
Jakmile pˇrestaneme seznam pouˇz´ıvat, je tˇreba ho smazat, uvolnit dynamicky pˇridˇelovanou pamˇet’. Algoritmus maz´ an´ı je velice jednoduch´ y. Pˇripomeˇ nme si, ˇze seznam je bud’ pr´ azdn´ y nebo se skl´ ad´ a z hlavy (prvn´ıho prvku), za kter´ y je pˇripojen ohon (coˇz je zase seznam - tedy bud’ pr´ azdn´ y, nebo hlava + ohon atd..). Algoritmus maz´ an´ı seznamu bude vych´ azet z tohoto popisu a bude rekurzivn´ı: 1. Je-li seznam pr´ azdn´ y, konec. Jinak uloˇz adresu hlavy do pomocn´e promˇenn´e. 2. Do ukazatele na hlavu vloˇz adresu hlavy ohonu; smaˇz hlavu. 3. Smaˇz ohon (tedy seznam, kter´ y zbyl po smaz´ an´ı hlavy). Pˇ r´ıklad 2.2 (dokonˇ cen´ı) Zruˇsen´ı seznamu je typick´ au ´loha pro destruktor objektov´eho typu. Destruktor typu seznam m˚ uˇze vypadat takto (pˇri implementaci se tentokr´ at rekurzi vyhneme): {uvoln´ ı vˇ sechnu dynamicky alokovanou pamˇ et’} destructor seznam.Zruˇ sSeznam; var q: uPrvek; begin while hlava <> nil do begin q := hlava; hlava := hlava^.Dalˇ sı ´; dispose(q); end; end; N´ avrh dalˇs´ıch operac´ı s jednosmˇern´ ymi seznamy ponech´ av´ ame ˇcten´ aˇri. Jin´ e typy seznam˚ u Nˇekdy se setk´ ame se dvousmˇern´ymi seznamy (anglicky double-linked list). Od jednosmˇern´ ych seznam˚ u se liˇs´ı t´ım, ˇze kaˇzd´ y prvek obsahuje odkaz nejen na n´ asleduj´ıc´ı prvek, ale i na prvek pˇredch´ azej´ıc´ı. Dvousmˇern´ y seznam lze snadno proch´ azet v obou smˇerech - jak od hlavy k posledn´ımu prvku tak i naopak. Prvkem takov´eho seznamu m˚ uˇze b´ yt struktura tvaru type uPrvek2 = ^Prvek2; Prvek2 = record D: data; Pˇ redchoz´ ı, N´ asleduj´ ıc´ ı: uPrvek2; end; Z´ akladn´ı operace se dvousmˇern´ ym seznamem jsou podobn´e operac´ım s jednosmˇern´ ym seznamem, proto se jimi nebudeme podrobnˇeji zab´ yvat. Dalˇs´ı typ seznamu, se kter´ ym se m˚ uˇzeme setkat, je kruhov´y seznam. M˚ uˇze j´ıt o kruhov´ y seznam jednosmˇern´ y i dvousmˇern´ y. V kruhov´em seznamu se obvykle nepouˇz´ıv´ a zar´ aˇzka, m´ısto toho posledn´ı prvek obsahuje odkaz na prvn´ı prvek (pokud jde o seznam dvousmˇern´ y, tak tak´e prvn´ı prvek obsahuje odkaz na posledn´ı prvek).
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
25
A B
E
D
J
C
K
F
L
H
G
M
N
I
O
Obr. 2.5: Pˇr´ıklad tern´ arn´ıho srtomu
2.2.2
Strom
Strom je dalˇs´ı z bˇeˇznˇe pouˇz´ıvan´ ych rekurzivn´ıch datov´ ych struktur. Abstraktn´ı definice stromu se z´ akladn´ım typem T je rekurzivn´ı a vypad´ a takto: Strom se z´ akladn´ım typem T je bud’ pr´ azdn´ a struktura nebo prvek typu T, na kter´ y je pˇripojen koneˇcn´ y poˇcet disjunktn´ıch stromov´ ych struktur se z´ akladn´ım typem T (oznaˇcujeme je jako podstromy). Prvky stromu se obvykle oznaˇcuj´ı jako vrcholy nebo uzly. Vrchol, ke kter´emu nen´ı pˇripojen ˇza ´dn´ y podstrom, oznaˇcujeme jako koncov´y vrchol nebo list. Vrchol, kter´ y s´ am nen´ı pˇripojen k ˇza ´dn´emu jin´emu vrcholu, oznaˇcujeme jako koˇren. To znamen´ a, ˇze koˇren spolu se vˇsemi podstromy, kter´e jsou k nˇemu pˇripojeny, tvoˇr´ı cel´ y strom. Prvky, kter´e nejsou listy, oznaˇcujeme jako vnitˇrn´ı vrcholy stromu. Je-li G koˇren podstromu, pˇripojen´eho k uzlu C (viz obr. 2.5), ˇr´ık´ ame tak´e, ˇze G je (pˇr´ım´ ym) n´ asledovn´ıkem C a C je (pˇr´ım´ ym) pˇredch˚ udcem G. Koˇren je vrchol, kter´ y nem´ a pˇredch˚ udce; list je vrchol, kter´ y nem´ a ˇza ´dn´eho n´ asledovn´ıka. Pˇrid´ ame k definici stromu poˇzadavek, aby poˇcet podstrom˚ u, pˇripojen´ y k libovoln´emu z vrchol˚ u dan´eho stromu, nepˇres´ ahl n. Takov´ y strom oznaˇcujeme jako n-´ arn´ı. Nejˇcastˇeji se setk´ ame s bin´ arn´ımi stromy (n = 2) nebo ˇ ıslo n ( -aritu“) budeme oznaˇcovat jako typ stromu. tern´ arn´ımi stromy (n = 3). C´ ” Pˇ r´ıklad 2.3 Na obr´ azku 2.5 vid´ıte pˇr´ıklad tern´ arn´ıho stromu s vrcholy oznaˇcen´ ymi A, . . . , O. 5 Vrchol A je koˇren tohoto stromu, vrcholy J, K, L, F, M, N, O, H a I jsou listy. Strom jsme zn´ azornili jako graf, jehoˇz uzly odpov´ıdaj´ı vrchol˚ um stromu a hrany odpov´ıdaj´ı odkaz˚ um na pˇripojen´e podstromy. Jeden takov´ y podstrom, pˇripojen´ y k uzlu C, se skl´ ad´ a z uzl˚ u G, M, N, O. Vˇsimnˇete si, ˇze v tomto grafu jsme zn´ azornili i odkaz na koˇren stromu (jako hranu vedouc´ı do A). N´ asleduj´ıc´ı pojmy vyuˇzijeme pˇri u ´vah´ ach o sloˇzitosti algoritm˚ u, kter´e vyuˇz´ıvaj´ı strom˚ u: O koˇreni stromu ˇr´ık´ ame, ˇze je na prvn´ı u ´rovni. Uzly, kter´e jsou n´ asledovn´ıky koˇrene, jsou na druh´e u ´rovni. Obecnˇe je-li uzel S na u ´rovni i a uzel Sˇ je n´ asledovn´ıkem S, je Sˇ na u ´rovni i + 1. Je-li x u ´roveˇ n vrcholu X, znamen´ a to, ˇze chceme-li proj´ıt stromem od koˇrene k X, mus´ıme proj´ıt x hran (vˇcetnˇe hrany, kter´ a vstupuje do koˇrene). Proto se m´ısto o u ´rovni vrcholu ˇcasto hovoˇr´ı o d´elce vnitˇrn´ı cesty dan´eho vrcholu. Souˇcet d´elek cest vˇsech uzl˚ u v dan´em stromˇe se naz´ yv´ a d´elka vnitˇrn´ı cesty stromu. Pr˚ umˇern´ a d´elka vnitˇrn´ı cesty stromu je pak definov´ ana vztahem 5 Stromy se zpravidla zobrazuj´ ı s koˇrenem jako nejvyˇsˇs´ım vrcholem a s listy dole. Pokud v´ am to pˇripad´ a nelogick´e, m´ ate jistˇe pravdu - stromy obvykle rostou obr´ acenˇe. M˚ uˇzete s t´ım nesouhlasit, ale to je asi tak vˇse, co s t´ım m˚ uˇzete dˇelat (J. Cimrman).
´ STRUKTURY KAPITOLA 2. DATOVE
26
A
B
D
J
C
E
K
G
F
L
M
H
N
I
O
Obr. 2.6: Strom z obr. 2.5 s pˇridan´ ymi zvl´ aˇstn´ımi vrcholy
PI = kde ni je poˇcet uzl˚ u na i-t´e u ´rovni stromu.
1X ni i, n i
Kromˇe d´elky vnitˇrn´ı cesty zav´ ad´ıme tak´e d´elku vnˇejˇs´ı cesty stromu. Neˇz ji ale definujeme, mus´ıme zav´est zvl´ aˇstn´ı vrcholy stromu (na obr. 2.6 jsou zn´ azornˇeny pomoc´ı ˇctvereˇck˚ u). V n-´ arn´ım stromu dopln´ıme poˇcet n´ asledovn´ık˚ u kaˇzd´eho obyˇcejn´eho“ vrcholu zvl´ aˇstn´ımi vrcholy na hodnotu n. Zvl´ aˇstn´ı vrcholy nebudou m´ıt ” ˇza ´dn´e n´ asledovn´ıky. D´elku vnˇejˇs´ı cesty stromu definujeme jako souˇcet d´elek cest vˇsech zvl´ aˇstn´ıch vrchol˚ u. Pr˚ umˇern´ a d´elka vnˇejˇs´ı cesty stromu je PE =
1 X mi i, m i
(2.1)
kde mi je poˇcet zvl´ aˇstn´ıch vrchol˚ u na i-t´e u ´rovni stromu a m je celkov´ y poˇcet zvl´ aˇstn´ıch vrchol˚ u. Poˇcet m zvl´ aˇstn´ıch vrchol˚ u, kter´e mus´ıme do stromu pˇridat, z´ avis´ı na typu stromu, vyj´ adˇren´em ˇc´ıslem d, a na poˇctu p˚ uvodn´ıch“ vrchol˚ u n. Protoˇze do kaˇzd´eho vrcholu rozˇs´ıˇren´eho stromu vstupuje pr´ avˇe jedna hrana, je v ” nˇem celkem m + n hran. Protoˇze z kaˇzd´eho p˚ uvodn´ıho vrcholu vystupuje vˇzdy d hran, zat´ımco ze speci´ aln´ıch vrchol˚ u ˇza ´dn´e hrany nevystupuj´ı, obsahuje strom celkem dn hran, vystupuj´ıc´ıch z vrchol˚ u, a nav´ıc jednu hranu, kter´ a vstupuje do koˇrene (ta nevych´ az´ı ze ˇza ´dn´eho z vrchol˚ u). Z tˇechto u ´vah dostaneme pro ˇc´ıslo m rovnici dn + 1 = m + n,
tj.
m = (d − 1) n + 1.
Maxim´ aln´ı poˇcet vrchol˚ u ve stromu typu d s k u ´rovnˇemi je roven souˇctu Nmax (d, k) = 1 + d + d2 + · · · + dk−1 =
k X
dk−1 =
i=1
dk − 1 , d−1
nebot’ na prvn´ı u ´rovni je nejv´ yˇse jeden vrchol, kter´ y m´ a nejv´ yˇse d n´ asledovn´ık˚ u na 2. u ´rovni, z nichˇz kaˇzd´ y m´ a opˇet nejv´ yˇse d n´ asledovn´ık˚ u na 3. u ´rovni atd. Speci´ alnˇe bin´ arn´ı strom s k u ´rovnˇemi m´ a nejv´ yˇse Nmax (2, k) = 2k − 1 uzl˚ u. Stromy nejˇcastˇeji reprezentujeme jako dynamick´e datov´e struktury. Vrcholy stromu typu d mohou b´ yt z´ aznamy, kter´e obsahuj´ı ukazatele na koˇreny pˇripojen´ ych podstrom˚ u (nebo nil, nen´ı-li podstrom pˇripojen ):
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
27
type uVrchol = ^vrchol; vrchol = record Dt: data; Dalˇ sı ´: array [1..d] of uVrchol; end; Uˇziteˇcn´e informace jsou uloˇzeny ve sloˇzce Dt, kter´ a je typu data (podobnˇe jako v pˇr´ıpadˇe seznam˚ u). Poznamenejme, ˇze na jednosmˇern´ y seznam se m˚ uˇzeme d´ıvat jako na un´ arn´ı strom (strom typu 1). Bin´ arn´ı stromy V bin´ arn´ım stromu jsou ke kaˇzd´emu vrcholu pˇripojeny dva podstromy (jeden nebo oba mohou b´ yt pr´ azdn´e). Jeden z nich oznaˇc´ıme jako lev´ y a druh´ y jako prav´ y podstrom. 6 Nejˇcastˇeji se setk´ ame s bin´ arn´ımi stromy, ve kter´ ych jsou data uspoˇra ´d´ ana podle n´ asleduj´ıc´ıho pravidla: Pro kaˇzd´y vrchol U plat´ı, ˇze vˇsechny u ´daje, uloˇzen´e v lev´em podstromu, pˇripojen´em k U , jsou menˇs´ı, neˇz je u ´daj uloˇzen´y v U , a vˇsechny u ´daje, uloˇzen´e v prav´em podstromu, pˇripojen´em k U , jsou vˇetˇs´ı, neˇz je u ´daj uloˇzen´y v U. Pokud nezd˚ urazn´ıme nˇeco jin´eho, budeme d´ ale pod oznaˇcen´ım bin´ arn´ı strom rozumˇet takto uspoˇra ´dan´ y bin´ arn´ı strom. Z´ akladem implementace bin´ arn´ıho stromu bude z´ aznam vrchol, definovan´ y takto: type uVrchol = ^vrchol; vrchol = record Dt: data; Lev´ y, Prav´ y: uVrchol; end; Bin´ arn´ı strom je obvykle pˇr´ıstupn´ y pomoc´ı ukazatele na koˇren. Vrcholy zpravidla alokujeme dynamicky. Tak´e u strom˚ u se nˇekdy pouˇz´ıv´ a zar´ aˇzka: vˇsechny pr´ azdn´e“ ukazatele na n´ asledovn´ıky obsahuj´ı m´ısto hodnoty ” nil adresu pevnˇe stanoven´eho prvku - zar´ aˇzky. Jde o analogii zvl´ aˇstn´ıch uzl˚ u, kter´ ych jsme pouˇzili pˇri definice vnˇejˇs´ı cesty stromu; zar´ aˇzka je ale jen jedna, spoleˇcn´ a pro cel´ y strom (viz obr. 2.7). Data uloˇzen´ a v zar´ aˇzce jsou samozˇrejmˇe bezv´ yznamn´ a. Adresu zar´ aˇzky, podobnˇe jako adresu koˇrene, ukl´ ad´ ame do zvl´ aˇstn´ı promˇenn´e. Pˇri proch´ azen´ı bin´ arn´ıho stromu se obvykle pouˇz´ıv´ a nˇekter´ a z metod, oznaˇcovan´ ych jako pˇr´ım´e zpracov´ an´ı (anglicky preorder ), vnitˇrn´ı zpracov´ an´ı (inorder ) a zpˇetn´e zpracov´ an´ı (postorder ). Pˇri pˇr´ım´em zpracov´ an´ı nejprve zpracujeme data, uloˇzen´ a v koˇreni, pak zpracujme lev´ y podstrom a nakonec prav´ y podstrom. Pˇri vnitˇrn´ım zpracov´ an´ı nejprve projdeme lev´ y podstrom, pak zpracujeme koˇren a nakonec projdeme prav´ y podstrom; pˇri zpˇetn´em zpracov´ an´ı postupujeme v poˇrad´ı lev´ y podstrom, prav´ y podstrom, koˇren. Pˇredpokl´ adejme, ˇze pro zpracov´ an´ı dat v uzlu s adresou t pouˇzijeme proceduru P(t). Potom procedura pro zpracov´ an´ı cel´eho stromu pˇr´ımou metodou bude m´ıt tvar procedure preorder(koˇ ren: begin if t <> nil then begin P(t); preorder(t^.Lev´ y); preorder(t^.Prav´ y); end; end;
uVrchol);
{zpracov´ an´ ı koˇ rene} {zpracov´ an´ ı lev´ eho podstromu} {zpracov´ an´ ı prav´ eho podstromu}
Je zˇrejm´e, ˇze procedury, zaloˇzen´e na zb´ yvaj´ıc´ıch dvou metod´ ach, se budou liˇsit pouze poˇrad´ım pˇr´ıkaz˚ u, oznaˇ aˇr je jistˇe snadno sestav´ı s´ ˇcen´ ych koment´ aˇri. Cten´ am. 6 T´ ım vlastnˇe definujeme uspoˇra ´d´ an´ı podstrom˚ u, a to bez ohledu na data, kter´ a obsahuj´ı. Poznamenejme, ˇze pˇri grafick´em zn´ azornˇen´ı budeme opravdu kreslit lev´ y podstrom vlevo a prav´ y vpravo.
´ STRUKTURY KAPITOLA 2. DATOVE
28
Ukazatel na koˇren Koˇren
Ukazatel na zar´aˇzku Zar´aˇzka Obr. 2.7: Bin´ arn´ı strom se zar´ aˇzkou Z´ akladn´ı operace s bin´ arn´ım stromem V tomto odd´ılu se sezn´ am´ıme jen s nejelement´ arnˇejˇs´ımi operacemi s bin´ arn´ımi stromy; pouˇzit´ı tˇechto struktur vˇenujeme pozdˇeji samostatnou kapitolu. Zde si pov´ıme, jak vytvoˇrit bin´ arn´ı strom, jak do nˇej pˇridat vrchol, jak zjistit, zda je ve stromˇe vrchol se zadanou hodnotou, jak zruˇsit nalezen´ y vrchol a jak zruˇsit cel´ y strom. Tyto algoritmy formulujeme pro bin´ arn´ı stromy bez zar´ aˇzky; jejich tvar pro stromy se zar´ aˇzkou si jistˇe odvod´ı ˇcten´ aˇr s´ am. Vytvoˇ ren´ı bin´ arn´ıho stromu Vytvoˇr´ıme samozˇrejmˇe pr´ azdn´ y bin´ arn´ı strom. Strom se skl´ ad´ a ze z´ aznam˚ u (struktur) typu vrchol; v programu tedy mus´ıme m´ıt ukazatel na koˇren stromu, promˇennou typu ukazatel na vrchol. Vytvoˇren´ı pr´ azdn´eho stromu potom spoˇc´ıv´ a v pˇriˇrazen´ı hodnoty nil (NULL v C++) t´eto promˇenn´e. Pˇ r´ıklad 2.4 Tak´e strom deklarujeme jako objektov´ y typ; tentokr´ at ale pouˇzijeme jazyk C++. Deklarace typu vrchol vznikne pˇrepisem pascalsk´e deklarace, kterou jsme si uˇz uvedli: typedef struct vrchol *uVrchol; struct vrchol { data Dt; uVrchol Lev´ y, Prav´ y; }; Objektov´ y typ (tˇr´ıda) strom bude m´ıt jedin´ y soukrom´ y atribut, ukazatel na koˇren stromu uKoˇren. (Pokud bychom chtˇeli strom se zar´ aˇzkou, pˇribyl by ukazatel za ni.) Rozhran´ı tˇr´ıdy strom se bude skl´ adat z veˇrejnˇe pˇr´ıstupn´eho konstruktoru, destruktoru (je nezbytn´ y, nebot’ v instanc´ıch alokujeme dynamickou pamˇet’ a destruktor se mus´ı postarat o jej´ı uvolnˇen´ı) a metody pro vloˇzen´ı vyhled´ an´ı a smaz´ an´ı vrcholu. Dalˇs´ı pomocn´e metody deklarujeme jako soukrom´e, tj. budou je smˇet pouˇz´ıvat pouze metody tˇr´ıdy strom. class strom { uVrchol uKoˇ ren; // ukazatel na koˇ ren stromu // pomocn´ e soukrom´ e metody uVrchol nov´ yVrchol(data &dd); void vloˇ zDoStromu(uVrchol &t, data &dd); uVrchol najdi(data &dd, uVrchol t, uVrchol &d); void smaˇ zList(uVrchol t, uVrchol pˇ redch);
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
29
void smaˇ zVeVˇ etvi(uVrchol t, uVrchol pˇ redch); void smaˇ zStrom(uVrchol &t); // rozhran´ ı tˇ rı ´dy strom public: strom(); // konstruktor ~strom(); // destruktor void vloˇ z(data &dd); // vloˇ zen´ ı vrcholu se zadan´ ymi daty uVrchol hledej(data &dd, uVrchol &d); // vyhled´ an´ ı vrcholu void smaˇ z(data &dd); // smaz´ an´ ı vrcholu se zadan´ ymi daty }; Nov´ y strom vytvoˇr´ıme bud’ deklarac´ı nebo t´ım, ˇze alokujeme dynamickou promˇennou typu strom. V obou pˇr´ıpadech se automaticky zavol´ a konstruktor. Jeho jedin´ ym u ´kolem bude vloˇzit do ukazatele na koˇren hodnotu NULL, kter´ a informuje uˇzivatele, ˇze strom je pr´ azdn´ y: strom::strom() { uKoˇ ren = NULL; } Zruˇ sen´ı bin´ arn´ıho stromu Algoritmus pro smaz´ an´ı cel´eho bin´ arn´ıho stromu lze odvodit - podobnˇe jako u seznamu - z definice: strom je bud’ pr´ azdn´ y (a nen´ı co mazat), nebo je to vrchol, ke kter´emu je pˇripojen lev´ y a prav´ y podstrom (coˇz je strom). Protoˇze jde o rekurzivn´ı popis, jistˇe nebudeme pˇrekvapeni, ˇze i algoritmus bude rekurzivn´ı. Zn´ ame-li ukazatel t na koˇren, m˚ uˇzeme strom smazat takto: 1. Je-li strom, na kter´ y t ukazuje, pr´ azdn´ y, skonˇc´ıme. 2. Smaˇzeme lev´ y podstrom, pˇripojen´ y k vrcholu, na kter´ y ukazuje t. 3. Smaˇzeme prav´ y podstrom, pˇripojen´ y k vrcholu, na kter´ y ukazuje t. 4. Smaˇzeme vrchol, na kter´ y ukazuje t. Pˇ r´ıklad 2.4 (pokraˇ cov´ an´ı) Smaz´ an´ı cel´eho stromu je typick´ au ´loha pro destruktor. Protoˇze ale nelze vylouˇcit, ˇze budeme cht´ıt smazat cel´ y strom i jindy, naprogramujeme tuto operaci jako samostatnou metodu, kterou bude destruktor volat. strom::~strom() { smaˇ zStrom(uKoˇ ren); } Metoda smaˇzStrom implementuje popsan´ y rekurzivn´ı algoritmus; umoˇzn ˇuje smazat nejen cel´ y strom, ale i libovoln´ y podstrom. Parametrem, kter´ y mazan´ y (pod)strom urˇcuje, je ukazatel na jeho koˇren. Do tohoto ukazatele vloˇz´ı metoda smaˇzStrom hodnotu NULL; proto jej pˇred´ av´ ame odkazem. V definici tˇr´ıdy strom jsme tuto metodu uvedli jako soukromou; moˇzn´ a, ˇze se pozdˇeji rozhodneme ji zveˇrejnit - pˇrem´ıstit ji do sekce public. void strom::smaˇ zStrom(uVrchol &t) { if(t) { smaˇ zStrom(t->Lev´ y); smaˇ zStrom(t->Prav´ y); delete t; t = NULL; } } Vloˇ zen´ı nov´ eho vrcholu Dostali jsme data dd a chceme je zaˇradit do stromu. Pokud tam takov´ yu ´daj jeˇstˇe nen´ı, pˇrid´ ame do stromu nov´ y vrchol, jinak nen´ı tˇreba prov´ adˇet v˚ ubec nic. Pˇrid´ av´ ame-li vrchol do pr´ azdn´eho stromu, alokujeme pro nˇej pamˇet’, uloˇz´ıme do nˇej potˇrebn´ a data a adresu vrcholu uloˇz´ıme do ukazatele na koˇren. V nepr´ azdn´em stromˇe vyjdeme od koˇrene a porovn´ ame dd s hodnotou v nˇem. Je-li dd menˇs´ı neˇz uloˇzen´ a hodnota, zaˇrad´ıme nov´ y vrchol do lev´eho podstromu, jinak jej zaˇrad´ıme do prav´eho podstromu.
´ STRUKTURY KAPITOLA 2. DATOVE
30 Pˇ r´ıklad 2.4 (pokraˇ cov´ an´ı)
Vytvoˇren´ı nov´eho vrcholu budeme potˇrebovat na nˇekolika m´ıstech. Nav´ıc obsahuje operaci, kter´ a se nemus´ı podaˇrit (alokaci pamˇeti). Proto bude rozumn´e definovat ji jako samostatnou funkci, kter´ a bude vracet adresu nov´eho vrcholu. uVrchol strom::nov´ yVrchol(data &dd) { uVrchol q = new vrchol; if(!q){ Chyba(); return NULL; } else { q -> Dt = dd; q -> Lev´ y = q -> Prav´ y = NULL; return q; } } Pokud se alokace pamˇeti nepodaˇr´ı, vol´ ame funkci Chyba (); jej´ı definici si m˚ uˇzete doplnit podle potˇreby. Pˇri vkl´ ad´ an´ı nov´eho uzlu do stromu potˇrebujeme strom rekurzivnˇe prohledat. Pˇritom vyjdeme od ukazatele na koˇren a budeme pokraˇcovat pˇres ukazatele na lev´ y ˇci prav´ y podstrom. Pokud v nˇekter´em z vrchol˚ u najdeme hodnotu dd, skonˇc´ıme - n´ aˇs u ´daj tam jiˇz je a nen´ı tˇreba pˇrid´ avat dalˇs´ı uzel. Pokud v dan´em uzlu n´ aˇs u ´daj nen´ı, je tˇreba jej vloˇzit do lev´eho nebo prav´eho podstromu, urˇcen´eho opˇet ukazatelem na koˇren. Je zˇrejm´e, ˇze procedura pro vloˇzen´ı u ´daje do stromu bude rekurzivn´ı. Jej´ımi parametry budou jednak vkl´ adan´ a hodnota a jednak ukazatel na koˇren (pod)stromu, do kter´eho ji chceme vloˇzit. Pˇritom vych´ az´ıme od ukazatele na koˇren cel´eho stromu - tedy od soukrom´eho atributu. Na druh´e stranˇe nen´ı tˇreba, aby uˇzivatel vˇedˇel cokoli o koˇreni stromu; jeho z´ ajem je vloˇzit do dan´eho stromu urˇcitou hodnotu a t´ım to konˇc´ı. Proto v rozhran´ı tˇr´ıdy definujeme (veˇrejnˇe pˇr´ıstupnou) metodu vloˇz, jej´ımˇz jedin´ ym parametrem bude vkl´ adan´ a hodnota dd. Ta zavol´ a soukromou metodu vloˇzDoStromu, kter´ a teprve implementuje algoritmus vkl´ ad´ an´ı. void strom::vloˇ z(data &dd) { vloˇ zDoStromu(uKoˇ ren, &dd); } Oba parametry metody vloˇzDoStromu budeme pˇred´ avat odkazem. Adresu koˇrene proto, ˇze ji v nˇekter´ ych pˇr´ıpadech chceme mˇenit, a data kv˚ uli u ´spoˇre m´ısta na z´ asobn´ıku pˇri rekurzivn´ım vol´ an´ı. void strom::vloˇ zDoStromu(uVrchol &t, data &dd) { if(!t) t = nov´ yVrchol(dd); // je-li strom pr´ azdn´ y else { if(dd == t->Dt) return; // dd tam uˇ z je if(dd < t->Dt) vloˇ zDoStromu(t->Lev´ y, dd); else vloˇ zDoStromu(t->Prav´ y, dd); } } Vyhled´ an´ı u ´daje ve stromu Chceme zjistit, zda dan´ y strom obsahuje hodnotu dd. To znamen´ a, ˇze opˇet rekurzivnˇe projdeme dan´ y strom. 1. Je-li strom pr´ azdn´ y, hledan´ yu ´daj v nˇem nen´ı; konec. 2. Jinak porovn´ ame dd s hodnotou v koˇreni; jsou-li si rovny, nalezli jsme hledan´ yu ´daj; konec.
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
31
3. Jinak je-li dd menˇs´ı neˇz hodnota, uloˇzen´ a v koˇreni, prohled´ ame lev´ y podstrom, pˇripojen´ y ke koˇreni provedeme s n´ım tento algoritmus a skonˇc´ıme. 4. Jinak prohled´ ame prav´ y podstrom a skonˇc´ıme. Pˇ r´ıklad 2.4 (pokraˇ cov´ an´ı) Vyhled´ av´ an´ı dan´e hodnoty ve stromu implementujeme jako funkci, kter´ a v pˇr´ıpadˇe u ´spˇechu vr´ at´ı adresu nalezen´eho uzlu. V pˇr´ıpadˇe ne´ uspˇechu (ˇza ´dn´ y uzel neobsahuje dd ) vr´ at´ı NULL. ˇ Casto je tˇreba zn´ at tak´e adresu pˇredch˚ udce nalezen´eho uzlu (to ocen´ıme zejm´ena pˇri ruˇsen´ı uzl˚ u). To algoritmus nijak nezkomplikuje - neˇz se dostaneme do hledan´eho uzlu, mus´ıme proj´ıt pˇres jeho pˇredch˚ udce. Staˇc´ı si tedy jeho adresu zapamatovat v pomocn´e promˇenn´e. Proto bude m´ıt funkce strom::hledej tak´e parametr d, pˇred´ avan´ y odkazem, ve kter´em bude vracet pr´ avˇe adresu pˇredch˚ udce. Poznamenejme, ˇze koˇren nem´ a pˇredch˚ udce. Je-li hledan´ y u ´daj uloˇzen v koˇreni cel´eho stromu, vr´ at´ı funkce strom::hledej v d hodnotu NULL. Pˇri dalˇs´ım rozboru zjist´ıme, ˇze k prohled´ an´ı stromu potˇrebujeme adresu jeho koˇrene. Z podobn´ ych d˚ uvod˚ u jako pˇri vkl´ ad´ an´ı do stromu proto bude veˇrejnˇe pˇr´ıstupn´ a metoda hledej volat soukromou metodu najdi, kter´ a bude m´ıt o jeden parametr nav´ıc. Metoda hledej tak´e zajist´ı inicializaci pomocn´eho parametru d. uVrchol strom::hledej(data &dd, uVrchol &d) { d = NULL; return najdi(dd, uKoˇ ren, d); } Metoda najdi teprve implementuje popsan´ y algoritmus: uVrchol strom::najdi(data &dd, uVrchol t, uVrchol &d) { if(!t) return NULL; // pr´ azdn´ y strom: nen´ ı tam if(t->Dt == dd) return t; // naˇ sli jsme ji d = t; // jdeme d´ al: adresa pˇ redka if(dd < t->Dt) return najdi(dd, t->Lev´ y, d); // prohledej else return najdi(dd, t->Prav´ y, d); // podstromy } Zruˇ sen´ı vrcholu Zruˇsen´ı jednotliv´eho vrcholu, jeho odstranˇen´ı ze stromu, je patrnˇe nejkomplikovanˇejˇs´ı ze z´ akladn´ıch operac´ı nad bin´ arn´ım stromem. Budeme totiˇz muset rozliˇsit nˇekolik pˇr´ıpad˚ u (viz t´eˇz obr. 2.8 a 2.9): 1. Ruˇs´ıme list. V takov´em pˇr´ıpadˇe uvoln´ıme dynamickou pamˇet’, pˇridˇelenou tomuto uzlu, a odstran´ıme odkaz na ruˇsen´ y vrchol v jeho pˇredch˚ udci (nebo v ukazateli na koˇren, jestliˇze mˇel strom jen jeden vrchol). 2. Ruˇs´ıme vrchol, kter´y m´ a jen jednoho n´ asledovn´ıka. Jde o podobnou situaci jako pˇri ruˇsen´ı prvku seznamu. Adresu ruˇsen´eho prvku uloˇz´ıme do pomocn´e promˇenn´e, odkaz v pˇredch˚ udci uprav´ıme tak, aby ukazoval na n´ asledovn´ıka a vrchol zruˇs´ıme. 3. Ruˇs´ıme vrchol, kter´y m´ a dva n´ asledovn´ıky. Zde vznik´ a probl´em, ˇc´ım zruˇsen´ y vrchol nahradit. Kromˇe hodnoty dd, kter´ a n´ as jiˇz nezaj´ım´ a, obsahuje totiˇz tak´e odkazy na sv´e n´ asledovn´ıky, kter´e je tˇreba uchovat. Nav´ıc v´ ysledkem t´eto operace mus´ı b´ yt opˇet uspoˇra ´dan´ y bin´ arn´ı strom. V tomto pˇr´ıpadˇe se pouˇz´ıv´ a n´ asleduj´ıc´ıho triku: 1. Najdeme nejpravˇejˇs´ı vrchol lev´eho podstromu, pˇripojen´eho k ruˇsen´emu vrcholu (oznaˇc´ıme jej Q; to je vrchol, v nˇemˇz je uloˇzena nejvˇetˇs´ı z hodnot, menˇs´ıch neˇz v ruˇsen´em vrcholu). Nalezen´ y vrchol m´ a nejv´ yˇse jednoho n´ asledovn´ıka (jinak by nemohl b´ yt nejpravˇejˇs´ı). 2. Hodnotu z vrcholu Q pˇreneseme do vrcholu, kter´ y chceme zruˇsit.
´ STRUKTURY KAPITOLA 2. DATOVE
32
10
7
15 25
9
8
20
30
Obr. 2.8: Ruˇsen´ı listu a vrcholu s jedin´ ym n´ asledovn´ıkem
10
15
7
25
9
8
20
30
Obr. 2.9: Ruˇsen´ı vrcholu se dvˇema n´ asledovn´ıky (zde koˇrene) 3. Zruˇs´ıme vrchol Q. M´ısto nejpravˇejˇs´ıho vrcholu v lev´em podstromu (tedy nejvˇetˇs´ı hodnoty, menˇs´ı neˇz je ruˇsen´ a) m˚ uˇzeme tak´e pouˇz´ıt nejlevˇejˇs´ıho vrcholu v prav´em podstromu (tedy nejmenˇs´ı hodnoty, kter´ a je vˇetˇs´ı neˇz hodnota v ruˇsen´em vrcholu). Lze samozˇrejmˇe navrhnout i jin´e postupy; ten, kter´ y jsme zde uvedli, vˇsak patˇr´ı k nejˇcastˇeji pouˇz´ıvan´ ym. Pˇ r´ıklad 2.4 (pokraˇ cov´ an´ı) Nyn´ı navrhneme metodu pro smaz´ an´ı vrcholu ve stromˇe. Pouˇzijeme v n´ı tˇri pomocn´e promˇenn´e, nebot’ v nejhorˇs´ım pˇr´ıpadˇe budeme potˇrebovat ukazatel na ruˇsen´ y vrchol, na nejpravˇejˇs´ı vrchol lev´eho podstromu a na jeho pˇredch˚ udce. Metoda nejprve vyhled´ a prvek, kter´ y chceme zruˇsit. Pokud jej nenajde, skonˇc´ı. D´ ale zjist´ı, zda jde o list nebo zda m´ a jedin´eho n´ asledovn´ıka a pokud je nˇekter´ a z tˇechto podm´ınek splnˇena, zavol´ a pomocnou soukromou metodu smaˇzList resp. smaˇzVeVˇetvi. Pokud m´ a dva n´ asledovn´ıky, vyhled´ a nejpravˇejˇs´ı vrchol lev´eho podstromu a smaˇze jej. K tomu pouˇzije metody pro smaz´ an´ı listu nebo smaz´ an´ı vrcholu s jedin´ ym n´ asledovn´ıkem. void strom::smaˇ z(data &dd) { // smaˇ ze vrchol uVrchol t,d,q; // pomocn´ e promˇ enn´ e t = hledej(dd, d); // hled´ an´ ı mazan´ eho vrcholu if(!t) return; // pokud tam nen´ ı, konec // je to list if(t->Lev´ y==NULL && t->Prav´ y == NULL) smaˇ zList(t, d);
´ DATOVE ´ STRUKTURY: SEZNAM A STROM 2.2. ODVOZENE
33
else // m´ a jen jednoho n´ asledovn´ ıka if(t->Lev´ y == NULL || t->Prav´ y == NULL) smaˇ zVeVˇ etvi(t, d); else { // m´ a 2 n´ asledovn´ ıky // najdi nejpravˇ ejˇ sı ´ho lev´ eho n´ asledovn´ ıka q = t->Lev´ y; d = t; while(q->Prav´ y) { d = q; q = q->Prav´ y; } // pˇ resuˇ n data z *q do *t t->Dt = q->Dt; // smaˇ z *q; if(q->Lev´ y || q->Prav´ y) smaˇ zVeVˇ etvi(q, d); else smaˇ zList(q, d); } } Pˇri hled´ an´ı vrcholu, kter´ y doopravdy smaˇzeme, jsme se tentokr´ at vyhnuli rekurzi a pouˇzili jsme cyklu while. Metoda pro smaz´ an´ı listu mus´ı rozliˇsit pˇr´ıpad, ˇze maˇzeme koˇren, a pˇr´ıpad, ˇze mazan´ y list m´ a pˇredch˚ udce. K tomu n´ am poslouˇz´ı adresa pˇredch˚ udce, kterou n´ am poskytla metoda hledej ; je uloˇzena v parametru pˇredch. (Pˇripomeˇ nme si, ˇze v pˇr´ıpadˇe koˇrene vr´ at´ı funkce strom::hledej jako adresu pˇredch˚ udce hodnotu NULL.) // smaˇ ze list stromu; t je adresa mazan´ eho vrcholu, // predch je adresa pˇ redch˚udce void strom::smaˇ zList(uVrchol t, uVrchol pˇ redch) { if(pˇ redch) { // nen´ ı to koˇ ren if(pˇ redch->Lev´ y == t) pˇ redch ->Lev´ y = NULL; else pˇ redch ->Prav´ y = NULL; } else { // je to koˇ ren uKoˇ ren = NULL; delete t; } } Metoda pro smaz´ an´ı vrcholu s jedn´ım n´ asledovn´ıkem je jen nepatrnˇe sloˇzitˇejˇs´ı neˇz metoda pro smaz´ an´ı prvku seznamu. Mus´ı rozliˇsit, zda byl mazan´ y prvek lev´ ym nebo prav´ ym n´ asledovn´ıkem sv´eho pˇredch˚ udce. Tak´e v pˇr´ıpadˇe, ˇze jde o koˇren, se bude postup ponˇekud liˇsit. void strom::smaˇ zVeVˇ etvi(uVrchol t, uVrchol pˇ redch) { uVrchol q; // q je adresa n´ asledovn´ ıka q = (t->Lev´ y)?t->Lev´ y:t->Prav´ y; if(!pˇ redch) uKoˇ ren = q; // je to koˇ ren? else (pˇ redch->Lev´ y == t? // *** pˇ redch->Lev´ y: pˇ redch->Prav´ y) = q; delete t; } V pomocn´e promˇenn´e q jsme si uloˇzili adresu n´ asledovn´ıka.7 Pod´ıvejme se jeˇstˇe na jednoduch´ y pˇr´ıklad pouˇzit´ı objektov´eho typu strom: void pokusy() { strom S; // vytvoˇ rı ´ pr´ azdn´ y strom 7 Pozn´ amka pro ˇcten´ aˇre, kteˇr´ı neznaj´ı jemnosti jazyka C++: Oper´ ator podm´ınˇen´eho v´ yrazu : vytv´ aˇr´ı v C++ l-hodnotu. To znamen´ a, ˇze jej m˚ uˇzeme pouˇz´ıt i na lev´e stranˇe pˇriˇrazovac´ıho v´ yrazu - jak jsme si to dovolili v pˇredposledn´ım pˇr´ıkazu t´eto metody, oznaˇcen´em tˇremi hvˇezdiˇckami. V´ yznam: Hodnotu q uloˇz´ıme do ukazatele na lev´eho nebo prav´eho n´ asledovn´ıka, podle toho, kter´ y z nich byl nenulov´ y.
´ STRUKTURY KAPITOLA 2. DATOVE
34
30 15 25 1 7 11
40 50
16 20 22 24
26 20 29
33 35 36 39
44 45
52 54 89
Obr. 2.10: B-strom druh´eho ˇra ´du Operace s b-stromem n-t´eho ˇra ´du S.vloˇ z(10); S.vloˇ z(5); S.vloˇ z(3); // vloˇ zı ´me nˇ ejak´ e prvky S.vloˇ z(7); S.vloˇ z(17);S.vloˇ z(17); S.smaˇ z(10); // a zase je smaˇ zeme } Destruktor se v C++ vol´ a automaticky pˇri z´ aniku instance. To znamen´ a, ˇze pˇri ukonˇcen´ı funkce pokusy se cel´ y strom smaˇze, aniˇz se o to mus´ıme starat.
2.3 2.3.1
Dalˇ s´ı odvozen´ e datov´ e struktury B-strom
B-strom (b-tree) je datov´ a struktura podobn´ a stromu; jeho vrcholy se naz´ yvaj´ı str´ anky. Pro kaˇzd´ y b-strom ˇra ´du n plat´ı, ˇze: 1. Kaˇzd´ a str´ anka obsahuje maxim´ alnˇe 2n poloˇzek (uloˇzen´ ych u ´daj˚ u). 2. Kaˇzd´ a str´ anka - kromˇe koˇrenov´e - obsahuje minim´ alnˇe n poloˇzek. Koˇrenov´ a str´ anka obsahuje alespoˇ n jednu poloˇzku. 3. Kaˇzd´ a str´ anka je bud’ listovou str´ ankou, tj. nem´ a ˇza ´dn´e n´ asledovn´ıky, nebo m´ a m+1 n´ asledovn´ık˚ u, kde m je poˇcet poloˇzek v n´ı uloˇzen´ ych. 4. Vˇsechny listov´e str´ anky jsou na stejn´e u ´rovni. Z t´eto definice plyne, ˇze vˇsechny vˇetve b-strom˚ u budou - na rozd´ıl od klasick´ ych“ strom˚ u - stejnˇe dlouh´e. To ” znamen´ a, ˇze pr´ ace s b-stromy m˚ uˇze b´ yt podstatnˇe efektivnˇejˇs´ı neˇz pr´ ace s klasick´ ymi stromy. Nav´ıc lze ˇcasto volit velikost str´ anky tak, aby odpov´ıdala jednomu sektoru na disku, a tak lze podstatnˇe zefektivnit vyuˇzit´ı diskov´eho prostoru a zrychlit pr´ aci s uloˇzen´ ymi daty. Pomˇernˇe ˇcasto se setk´ ame s b-stromy 1. ˇra ´du, jejichˇz str´ anky obsahuj´ı 1 nebo 2 poloˇzky. Takov´eto b-stromy se ˇcasto naz´ yvaj´ı 2-3-stromy, nebot’ kaˇzd´ a str´ anka kromˇe list˚ u m´ a 2 - 3 n´ asledovn´ıky. Setk´ ame se tak´e s n´ azvy bin´ arn´ı b-stromy nebo bb-stromy. Pod´ıvejme se na str´ anku S, kter´ a obsahuje m poloˇzek a kter´ a nen´ı listov´ a. K n´ı je pˇripojeno m+1 podstrom˚ u8, kter´e oznaˇc´ıme jako nult´ y, prvn´ı,. . . , m-t´ y. Data, uloˇzen´ a v b-stromu, jsou uspoˇra ´d´ ana obvykle tak, ˇze vˇsechny hodnoty, uloˇzen´e nult´em podstromu, jsou menˇs´ı, neˇz prvn´ı hodnota uloˇzen´ a ve str´ ance S. Vˇsechny hodnoty, uloˇzen´e v prvn´ım podstromu, jsou vˇetˇs´ı, neˇz prvn´ı prvek ve str´ ance S a menˇs´ı, neˇz druh´ y prvek v S,. . . , a vˇsechny hodnoty, uloˇzen´e v m-t´em podstromu, jsou vˇetˇs´ı, neˇz m-t´ y prvek str´ anky S. Na obr´ azku 2.10 vid´ıme b-strom druh´eho ˇra ´du. Jeho str´ anky tedy obsahuj´ı 2 - 4 u ´daje. Pˇ rid´ an´ı u ´daje do b-stromu Pˇrid´ an´ı nov´e poloˇzky do b-stromu je v podstatˇe pˇr´ımoˇcar´e. Podobnˇe jako ve stromu vyhled´ ame listovou str´ anku, do kter´e nov´ yu ´daj patˇr´ı. Pokud je v n´ı voln´e m´ısto (obsahuje m´enˇe neˇz 2n u ´daj˚ u), vloˇz´ıme nov´ yu ´daj a skonˇc´ıme. 8 Budeme
hovoˇrit o podstromech, i kdyˇz jde o b-strom. Term´ın pod-b-strom by byl sice pˇresnˇejˇs´ı, mnˇe se ale naprosto nel´ıb´ı.
´ DATOVE ´ STRUKTURY 2.3. DALSˇ´I ODVOZENE
35
30 35 40 50 31 33
36 39
44 45
52 54 89
Obr. 2.11: Po vloˇzen´ı ˇc´ısla 31 do b-stromu na obr. 2.10 Je-li vˇsak str´ anka jiˇz zaplnˇen´ a, obsahovala by po pˇrid´ an´ı nov´eho prvku 2n+1 prvk˚ u. Nov´ y prvek tedy zaˇrad´ıme na spr´ avn´e m´ısto, takto vzniklou pˇreplnˇenou“ str´ anku rozdˇel´ıme na dvˇe po n prvc´ıch a prostˇredn´ı prvek ” pˇresuneme do pˇredch˚ udce. Jestliˇze str´ anka pˇredch˚ udce nem´ a, vytvoˇr´ıme ho. V pˇredch˚ udci se m˚ uˇze situace opakovat. Odtud plyne, ˇze b-strom roste vlastnˇe pouze tak, ˇze se rozdˇel´ı koˇrenov´ a str´ anka. Pˇ r´ıklad 2.5 Do stromu na obr. 2.10 vloˇz´ıme u ´daj 10. Snadno zjist´ıme, ˇze patˇr´ı do nejlevˇejˇs´ıho listu mezi poloˇzky 7 a 11. Protoˇze v t´eto str´ ance je voln´e m´ısto, vloˇz´ıme jej a skonˇc´ıme. D´ ale chceme pˇridat ˇc´ıslo 31. To zˇrejmˇe patˇr´ı do 4. listu zleva pˇred ˇc´ıslo 33. Tento list je ale jiˇz pln´ y. Jestliˇze sem ˇc´ıslo 31 pˇresto form´ alnˇe pˇrid´ ame, dostaneme str´ anku s prvky 31, 33, 35, 36, 39. Tu rozdˇel´ıme na dvˇe str´ anky tak, ˇze do jedn´e d´ ame hodnoty 31 a 33, do druh´e 36 a 39 a ˇc´ıslo 35 pˇresuneme do pˇredch˚ udce, o u ´roveˇ n v´ yˇse. V´ ysledek vid´ıte na obr. 2.11. Odstranˇ en´ı prvku z b-stromu Tak´e odstraˇ nov´ an´ı prvk˚ u z b-stromu je v podstatˇe jednoduch´e - i kdyˇz detailn´ı proveden´ı vypad´ a sloˇzitˇe. Mus´ıme rozliˇsit dvˇe situace, podle toho, zda neˇza ´douc´ı prvek leˇz´ı nebo neleˇz´ı v listov´e str´ ance. Jestliˇze odstraˇ novan´y prvek neleˇz´ı v listov´e str´ ance, vyhled´ ame (podobnˇe jako u strom˚ u) nejbliˇzˇs´ı menˇs´ı prvek, tedy nejpravˇejˇs´ı prvek v lev´em podstromu pˇripojen´em k tomuto prvku. Pˇresnˇeji: odstraˇ nujeme-li m-t´ y prvek ve str´ ance S, vyhled´ ame nejvˇetˇs´ı prvek v (m − 1)-t´em podstromu, pˇripojen´em k S. Tento n´ ahradn´ı“ prvek bude urˇcitˇe leˇzet v listov´e str´ ance. Jeho hodnotu pˇresuneme na m´ısto mazan´eho prvku ” a smaˇzeme n´ ahradn´ı“ prvek v listu. ” Odstranˇen´ı prvku z listu: Pot´e, co prvek z listu S vyjmeme, se mus´ıme pˇresvˇedˇcit, kolik prvk˚ u v nˇem z˚ ustalo, nebot’ kromˇe koˇrene ˇza ´dn´ a str´ anka b-stromu nesm´ı m´ıt m´enˇe neˇz n prvk˚ u. Pokud v listu S z˚ ustalo alespoˇ nn prvk˚ u, skonˇc´ıme. Pokud bude list S obsahovat n − 1 prvk˚ u, pokus´ıme se vyp˚ ujˇcit si“ potˇrebn´ y prvek ze sousedn´ı str´ anky (se ” spoleˇcn´ ym pˇredch˚ udcem). To je moˇzn´e, jestliˇze sousedn´ı str´ anka obsahuje alespoˇ n n + 1 prvk˚ u. Pˇredpokl´ adejme, ˇze si p˚ ujˇcujeme ze str´ anky T, kter´ a leˇz´ı vpravo od S. V takov´em pˇr´ıpadˇe do str´ anky S pˇrevedeme prvek z pˇredch˚ udce, kter´ y leˇz´ı mezi S a T, a nahrad´ıme jej nejlevˇejˇs´ım prvkem z T. Budeme-li si vyp˚ ujˇcovat ze str´ anky, kter´ a leˇz´ı vlevo od S, bude postup podobn´ y. Jestliˇze vˇsak sousedn´ı str´ anka obsahuje pouze n prvk˚ u, nelze si od n´ı p˚ ujˇcit“ a mus´ıme tyto dvˇe str´ anky ” slouˇcit. To m˚ uˇzeme, nebot’ obˇe tyto listov´e str´ anky maj´ı dohromady pouze 2n − 1 prvk˚ u. Pˇredpokl´ adejme, ˇze sluˇcujeme list S s listem T, kter´ y leˇz´ı vpravo od S a kter´ y m´ a s S spoleˇcn´eho pˇredch˚ udce. Potom do str´ anky S pˇrevedeme prvek z pˇredch˚ udce, kter´ y leˇz´ı mezi S a T, spolu s n´ım vˇsechny prvky z T a zruˇs´ıme str´ anku T. T´ım vznikl jeden list s 2n prvky a v jeho pˇredch˚ udci ubyl jeden prvek (ten, kter´ y leˇzel mezi S a T ).
´ STRUKTURY KAPITOLA 2. DATOVE
36
40 52
40 50 33 35 36 39
44 45
52 54 89
33 35 36 39
44 50
54 89
Obr. 2.12: Maz´ an´ı s vyp˚ ujˇcen´ım v ˇca ´sti b-stromu z obr. 2.10
15 25 30 40 1 7 11
10 20 22 24
26 28 29
33 35 36 39
44 50 52 89
Obr. 2.13: Strom z obr. 2.10 po vymaz´ an´ı prvk˚ u 45 a 54 Pˇritom se m˚ uˇze st´ at, ˇze v pˇredch˚ udci zbude pouze n − 1 prvk˚ u. Opˇet si tedy bud’ vyp˚ ujˇc´ıme od souseda nebo ho s nˇekter´ ym sousedem slouˇc´ıme (zde ale jiˇz nesm´ıme zapomenout na to, ˇze mus´ıme tak´e pˇresunout pˇripojen´e podstromy). Slouˇcen´ım dvou uzl˚ u na druh´e u ´rovni m˚ uˇze vzniknout nov´ y koˇren. To je jedin´ y zp˚ usob, jak se m˚ uˇze zmenˇsit poˇcet u ´rovn´ı b-stromu. Pˇ r´ıklad 2.6 Vezmeme strom na obr. 2.10 a vymaˇzeme poloˇzku 30 (koˇren). Nejpravˇejˇs´ı prvek v lev´em podstromu je 29; tuto hodnotu tedy pˇresuneme do koˇrene a hodnotu 29 v listu smaˇzeme. Protoˇze v tomto listu zbyly jeˇstˇe 2 prvky, m˚ uˇzeme skonˇcit. D´ ale smaˇzeme hodnotu 45. Ta je v listu, kter´ y m´ a pouze dva prvky. Proto si vyp˚ ujˇc´ıme z prav´eho souseda. Hodnotu 50 z pˇredch˚ udce pˇrevedeme na m´ısto smazan´e 45 a nahrad´ıme ji ˇc´ıslem 52 ze souseda (viz obr. 2.12). Nyn´ı smaˇzeme ve vznikl´em stromˇe hodnotu 54 (posledn´ı list vpravo). Zde si jiˇz vyp˚ ujˇcit nem˚ uˇzeme, takˇze posledn´ı str´ anku slouˇc´ıme s jej´ım lev´ ym sousedem. T´ım vznikne list, kter´ y bude obsahovat hodnoty 44, 50, 52 a 89; pˇredch˚ udce bude ale m´ıt jen jedin´ y prvek, 40. Protoˇze si zde opˇet nem˚ uˇzeme vyp˚ ujˇcit od souseda, mus´ıme slouˇcit obˇe str´ anky na u ´rovni 2. Pˇritom se v´ yˇska stromu sn´ıˇz´ı o jednu u ´roveˇ n. V´ ysledek vid´ıte na obr. 2.13.
2.3.2
Z´ asobn´ık
Z´ asobn´ık (anglicky stack 9 ) je datov´ a struktura, do kter´e odkl´ ad´ ame“ data v pr˚ ubˇehu zpracov´ an´ı. Data ze ” z´ asobn´ıku m˚ uˇzeme vyb´ırat pouze v poˇrad´ı obr´ acen´em, neˇz v jak´em jsme je do nˇej vloˇzili. To znamen´ a, ˇze poloˇzku, vloˇzenou jako posledn´ı, mus´ıme vyjmout jako prvn´ı, poloˇzku, vloˇzenou jako pˇredposledn´ı, vyjmeme jako druhou apod. O posledn´ı vloˇzen´e poloˇzce ˇr´ık´ ame, ˇze leˇz´ı na vrcholu z´ asobn´ıku. Poloˇzku, kter´ a neleˇz´ı na vrcholu z´ asobn´ıku, nelze vyjmout (museli bychom nejdˇr´ıve vyjmout vˇsechny poloˇzky, kter´e leˇz´ı nad n´ı). Pokud ale zn´ ame jej´ı polohu v z´ asobn´ıku, m˚ uˇzeme ji pˇreˇc´ıst, z´ıskat jej´ı hodnotu, aniˇz ji ze z´ asobn´ıku vyjmeme. Z´ asobn´ık lze implementovat pomoc´ı pole nebo pomoc´ı seznamu. Implementujeme-li z´ asobn´ık pomoc´ı pole, mus´ıme si neust´ ale pamatovat index poslednˇe vloˇzen´eho prvku, kter´ y je na vrcholu z´ asobn´ıku. Pouˇzijeme-li seznamu, je nejjednoduˇsˇs´ı definovat hlavu seznamu jako vrchol z´ asobn´ıku. 9 V literatuˇ re se lze tak´e setkat s n´ azvem push down storage nebo LIFO, coˇz je zkratka z oznaˇcen´ı Last In First Out, (posledn´ı dovnitˇr, prvn´ı ven - tj. objekt, kter´ y se do z´ asobn´ıku vloˇz´ı jako posledn´ı, se z nˇej vyjme jako prvn´ı).
´ DATOVE ´ STRUKTURY 2.3. DALSˇ´I ODVOZENE
Vyjmut´ı poloˇzky
37
Vloˇzen´ı dalˇs´ı poloˇzky
Vrchol z´asobn´ıku
Dno z´asobn´ıku Obr. 2.14: Sch´ema z´ asobn´ıku Pˇ r´ıklad 2.7 Na osobn´ıch poˇc´ıtaˇc´ıch ˇrady PC je z´ asobn´ık standardn´ı souˇca ´st´ı uspoˇra ´d´ an´ı pamˇeti libovoln´eho programu pod DOSem. Tento z´ asobn´ık je definov´ an jako pole byt˚ u v RAM o d´elce maxim´ alnˇe 64 KB (nebot’ DOS pouˇz´ıv´ a re´ aln´eho reˇzimu procesoru); pamˇet’ vyhrazen´ a pro z´ asobn´ık se oznaˇcuje jako z´ asobn´ıkov´ y segment“. ” Adresa poˇca ´tku z´ asobn´ıku (jej´ı segmentov´ a ˇca ´st) je uloˇzena v registru SS; relativn´ı adresa (tj. ofsetov´ a ˇca ´st adresy) vrcholu z´ asobn´ıku vzhledem k zaˇca ´tku z´ asobn´ıku je uloˇzena v registru SP. To znamen´ a, ˇze u ´pln´ a adresa vrcholu z´ asobn´ıku je SS:SP. Pro usnadnˇen´ı orientace na z´ asobn´ıku se pouˇz´ıv´ a jeˇstˇe registr BP 10 , kter´ y vˇzdy obsahuje relativn´ı adresu jist´eho v´ yznaˇcn´eho bodu na z´ asobn´ıku - viz d´ ale. Z´ asobn´ık v PC m´ a dno“ na nejvyˇsˇs´ı adrese z´ asobn´ıkov´eho segmentu a rozr˚ ust´ a se smˇerem dol˚ u“, tj. kaˇzd´ a ” ” dalˇs´ı poloˇzka je vˇzdy na niˇzˇs´ı adrese neˇz byla poloˇzka pˇredchoz´ı. Pro manipulace se z´ asobn´ıkem m´ ame k dispozici ˇradu instrukc´ı asembleru. Napˇr. instrukce PUSH XX vloˇz´ı do z´ asobn´ıku obsah registru XX, tj. okop´ıruje hodnotu z nˇej na vrchol z´ asobn´ıku a od obsahu registru SP odeˇcte 2 (pˇripomeˇ nme si, ˇze zde z´ asobn´ık roste dol˚ u“). Instrukce POP XX vyjme obsah dvou byt˚ u na vrcholu z´ asobn´ıku ” a vloˇz´ı je do registru XX. Tak´e instrukce CALL (vol´ an´ı podprogramu) a RET (n´ avrat z podprogramu) vyuˇz´ıvaj´ı z´ asobn´ık. Programy vyuˇz´ıvaj´ı z´ asobn´ıku pˇri napˇr. pˇred´ av´ an´ı skuteˇcn´ ych parametr˚ u procedur´ am a funkc´ım nebo pˇri vytv´ aˇren´ı lok´ aln´ıch promˇenn´ ych. Jako pˇr´ıklad si uk´ aˇzeme, co se dˇeje pˇri vol´ an´ı funkce 11 v Borland C/C++. Pˇr´ıkaz f(10.1, ’a’); • zp˚ usob´ı, ˇze – se vypoˇctou hodnoty skuteˇcn´ ych parametr˚ u (zde nen´ı co poˇc´ıtat) a uloˇz´ı se na z´ asobn´ık; nejprve se vloˇz´ı 8B, pˇredstavuj´ıc´ıch hodnotu 10.1, a pak dva byty, obsahuj´ıc´ı znakovou konstantu ’a’; – provede se instrukce CALL f ; ta uloˇz´ı na z´ asobn´ık n´ avratovou adresu a skoˇc´ı na prvn´ı instrukci funkce f ; • zavolan´ a funkce nejprve uloˇz´ı na z´ asobn´ık obsah registru BP; • pak pˇresune do BP obsah registru SP; 10 Oznaˇ cen´ı
registr˚ u jsou zkratky n´ azv˚ u Stack Segment, Stack Pointer a Base Pointer. p˚ ujde o vol´ an´ı vzd´ alen´e funkce, kter´ a pouˇz´ıv´ a c´eˇckovskou volac´ı konvenci. Podrobn´e informace o tom, jak vypad´ a v Borland C++ vol´ an´ı r˚ uzn´ ych typ˚ u funkc´ı pˇri r˚ uzn´ ych volac´ıch konvenc´ıch, najdete v [17], kap. 6. 11 Pˇ resnˇe
´ STRUKTURY KAPITOLA 2. DATOVE
38
Vyˇsˇs´ı adresy
pˇred´avan´e parametry CS IP BP
n´avratov´a adresa hodnota registru BP volaj´ıc´ı funkce lok´aln´ı promˇenn´e
Niˇzˇs´ı adresy
voln´a pamˇet pro dalˇs´ı r˚ ust z´asobn´ıku
Obr. 2.15: Standardn´ı oˇsetˇren´ı pˇri vstupu do vzd´ alen´e funkce (v C´eˇcku na PC) • nakonec se od SP odeˇcte tolik, kolik byt˚ u je tˇreba na lok´ aln´ı promˇenn´e (t´ım se pro nˇe vyhrad´ı m´ısto a tak se vytvoˇr´ı). Vˇsimnˇeme si triku s registrem BP. Ten nyn´ı obsahuje adresu, na kter´e je uloˇzen obsah BP ve volaj´ıc´ı funkci. Form´ aln´ı parametry maj´ı vzhledem k tomuto m´ıstu kladnou relativn´ı adresu, lok´ aln´ı promˇenn´e z´ apornou. ´ Upln´ a adresa lok´ aln´ı promˇenn´e m˚ uˇze b´ yt vyj´ adˇrena napˇr. v´ yrazem SS:[BP-10]. Registr BP pˇredstavuje jak´ ysi poˇca ´tek lok´ aln´ıch souˇradnic na z´ asobn´ıku. Viz tak´e obr´ azek 2.15. Pˇri ukonˇcen´ı volan´e funkce se provedou tyto akce: • do registru SP se pˇresune obsah registru BP (t´ım se ze z´ asobn´ıku odstran´ı vˇsechny lok´ aln´ı promˇenn´e, nebot’ vrchol z´ asobn´ıku se pˇresune do m´ısta, kde je uloˇzen obsah registru BP volaj´ıc´ı funkce); • poloˇzka na vrcholu z´ asobn´ıku se vyjme a uloˇz´ı do registru BP; t´ım se obnov´ı obsah registru BP z volaj´ıc´ı funkce; • provede se instrukce RET, kter´ a vyjme ze z´ asobn´ıku n´ avratovou adresu a skoˇc´ı na ni; t´ım se ˇr´ızen´ı vr´ atilo do volaj´ıc´ı funkce; – volaj´ıc´ı funkce odstran´ı ze z´ asobn´ıku parametry; t´ım se z´ asobn´ık uvede do stejn´eho stavu jako byl pˇred vol´ an´ım funkce f . Pozn´ amka: Operace, uveden´e symbolem •“, se obvykle oznaˇcuj´ı jako standardn´ı oˇsetˇren´ı z´ asobn´ıku ” nebo standardn´ı r´ amec z´ asobn´ıku (standard stack frame).
2.3.3
Fronta
Fronta12 (obr. 2.16) je datov´ a struktura podobn´ a z´ asobn´ıku. Jej´ı vnitˇrn´ı organizace je ale odliˇsn´ a. Prvky do fronty vkl´ ad´ ame na jedn´e stranˇe (konci) a vyb´ır´ ame je na stranˇe druh´e (ˇcele). Ve frontˇe jsou uloˇzeny v poˇrad´ı, ve kter´em byly do fronty zaˇrazeny, takˇze je z fronty vyb´ır´ ame ve stejn´em poˇrad´ı, v jak´em jsme je do n´ı vloˇzili. Podobnˇe jako u z´ asobn´ıku lze prvek z fronty vyjmout pouze v pˇr´ıpadˇe, ˇze je na ˇradˇe“; jeho hodnotu m˚ uˇzeme ” ale pˇreˇc´ıst kdykoli (prvek tam z˚ ustane). Frontu lze, podobnˇe jako z´ asobn´ık, reprezentovat bud’ pomoc´ı pole nebo pomoc´ı seznamu. 12 Vedle n´ azvu fronta (anglicky queue) se ˇcasto setk´ ame s term´ınem FIFO, coˇz je zkratka anglick´eho oznaˇcen´ı First In, First Out (prvn´ı dovnitˇr, prvn´ı ven).
´ DATOVE ´ STRUKTURY 2.3. DALSˇ´I ODVOZENE
39
Vyjmut´ı poloˇzky
Vloˇzen´ı dalˇs´ı poloˇzky
ˇ Celo fronty
Konec fronty
Obr. 2.16: Sch´ema fronty
ˇcelo fronty [0] [1]
[n − 1]
[2] [3]
konec fronty Obr. 2.17: Kruhov´ a fronta Fronta s prioritami Priorita prvku je nˇejak´ a funkce hodnoty v prvku uloˇzen´e. Fronta s prioritami (s pˇredb´ıh´ an´ım) se liˇs´ı od obyˇcejn´e“ fronty t´ım, ˇze prvky sice ukl´ ad´ ame v poˇrad´ı, ve kter´em pˇriˇsly, vyb´ır´ ame je ale v poˇrad´ı z´ avisl´em ” na jejich prioritˇe (nejprve prvek s nejvyˇsˇs´ı prioritou). Priority lze uplatˇ novat i pˇri vkl´ ad´ an´ı prvk˚ u do fronty. Fronty s prioritami se obvykle implementuj´ı pomoc´ı seznam˚ u. Kruhov´ a fronta Kruhov´ a fronta pˇredstavuje jednu z obvykl´ ych implementac´ı fronty. Vezmeme pole Q [0, . . . , n − 1] a budeme s n´ım zach´ azet, jako kdyby bylo kruhov´e, tedy kdyby po prvku Q [n − 1] n´ asledoval prvek Q [0]. Pro obsluhu kruhov´e fronty potˇrebujeme ukazatel f na ˇcelo a ukazatel r na konec. Pˇri kaˇzd´e operaci vkl´ ad´ an´ı nebo v´ ybˇeru z kruhov´e fronty kontrolujeme, zda n´ ahodou nenastala rovnost f = r. Nastane li tato situace po v´ ybˇeru z fronty, znamen´ a to, ˇze fronta je pr´ azdn´ a. Nastane-li tato rovnost po vloˇzen´ı nov´eho prvku, znamen´ a to, ˇze je fronta jiˇz pln´ a.
2.3.4
Tabulka
Tabulka symbol˚ u je datov´ a struktura, kter´ a umoˇzn ˇuje rychle zjistit, zda se v n´ı nˇekde nˇejak´ y prvek vyskytuje. Umoˇzn ˇuje tak´e snadno a rychle vloˇzit nov´ y prvek nebo nˇejak´ y prvek vyjmout. Takov´eto tabulky se ˇcasto pouˇz´ıvaj´ı v pˇrekladaˇc´ıch pˇri lexik´ aln´ı anal´ yze: jakmile naraz´ıme v pˇrekl´ adan´em programu na identifik´ ator, je tˇreba zjistit, zda byl jiˇz deklarov´ an, a pokud ne, zaˇradit jej spolu s dalˇs´ımi informacemi do tabulky.
´ STRUKTURY KAPITOLA 2. DATOVE
40
Roli tabulky symbol˚ u mohou docela dobˇre hr´ at bin´ arn´ı stromy. Jestliˇze vˇsak nem´ ame ˇza ´dn´e apriorn´ı informace o statistick´em rozdˇelen´ı vkl´ adan´ ych prvk˚ u, nebudou vytvoˇren´e stromy zpravidla pˇr´ıliˇs optim´ aln´ı. Proto se obvykle pouˇz´ıvaj´ı heˇsov´e tabulky 13 . Ukl´ ad´ an´ı hesel do tˇechto tabulek, heˇsov´ an´ı, je v principu velice jednoduch´e au ´ˇcinn´e. Na druh´e stranˇe modstraˇ nov´ an´ı hesel z heˇsov´e tabulky m˚ uˇze b´ yt obt´ıˇzn´e - d´ ale uvid´ıme, proˇc. Heˇ sov´ a tabulka Heˇsov´ a tabulka je vyhrazen´ a souvisl´ a oblast pamˇeti, kter´ a slouˇz´ı pro ukl´ ad´ an´ı prvk˚ u - hesel. Tabulka je rozdˇelena na tzv. koˇse, kter´ ych je ˇreknˇeme k a kter´e oznaˇc´ıme K (1), K (2),. . . , K (k ). V kaˇzd´em z koˇs˚ u m˚ uˇze ˇ b´ yt s hesel (kaˇzd´ y z koˇs˚ u m´ a s pozic). Casto se vol´ı s = 1. Heˇsovou tabulku lze implementovat napˇr. jako pole koˇs˚ u nebo pole hesel. Pˇri vyhled´ av´ an´ı v bin´ arn´ım stromu jsme postupnˇe porovn´ avali hledan´ y prvek s prvky v jednotliv´ ych uzlech. Naproti tomu polohu (adresu, index koˇse) prvku X v heˇsov´e tabulce vypoˇcteme pomoc´ı heˇsovac´ı funkce f (X ). Hodnotu f (X ) oznaˇcujeme jako heˇsovou adresu prvku X. Heˇsovac´ı funkce zobrazuje mnoˇzinu vˇsech moˇzn´ ych hesel na mnoˇzinu n ˆ = {1, 2, . . . , k} . Oznaˇc´ıme T mohutnost prostoru hesel, tj. poˇcet vˇsech moˇzn´ ych hesel. Pomˇer n/T , kde n je poˇcet hesel, uloˇzen´ ych v tabulce, oznaˇc´ıme jako hustotu hesel, a pomˇer n/ (sk) jako zav´ adˇec´ı faktor. Pod´ıvejme se napˇr. na tabulku, do kter´e budeme ukl´ adat identifik´ atory v programu napsan´em ve Fortranu. Fortransk´e identifik´ atory obsahuj´ı nejv´ yˇse 6 znak˚ u, prvn´ı mus´ı b´ yt p´ısmeno anglick´e abecedy, dalˇs´ı mohou b´ yt p´ısmena nebo ˇc´ıslice. Na velikosti p´ısmen nez´ aleˇz´ı. To znamen´ a, ˇze velikost prostoru hesel , tj. poˇcet moˇzn´ ych identifik´ ator˚ u, je 5 X T = 26 36i ≈ 1, 6.109 . i=0
Poˇcet identifik´ ator˚ u v programu b´ yv´ a ovˇsem o nˇekolik ˇra ´d˚ u menˇs´ı. Pˇredchoz´ı pˇr´ıklad ukazuje, poˇcet hesel, vkl´ adan´ ych do tabulky, bude zpravidla podstatnˇe menˇs´ı neˇz T. Proto se tak´e poˇcet koˇs˚ u, k, vol´ı podstatnˇe menˇs´ı neˇz T. Z toho ale plyne, ˇze m˚ uˇze nastat kolize: heˇsovac´ı funkce m˚ uˇze zobrazit dvˇe r˚ uzn´ a hesla do stejn´eho koˇse, tj. pro dvˇe r˚ uzn´ a hesla H 1 6= H2 m˚ uˇze platit f (H1 ) = f (H2 ). (Takov´ a hesla oznaˇcujeme jako synonyma vzhledem k dan´e heˇsovac´ı funkci f. ) Synonyma se ukl´ adaj´ı po ˇradˇe do n´ asleduj´ıc´ıch pozic ve stejn´em koˇsi. Jestliˇze heˇsovac´ı funkce zobraz´ı nov´e heslo do koˇse, kter´ y uˇz neobsahuje ˇza ´dnou volnou pozici, nastane pˇreplnˇen´ı. Heˇ sovac´ı funkce Heˇsovac´ı funkce zobrazuje heslo (zpravidla identifik´ ator) na adresu koˇse. Je jasn´e, ˇze potˇrebujeme funkci, kterou p˚ ujde snadno a rychle vypoˇc´ıtat a kter´ a pˇritom bude minimalizovat poˇcet koliz´ı (a t´ım i poˇcet pˇreplnˇen´ı). Pravdˇepodobnost, ˇze n´ ahodnˇe zvolen´e heslo X z prostoru hesel padne do kter´ehokoli koˇse, by mˇela b´ yt pro vˇsechny koˇse stejn´ a, rovn´ a 1/k. Heˇsovac´ı funkce by tedy mˇela v´est k rovnomˇern´emu rozdˇelen´ı hesel v tabulce. Velmi jednoduchou, nikoli ovˇsem dobrou moˇznost pˇredstavuje heˇsovac´ı funkce zaloˇzen´ a na abecedn´ım tˇr´ıdˇen´ı. Uvaˇzujme napˇr. tabulku identifik´ ator˚ u se ˇsestadvaceti koˇsi, k = 26. Heˇsovac´ı funkce bude identifik´ ator˚ um, zaˇc´ınaj´ıc´ım A, pˇriˇrazovat prvn´ı koˇs, identifik´ ator˚ um, zaˇc´ınaj´ıc´ım B druh´ y koˇs atd. V programech se ovˇsem ˇcasto vyskytuj´ı identifik´ atory, kter´e zaˇc´ınaj´ı stejnˇe - napˇr. A1, A2, A3 apod. Na druh´e stranˇe nˇekter´ a poˇca ´teˇcn´ı p´ısmena m˚ uˇze program´ ator z r˚ uzn´ ych d˚ uvod˚ u diskriminovat. 13 Anglicky hash tables, coˇ z znamen´ a asi tak ”rozsekan´e tabulky”. V ˇceˇstinˇe m˚ uˇzete setkat s oznaˇcen´ım ”tabulka z rozpt´ ylen´ ymi hesly” nebo ”rozpt´ ylen´ a tabulka”.
´ DATOVE ´ STRUKTURY 2.3. DALSˇ´I ODVOZENE
41
To znamen´ a, ˇze prost´e abecedn´ı ˇrazen´ı nebude pˇr´ıliˇs v´ yhodn´e. Jednoduchou a efektivn´ı moˇznost pˇredstavuje pouˇzit´ı operace modulo. Napˇr. interpretujeme heslo jako cel´e ˇc´ıslo14 , kter´e vydˇel´ıme vhodn´ ym ˇc´ıslem M. Zbytek po dˇelen´ı pak pouˇzijeme jako hodnotu heˇsovac´ı funkce, fD (X) = X mod M Velikost heˇsov´e tabulky pak mus´ı b´ yt v tomto pˇr´ıpadˇe alespoˇ n M. Rozumnost t´eto funkce z´ avis´ı na hodnotˇe M. Pokud bychom napˇr´ıklad zvolili M rovno nˇejak´e mocninˇe dvou, bude hodnota f urˇcena pouze posledn´ımi nˇekolika bity hesla. Kdybychom napˇr. zvolili M =256, pˇredstavovala by hodnota fD (X) posledn´ı byte hesla. Takov´ a funkce ale rozhodnˇe nebude rovnomˇern´ a; snadno nahl´edneme, ˇze je daleko pravdˇepodobnˇejˇs´ı napˇr. identifik´ ator, konˇc´ıc´ı ˇc´ıslic´ı 1, neˇz identifik´ ator, konˇc´ıc´ı ˇc´ıslic´ı 9. Lze uk´ azat, ˇze nejv´ yhodnˇejˇs´ı je pouˇz´ıt jako M prvoˇc´ıslo. Pokud lze totiˇz M zapsat jako souˇcin nˇekolika ˇc´ısel, budou se hesla, kter´ a vzniknou jedno z druh´eho permutac´ı znak˚ u, chovat ˇcasto jako synonyma. D. Knuth d´ ale uk´ azal [4], ˇze vhodn´ a jsou prvoˇc´ıseln´ a M, kter´ a nejsou dˇeliteli ˇc´ısel r t + a nebo pro rt − a mal´ a t a a (r je z´ aklad pouˇzit´e ˇc´ıseln´e soustavy). Praxe ukazuje, ˇze staˇc´ı volit M, kter´e nem´ a prvoˇc´ıseln´e dˇelitele menˇs´ı neˇz 20 [2]. Jin´ a ˇcasto pouˇz´ıvan´ a heˇsovac´ı funkce se oznaˇcuje jako stˇred druh´e mocniny“. Pˇredpokl´ ad´ ame, ˇze heslo lze ” uloˇzit v jednom poˇc´ıtaˇcov´em slovu. Umocn´ıme jej na druhou a vezmeme odpov´ıdaj´ıc´ı poˇcet bit˚ u z prostˇredka v´ ysledku. Pˇ replnˇ en´ı Pˇreplnˇen´ı nastane, jestliˇze se pokus´ıme uloˇzit nov´e heslo do pln´eho koˇse. Pod´ıvejme se na nˇekter´ a moˇzn´ a ˇreˇsen´ı t´eto situace. Asi nejjednoduˇsˇs´ı ˇreˇsen´ı m˚ uˇze b´ yt vyhled´ an´ı prvn´ıho nezaplnˇen´eho koˇse. Je-li koˇs f (x) zaplnˇen, zkus´ıme n´ asleduj´ıc´ı; pokud je pln´ y i ten, vezmeme dalˇs´ı apod. Podobnˇe postupujeme pˇri vyhled´ av´ an´ı hesla. Jestliˇze heslo nenajdeme v koˇsi f (x) a tento koˇs je pln´ y, hled´ ame v koˇsi n´ asleduj´ıc´ım atd. Zpravidla se ovˇsem neuvaˇzuje hned n´ asleduj´ıc´ı koˇs, ale koˇs f (x)+m pro nˇejak´e m > 1. T´ım obvykle dos´ ahneme ˇ ısla m a k ale nesm´ı b´ rovnomˇernˇejˇs´ıho rozloˇzen´ı obsazen´ ych koˇs˚ u. C´ yt soudˇeln´ a, jinak bychom nemohli v pˇr´ıpadˇe potˇreby proj´ıt celou tabulku. Tato metoda se naz´ yv´ a line´ arn´ı otevˇren´e adresov´ an´ı. Nen´ı pˇr´ıliˇs u ´ˇcinn´ a, nebot’ v nejhorˇs´ım pˇr´ıpadˇe pˇri n´ı vyzkouˇs´ıme k - 1 koˇs˚ u. To m˚ uˇze b´ yt podstatnˇe horˇs´ı neˇz pˇri prohled´ av´ an´ı bin´ arn´ıch strom˚ u. Pˇ r´ıklad 2.6 Uvaˇzujme heˇsovou tabulku s 26 koˇsi a s jednou pozic´ı v kaˇzd´em koˇsi. Chceme do n´ı vloˇzit identifik´ atory A1, BUBU, A2, A3, EJTA, EAI, ZX, EEE, B1 (v tomto poˇrad´ı). Pouˇzijeme otevˇren´e line´ arn´ı adresov´ an´ı s m = 1 a heˇsovac´ı funkci, jej´ıˇz hodnota bude rovna prvn´ımu p´ısmenu hesla. Prvn´ı dvˇe hesla pˇrijdou do pr´ azdn´ ych koˇs˚ u. Tˇret´ı heslo, A2, patˇr´ı do prvn´ıho koˇse, nejbliˇzˇs´ı voln´ y je ale aˇz tˇret´ı. Podobnˇe A3 pˇrijde do ˇctvrt´eho koˇse. Heslo EJTA patˇr´ı do p´ at´eho koˇse, kter´ y je voln´ y... atd. Viz obr. 2.18. Probl´emy s pˇreplnˇen´ım lze odstranit tak´e t´ım, ˇze hesla nebudeme ukl´ adat do pˇr´ımo koˇs˚ u, ale do seznam˚ u, pˇripojen´ ych ke koˇs˚ um. Koˇs bude obsahovat pouze ukazatel na hlavu seznamu hesel, kter´ a jsou v nˇem uloˇzena. Sch´ema takov´eto tabulky vid´ıte na obr. 2.19. 14 Takov´ ato
funkce bude samozˇrejmˇe silnˇe z´ avisl´ a na zp˚ usobu ukl´ ad´ an´ı znakov´ ych ˇretˇezc˚ u v dan´em syst´emu.
´ STRUKTURY KAPITOLA 2. DATOVE
42
f (x) : 1 1 5 5
A1
BU BU
A2
1
2
3
ˇc´ıslo koˇse:
A3
EJT A
EAI
5
6
4
...
ZX 26
Obr. 2.18: Vkl´ ad´ an´ı hesel do heˇsov´e tabulky
ˇc. koˇse Tabulka 1
A1
2 3
nil
4
nil
5
24 25 26
Pˇripojen´ y seznam hesel A2
BU BU
B1
EJT A
EAI
A3
nil
EEE
nil
nil nil ZX
nil
Obr. 2.19: Heˇsov´ a tabulka se seznamy pˇripojen´ ymi ke koˇs˚ um
´ DATOVE ´ STRUKTURY 2.3. DALSˇ´I ODVOZENE
43
4
4 5
5
3
3
2 6 1
1
2 (a)
(b)
Obr. 2.20: (a) orientovan´ y graf, (b) neorientovan´ y graf
2.3.5
Grafy
Orientovan´y graf G definujeme jako dvojici G = {U, H} ,kde U je koneˇcn´ a mnoˇzina uzl˚ u, kterou zpravidla ztotoˇzn ˇujeme s mnoˇzinou n ˆ = {1, 2, . . . , n} a H ⊂ U × U je mnoˇzina orientovan´ ych hran. O orientovan´e hranˇe e = hi, ji kde i a j jsou uzly, ˇrekneme, ˇze jde z uzlu i do uzlu j. Orientovan´ y graf m˚ uˇze obsahovat i hranu hi, ji. (M´ısto hi, ji se pro orientovan´e hrany tak´e pouˇz´ıv´ a z´ apis i → j.) Jestliˇze ztotoˇzn´ıme hranu hi, ji s hranou hj, ii, dostaneme neorientovan´y graf. Grafy se obvykle zn´ azorˇ nuj´ı obr´ azky podobn´ ymi jako je 2.20. Nˇekdy je vhodn´e definovat zobrazen´ı h : H → R kter´e jednotliv´ ym hran´ am pˇriˇrazuje ˇc´ıseln´e hodnoty. Pak hovoˇr´ıme o ohodnocen´em grafu. Posloupnost orientovan´ ych hran hi, i1 i , hi1 , i2 i , . . . , hik , jioznaˇcujeme jako cestu, vych´ azej´ıc´ı z uzlu i a konˇc´ıc´ı v uzlu j. Na obr´ azku 2.20 (a) vid´ıme napˇr. cestu h1, 5i, h5, 3i, h3, 4i z uzlu 1 do uzlu 4. Snadno zjist´ıme, ˇze pokud graf G obsahuje cestu z uzlu i do j a z j do k, obsahuje tak´e cestu z i do k. Cestu, jej´ıˇz poˇca ´teˇcn´ı uzel je roven uzlu koncov´emu, oznaˇcujeme jako cyklus. Podobnˇe jestliˇze v neorientovan´em grafu existuje posloupnost neorientovan´ ych hran hi, i 1 i , hi1 , i2 i , . . . , hik , ji, oznaˇc´ıme ji jako cestu, spojuj´ıc´ı uzly i a j. Uzly, mezi kter´ ymi existuje cesta, oznaˇc´ıme jako sdruˇzen´e. Nen´ı tˇeˇzk´e uk´ azat, ˇze mnoˇzina vˇsech uzl˚ u, sdruˇzen´ ych s uzlem i, tvoˇr´ı tˇr´ıdu ekvivalence. Mnoˇzina tˇechto uzl˚ u spolu s hranami, kter´e je spojuj´ı, se naz´ yv´ a komponenta grafu. Z t´eto definice plyne, ˇze pokud uzly i a j leˇz´ı v t´eˇze komponentˇe, existuje mezi nimi cesta. Jestliˇze naopak leˇz´ı ve dvou r˚ uzn´ ych komponent´ ach grafu, cesta mezi nimi neexistuje. Komponenty tedy pˇredstavuj´ı vlastnˇe samostatn´e grafy. Neorientovan´ y graf, kter´ y se skl´ ad´ a z jedin´e komponenty, oznaˇcujeme jako souvisl´y . Obr´ azek 2.21 ukazuje neorientovan´ y graf, sloˇzen´ y ze dvou komponent. Orientovan´e grafy se ˇcasto reprezentuj´ı pomoc´ı incidenˇcn´ıch 4 matic. Incidenˇcn´ı matice grafu G, kter´ y m´ a n uzl˚ u, bude typu n × n a jej´ı prvek aij bude roven 1, jestliˇze graf obsahuje hranu 5 hi, ji, a 0 v pˇr´ıpadˇe, ˇze ji neobsahuje. 3 Chceme-li reprezentovat ohodnocen´ y graf, dopln´ıme do nˇej hrany, kter´e se v nˇem nevyskytuj´ı, a pˇridˇel´ıme jim napˇr. ohodnocen´ı +∞ (nebo jakoukoli jinou hodnotu, kter´ a se nem˚ uˇze vyskytnout jako ohodnocen´ı existuj´ıc´ı hrany). Graf pak reprezentujeme matic´ı, jej´ıˇz prvky obsahuj´ı ohodnocen´ı jednotliv´ ych hran.
6
2 1
Obr. 2.21: Graf sloˇzen´ y ze dvou komponent: Bude-li se u grafu mˇenit pr˚ ubˇeˇznˇe poˇcet hran ˇci jejich ohodno- 1-4-6 a 2-3-5. cen´ı, budeme prostˇe mˇenit prvky incidenˇcn´ı matice. Jestliˇze se ale m˚ uˇze mˇenit i poˇcet uzl˚ u, je v´ yhodnˇejˇs´ı reprezentovat graf pomoc´ı seznamu uzl˚ u. K z´ aznamu o kaˇzd´em z uzl˚ u pˇripoj´ıme seznam hran, kter´e z nˇej vych´ azej´ı (nebo hran, kter´e do nˇej vch´ azej´ı).
´ STRUKTURY KAPITOLA 2. DATOVE
44
2.3.6
Mnoˇ ziny
Mnoˇziny s prvky ordin´ aln´ıch typ˚ u Nejˇcastˇeji se setk´ av´ ame s mnoˇzinami prvk˚ u nˇekter´eho z ordin´ aln´ıch typ˚ u (znaky, intervaly, v´ yˇctov´e typy). Protoˇze ordin´ aln´ı typy maj´ı pouze koneˇcn´ y poˇcet hodnot, m˚ uˇzeme odpov´ıdaj´ıc´ı mnoˇzinu reprezentovat pomoc´ı bitov´ ych pˇr´ıznak˚ u. Jestliˇze m´ a b´ azov´ y typ, tj. typ prvk˚ u mnoˇziny M, celkem n r˚ uzn´ ych hodnot, m˚ uˇzeme tyto hodnoty oˇc´ıslovat, pˇriˇradit jim ˇc´ısla 0, 1, . . . , n − 1. Nult´ y bit promˇenn´e M bude roven 1, pr´ avˇe kdyˇz mnoˇzina M obsahuje prvek s poˇradov´ ym ˇc´ıslem 0, prvn´ı bit M bude indikovat pˇr´ıtomnost prvku s poˇradov´ ym ˇc´ıslem 1 atd. Snadno se pˇresvˇedˇc´ıme, ˇze obecnˇe pˇr´ıtomnost k -t´e hodnoty b´ azov´eho typu (pˇri ˇc´ıslov´ an´ı od nuly) indikuje bit ˇc´ıslo k − [k/8] 8 = k mod 8 v bytu ˇc´ıslo [k/8] = k div 8. Pr´ azdn´ a mnoˇzina bude m´ıt vˇsechny bity nulov´e. Pod´ıvejme se na promˇennou paleta, definovanou jako mnoˇzinu barev : type barvy = (ˇ cerven´ a, z ˇlut´ a, zelen´ a, modr´ a, b´ ıl´ a, oranˇ zov´ a fialov´ a, c ˇern´ a); var paleta: set of barvy; Protoˇze b´ azov´ y typ barvy m´ a pouze osm prvk˚ u, staˇc´ı k reprezentaci jak´ekoli mnoˇziny barev - tedy i promˇenn´e paleta - jeden byte. Obsahuje-li paleta zelenou barvu, bude 3. bit roven 1, jinak bude nulov´ y. Z´ akladn´ı operace s mnoˇzinami implementujeme jako bitov´e operace. Pˇrid´ an´ı prvku do mnoˇziny M znamen´ a nastaven´ı pˇr´ısluˇsn´eho bitu na hodnotu 1, vyjmut´ı prvku z mnoˇziny M odpov´ıd´ a nastaven´ı pˇr´ısluˇsn´eho bitu na 0. Test pˇr´ıtomnosti prvku (v Pascalu oper´ ator in) v mnoˇzinˇe M je testem hodnoty bitu. Doplnˇek C (M ) mnoˇziny M do mnoˇziny vˇsech prvk˚ u b´ azov´eho typu vytvoˇr´ıme pomoc´ı bitov´e negace. Pr˚ unik dvou mnoˇzin M a N se stejn´ ym b´ azov´ ym typem z´ısk´ ame pomoc´ı bitov´e konjunkce (prvek je v pr˚ uniku M ∩ N , je-li z´ aroveˇ n v M i v N ), sjednocen´ı pomoc´ı operace bitov´e disjunkce (prvek je ve sjednocen´ı M ∪ N je-li ” alespoˇ n v jedn´e z mnoˇzin M nebo N ). Jen nepatrnˇe sloˇzitˇejˇs´ı je rozd´ıl mnoˇzin: mnoˇzina M − N obsahuje prvky, kter´e jsou v M a nejsou v N (jsou v doplˇ nku N ), takˇze M − N = M ∩ C (N ). V´ ysledek tedy bude konjunkce M a bitov´e negace N. Mnoˇ ziny s obecn´ ymi prvky Jinou moˇznost, jak implementovat mnoˇziny, pˇredstavuj´ı seznamy objekt˚ u. Pˇredpokl´ adejme, ˇze vˇsechny moˇzn´e prvky naˇs´ı mnoˇziny M m˚ uˇzeme reprezentovat pomoc´ı instanc´ı objektov´ ych typ˚ u se spoleˇcn´ ym pˇredkem T. Pak m˚ uˇzeme M implementovat jako seznam, jehoˇz sloˇzkami budou ukazatele na typ T a budou obsahovat adresy prvk˚ u. Operace s takto implementovan´ ymi mnoˇzinami jsou ovˇsem podstatnˇe n´ aroˇcnˇejˇs´ı na ˇcas, nebot’ znamenaj´ı opakovan´e prohled´ av´ an´ı seznam˚ u.
Kapitola 3
Metody n´ avrhu algoritm˚ u V t´eto kapitole budeme hovoˇrit o nejuˇz´ıvanˇejˇs´ıch metod´ ach n´ avrhu algoritm˚ u, tedy vlastnˇe o zp˚ usobech ˇreˇsen´ı probl´em˚ u se zˇretelem k tomu, ˇze v´ ysledkem m´ a b´ yt program. Pˇrehled, kter´ y zde uvedeme, samozˇrejmˇe nem˚ uˇze b´ yt vyˇcerp´ avaj´ıc´ı. Mus´ıme ovˇsem zd˚ uraznit, ˇze ˇza ´dn´ a z tˇechto metod nemus´ı v´est k c´ıli. V´ yklad v t´eto kapitole budeme ilustrovat pouze velmi jednoduch´ ymi pˇr´ıklady. S dalˇs´ımi aplikacemi popsan´ ych metod se setk´ ame v n´ asleduj´ıc´ıch kapitol´ ach.
3.1
Rozdˇ el a panuj
Rozdˇel a panuj (anglicky divide and conquer ) je nejbˇeˇznˇejˇs´ı metoda, se kterou se m˚ uˇzeme setkat. M˚ uˇzeme se na ni d´ıvat jako na aplikaci postupu shora dol˚ u, se kter´ ym jsme se sezn´ amili v kap. 1.1.3. Potˇrebujeme zpracovat mnoˇzinu V sloˇzenou z n u ´daj˚ u. Toto mnoˇzinu rozdˇel´ıme na k disjunktn´ıch podmnoˇzin, kter´e zpracujeme kaˇzdou zvl´ aˇst’. Z´ıskan´e d´ılˇc´ı v´ ysledky pak spoj´ıme, odvod´ıme z nich ˇreˇsen´ı pro celou mnoˇzinu V. Pˇritom se m˚ uˇze st´ at, ˇze probl´em zpracov´ an´ı d´ılˇc´ıch podmnoˇzin je stejn´eho typu jako p˚ uvodn´ı probl´em zpracov´ an´ı vˇsech n u ´daj˚ u. V takov´em pˇr´ıpadˇe vede opakov´ an´ı metody rozdˇel a panuj zcela pˇrirozenˇe k rekurzi. Pod´ıvejme se pro urˇcitost na situaci, kdy mnoˇzinu vstupn´ıch dat, reprezentovanou polem A, rozdˇel´ıme na dvˇe podmnoˇziny a kdy zpracov´ an´ı takto z´ıskan´ ych podmnoˇzin pˇredstavuje u ´lohu stejn´eho typu jako byla u ´loha p˚ uvodn´ı. V takov´em pˇr´ıpadˇe bychom mohli metodu rozdˇel a panuj symbolicky zapsat ve tvaru n´ asleduj´ıc´ı funkce RAP: var A: array [1..n] of data; {vstupn´ ı data} function RAP(p,q: integer): v´ ysledek; {ˇ reˇ sen´ ı pro i=p,..,q} var m: integer; {1 <= p <= q <= n} begin if MAL´ E(p,q) then RAP := G(p,q) else begin m := ROZDˇ EL(p,q); {p < m < q} ˇ RAP(p, m) a RAP(m + 1, q)“ RAP := SLOZ ” end; end; ´ je booleovsk´ Zde pˇredpokl´ ad´ ame, ˇze MALE a funkce, kter´ a vr´ at´ı true, je-li interval p..q dostateˇcnˇe mal´ y, aby jej nebylo tˇreba d´ ale dˇelit. V takov´em pˇr´ıpadˇe zpracuje prvky A [p] , . . . , A [q] funkce G. Jinak urˇc´ıme pomoc´ı ˇ ˇc´ıslo m, kter´e interval p..q rozdˇel´ı na dva podintervaly a v´ procedury ROZDEL ysledek sestav´ıme z d´ılˇc´ıch ˇ v´ ysledk˚ u pomoc´ı operace SLOZ. Pˇ r´ıklad 3.1 (bin´ arn´ı vyhled´ av´ an´ı) Je d´ ano setˇr´ıdˇen´e pole A cel´ ych ˇc´ısel. Hodnoty jsou v tomto poli uloˇzeny v neklesaj´ıc´ım poˇrad´ı. Naˇs´ım u ´kolem je urˇcit, zda toto pole obsahuje zadanou hodnotu x, a pokud ano, vr´ atit index prvku, ve kter´em je uloˇzena. Pokud x v poli A nen´ı, vr´ at´ıme 0. 45
´ ˚ KAPITOLA 3. METODY NAVRHU ALGORITMU
46
Postup ˇreˇsen´ı metodou rozdˇel a panuj : Pole A rozdˇel´ıme na nˇekolik u ´sek˚ u (napˇr. na dva, pˇribliˇznˇe stejnˇe dlouh´e) a budeme testovat kaˇzd´ y zvl´ aˇst’. Protoˇze je toto pole seˇrazen´e podle velikosti prvk˚ u, snadno zjist´ıme, zda v nˇem m˚ uˇze dan´e x v˚ ubec leˇzet. Kaˇzd´ yzu ´sek˚ u ovˇsem pˇredstavuje opˇet pole typu integer, takˇze jej znova rozdˇel´ıme. Dˇelen´ı skonˇc´ı, kdyˇz dojdeme ku ´sek˚ um d´elky 1. Zde jiˇz porovn´ an´ım snadno zjist´ıme, zda jsme zadan´e x naˇsli. Algoritmus, kter´ ym zjist´ıme, zda u ´sek A [p] , . . . , A [p] obsahuje hodnotu x, m˚ uˇze m´ıt n´ asleduj´ıc´ı tvar: 1. Je-li p = q, zjist´ıme, zda plat´ı A [p] = x. Pokud ano, je v´ ysledkem p, jinak je v´ ysledkem 0. 2. Jinak poloˇz´ıme m := (p + q) div 2. 3. Je-li x ∈ hA [p] , A [m]i, opakujeme tento postup pro q := m, jinak 4. je-li x ∈ hA [m + 1] , A [q]i, opakujeme tento postup pro p := m + 1, jinak 5. hodnota x v poli A nen´ı, v´ ysledek je 0. Pod´ıvejme se, jak je to se sloˇzitost´ı tohoto algoritmu. Nejprve budeme pˇredpokl´ adat, ˇze d´elka pole A je rovna n = 2m pro nˇejak´e pˇrirozen´e m. V prvn´ım kroku pole rozdˇel´ıme na dva u ´seky d´elky 2 m−1 a v jednom z nich budeme hledat. V n´ asleduj´ıc´ım kroku dostaneme u ´sek d´elky 2m−2 , pak 2m−3 atd. To znamen´ a, ˇze po m kroc´ıch dospˇejeme k u ´seku d´elky 1. Odtud plyne, ˇze pro n = 2m je poˇcet operac´ı K u ´mˇern´ y m = log2 n, D´ ale je zˇrejm´e, ˇze pˇri n < 2m nem˚ uˇze b´ yt poˇcet operac´ı vˇetˇs´ı neˇz pˇri n = 2m . To znamen´ a, ˇze poˇcet operac´ı pˇri bin´ arn´ım vyhled´ av´ an´ı je vˇzdy O (log2 n). Poznamenejme, ˇze prohled´ an´ı pole prvek po prvku vyˇzaduje O (n) operac´ı.
3.2
Hladov´ y algoritmus
Hladov´ y algoritmus pˇredstavuje velmi pˇr´ımoˇcar´ y pˇr´ıstup k ˇreˇsen´ı urˇcit´e tˇr´ıdy optimalizaˇcn´ıch u ´loh. Je aˇz s podivem, jak ˇcasto jej lze s u ´spˇechem pouˇz´ıt. Je d´ ana mnoˇzina V sloˇzen´ a z n vstupn´ıch hodnot. Naˇs´ım u ´kolem je naj´ıt podmnoˇzinu W mnoˇziny V, kter´ a vyhovuje urˇcit´ ym podm´ınk´ am a pˇritom optimalizuje (tj. minimalizuje nebo maximalizuje) pˇredepsanou u ´ˇcelovou funkci. Jakoukoli podmnoˇzinu W, vyhovuj´ıc´ı dan´ ym podm´ınk´ am, oznaˇc´ıme jako pˇr´ıpustn´e ˇreˇsen´ı. Pˇr´ıpustn´e ˇreˇsen´ı, pro kter´e nab´ yv´ au ´ˇcelov´ a funkce optim´ aln´ı hodnoty, oznaˇcujeme jako optim´ aln´ı ˇreˇsen´ı. Hladov´ y algoritmus se bude skl´ adat z krok˚ u, kter´e budou prob´ırat jednotliv´e prvky vstupn´ı mnoˇziny V, a v kaˇzd´em kroku rozhodne, zda se dan´ y prvek hod´ı do optim´ aln´ıho ˇreˇsen´ı. Prvky V bude prob´ırat v poˇrad´ı, urˇcen´em jistou v´ ybˇerovou procedurou. V kaˇzd´em kroku mus´ıme ovˇsem dostat pˇr´ıpustn´e ˇreˇsen´ı. Prvek, kter´ y by vedl k nepˇr´ıpustn´emu ˇreˇsen´ı, nevezmeme vu ´vahu. V´ ybˇerov´ a procedura, kter´ a urˇcuje poˇrad´ı, v jak´em budeme zpracov´ avat prvky V , bude zaloˇzena na nˇejak´e optimalizaˇcn´ı m´ıˇre - funkci, kter´ a m˚ uˇze b´ yt odvozena od u ´ˇcelov´e funkce. ˇ aˇr n´ Form´ alnˇe bychom mohli hladov´ y algoritmus vyj´ adˇrit n´ asleduj´ıc´ı procedurou HLAD. Cten´ am jistˇe promine, kdyˇz tentokr´ at poruˇs´ıme syntaktick´ a pravidla jazyka Pascal v´ıce neˇz obvykle - jde n´ am o symbolick´ y z´ apis, nikoli o program. var r ˇeˇ sen´ ı: set of data; {hledan´ a mnoˇ zina} function HLAD(A: set of data; n: integer): set of data; var i: integer; {n je poˇ cet prvk˚ u mnoˇ ziny A} x: data;
´ ALGORITMUS 3.2. HLADOVY
47
begin r ˇeˇ sen´ ı := [ ]; for i := 1 to n do begin x := ZVOL(A); if Pˇ R´ IPUSTN´ E(ˇ reˇ sen´ ı, x) then r ˇeˇ sen´ ı := r ˇeˇ sen´ ı + [x]; end; HLAD := r ˇeˇ sen´ ı; end;
{pr´ azdn´ a mnoˇ zina je} {pˇ rı ´pustn´ e r ˇeˇ sen´ ı} {urˇ cı ´me dalˇ sı ´ prvek} {lze x pˇ ridat k r ˇeˇ sen´ ı?} {kdyˇ z lze, tak ho pˇ rid´ ame}
ˇ´ ´ testuje, zda Funkce ZVOL vybere dalˇs´ı hodnotu a odstran´ı ji z mnoˇziny A. Booleovsk´ a funkce P R IPUSTNE pˇrid´ an´ım x vznikne pˇr´ıpustn´e ˇreˇsen´ı. Poznamenejme, ˇze typ vstupn´ıch u ´daj˚ u data zpravidla nem˚ uˇze slouˇzit jako b´ azov´ y typ mnoˇziny v Pascalu. Pˇ r´ıklad 3.2 Na magnetick´e p´ asce poˇc´ıtaˇce je tˇreba uloˇzit n soubor˚ u s d´elkami l 1 , . . . , ln . Magnetick´ a p´ aska umoˇzn ˇuje pouze sekvenˇcn´ı pˇr´ıstup k soubor˚ um, takˇze chceme-li ˇc´ıst r-t´ y soubor, mus´ıme nejprve pˇreˇc´ıst r − 1 soubor˚ u, zapsan´ ych pˇred n´ım. Doba ˇcten´ı souboru je pˇr´ımo u ´mˇern´ a d´elce souboru a pravdˇepodobnosti ˇcten´ı jsou u vˇsech soubor˚ u stejn´e. V jak´em poˇrad´ı m´ ame soubory uloˇzit, aby byla stˇredn´ı doba pˇr´ıstupu k soubor˚ um nejmenˇs´ı? Ze zad´ an´ı plyne, ˇze doba ˇcten´ı jednoho souboru je pˇr´ımo u ´mˇern´ a jeho d´elce. Doba t k , potˇrebn´ a k nalezen´ı a pˇreˇcten´ı k -t´eho souboru na p´ asce, je tedy u ´mˇern´ a souˇctu d´elek prvn´ıch k soubor˚ u. Jsou-li soubory uloˇzeny na p´ asce v poˇrad´ı dan´em permutac´ı I = (i1 , . . . , in ), plat´ı tk ∼ =
k X
li 1
j=1
a stˇredn´ı doba ˇcten´ı souboru bude u ´mˇern´ a veliˇcinˇe (I ), kterou zavedeme vztahem j n n 1X 1 XX t (I) ∼ tj = li1 = τ (I) = n j=1 n j=1
(3.1)
k=1
Naˇs´ım u ´kolem je naj´ıt takovou permutaci I = (i1 , . . . , in ), pro kterou bude stˇredn´ı doba ˇcten´ı souboru t (I) minim´ aln´ı. Tuto u ´lohu se pokus´ıme vyˇreˇsit pomoc´ı hladov´eho algoritmu. Vˇsechny permutace jsou pˇr´ıpustn´ a ˇreˇsen´ı. ´ celovou funkc´ı bude minim´ Uˇ aln´ı veliˇcina (I), zaveden´ a vztahem (3.1) (a tedy tak´e stˇredn´ı doba pˇr´ıstupu k souboru t (I)). Tato u ´ˇcelov´ a funkce z´ avis´ı na zvolen´e permutaci I. Pˇri konstrukci permutace I budeme postupovat tak, aby (I) bylo st´ ale co nejmenˇs´ı: jako dalˇs´ı v poˇrad´ı vezmeme vˇzdy soubor, pro kter´ y (I) vzroste nejm´enˇe. To znamen´ a, ˇze soubory uloˇz´ıme v poˇrad´ı podle rostouc´ı d´elky 1 . Uk´ aˇzeme, ˇze tak opravdu z´ısk´ ame optim´ aln´ı ˇreˇsen´ı. Abychom si zjednoduˇsili z´ apis, oˇc´ıslujeme soubory tak, aby platilo l1 < . . . < ln . Chceme dok´ azat, ˇze optim´ aln´ı permutace je pak (1, 2, . . . , n). Vezmeme libovolnou permutaci I = (i1 , . . . , in ). Z (3.1) plyne, ˇze n k n 1X 1 XX li 1 = (n − k + 1) li1 , τ (I) = n n j=1 k=1
k=1
nebot’ li1 je v tomto souˇctu n-kr´ at, li2 je tam (n − 1)-kr´ at apod. Jestliˇze existuj´ı indexy a < b takov´e, ˇze lia > lib , dostaneme z´ amˇenou ia a ib permutaci I 0 , pro kterou plat´ı n X 1 τ (I 0 ) = (n − k + 1) li1 + (n − b + 1) lia + (n − a + 1) lib , n k=1, k6=a, k6=b
takˇze pro rozd´ıl hodnot u ´ˇcelov´e funkce pro permutace I a I 0 dostaneme 1 V´ ybˇerov´ a procedura se tedy ˇr´ıd´ı poˇzadavkem, aby se pˇrid´ an´ım dalˇs´ıho souboru u ´ˇcelov´ a funkce zvˇetˇsila co nejm´enˇe. To je typick´e pouˇzit´ı hladov´eho algoritmu, od kter´eho tak´e poch´ az´ı jeho oznaˇcen´ı: jako hladovci s´ ahneme po tom prvku, kter´e poskytuje nejvˇetˇs´ı okamˇzit´ y prospˇech. (Poznamenejme, ˇze anglick´e oznaˇcen´ı the greedy method je jeˇstˇe o nˇeco m´enˇe uˇcesan´e; greedy znamen´ a ˇzrav´ y.)
´ ˚ KAPITOLA 3. METODY NAVRHU ALGORITMU
48
45 50
0 10
30 2
1
10
4
35 15 15
20 3
30 3
5
Obr. 3.1: Ohodnocen´ y graf n [τ (I) − τ (I 0 )] = (n − a + 1) (lia − lib ) + (n − b + 1) (lib − lib ) = (b − a) (lia − lib ) > 0. To ale znamen´ a, ˇze z´ amˇenou soubor˚ u ia a ib se u ´ˇcelov´ a funkce zmenˇsila, a tedy se tak´e zmenˇsila stˇredn´ı doba pˇr´ıstupu k soubor˚ um. Proto nem˚ uˇze b´ yt optim´ aln´ı ˇza ´dn´ a permutace, pˇri kter´e nejsou soubory uspoˇra ´d´ any podle velikosti v neklesaj´ıc´ım poˇrad´ı. Pˇ r´ıklad 3.3 (probl´ em nejkratˇ s´ı cesty) V ohodnocen´em orientovan´em grafu (viz napˇr. obr. 3.1) je d´ an v´ ychoz´ı“ uzel v0 . Hled´ ame nejkratˇs´ı cesty do ” vˇsech ostatn´ıch uzl˚ u grafu, tj. takov´e cesty, kter´e budou m´ıt nejniˇzˇs´ı souˇcet ohodnocen´ı hran 2 . Ze zad´ an´ı je jasn´e, ˇze u ´ˇcelovou funkc´ı bude souˇcet ohodnocen´ı hran cesty. N´ aˇs postup bude zaloˇzen na n´ asleduj´ıc´ı myˇslence: Nejprve najdeme uzel v 1 , kter´ y je nejbliˇzˇs´ı v´ ychoz´ımu uzlu v0 (tj. vede do nˇej hrana, kter´ a m´ a nejniˇzˇs´ı ohodnocen´ı ze vˇsech hran, kter´e vych´ azej´ı z v 0 ). Je zˇrejm´e, ˇze jsme t´ım naˇsli nejkratˇs´ı cestu do uzlu v1 . D´ ale projdeme vˇsechny uzly, do kter´ ych vede hrana z v0 nebo z v1 , a najdeme mezi nimi ten, do kter´eho vede nejkratˇs´ı cesta z v0 . Oznaˇc´ıme jej v2 . Pˇritom nejkratˇs´ı cesta z v0 do v2 m˚ uˇze b´ yt bud’ hrana v0 → v2 nebo cesta v0 → v1 → v2 . D´ ale projdeme vˇsechny uzly, do kter´ ych vede hrana z v0 , z v1 nebo z v2 atd. Takto postupnˇe najdeme nejkratˇs´ı cesty do vˇsech uzl˚ u grafu - nejprve cestu do uzlu, nejbliˇzˇs´ıho k v´ ychoz´ımu, pak do druh´eho nejbliˇzˇs´ıho atd. Nyn´ı tento postup vyj´ adˇr´ıme form´ alnˇeji a uk´ aˇzeme, ˇze je spr´ avn´ y. Definujeme S jako mnoˇzinu uzl˚ u, do kter´ ych jiˇz byly nalezeny nejkratˇs´ı cesty z v 0 . (Mnoˇzina S bude samozˇrejmˇe na poˇca ´tku obsahovat jen uzel v0 .) D´ ale oznaˇc´ıme Dist (w) d´elku nejkratˇs´ı cesty z uzlu v0 do w, proch´ azej´ıc´ı pouze uzly z S a konˇc´ıc´ı ve w. Pokud takov´ a cesta neexistuje, tj. pokud ze ˇza ´dn´eho z uzl˚ u mnoˇziny S nevede hrana do w, poloˇz´ıme Dist (w) = +∞. Pˇritom si povˇsimneme n´ asleduj´ıc´ıch skuteˇcnost´ı: (1)
Jestliˇze jsme pˇri dalˇs´ım kroku naˇsli nejkratˇs´ı cestu do uzlu u, jde o cestu z uzlu v 0 do u proch´ azej´ıc´ı pouze uzly z mnoˇziny S. Kdyby totiˇz na nejkratˇs´ı cestˇe v0 → u leˇzel uzel w ∈ / S, znamenalo by to, ˇze se cesta v0 → u skl´ ad´ a z cest v0 → w a w → u. Cesta v0 → w je ale nutnˇe kratˇs´ı neˇz v0 → u, takˇze podle naˇseho postupu bychom ji museli vz´ıt pˇred v0 → u.
(2)
Z postˇrehu (1) plyne, ˇze koncov´ ym uzlem n´ asleduj´ıc´ı generovan´e nejkratˇs´ı cesty mus´ı b´ yt uzel u, kter´ y m´ a nejmenˇs´ı vzd´ alenost Dist (u) ze vˇsech uzl˚ u, kter´e neleˇz´ı v S. Pokud m´ a v´ıce uzl˚ u stejnou Dist (u), m˚ uˇzeme zvolit kter´ ykoli z nich. Jin´ ymi slovy: cesta z v0 do novˇe pˇridan´eho uzlu u se skl´ ad´ a z nejkratˇs´ı cesty z v0 do nˇekter´eho z uzl˚ u mnoˇziny S a z hrany, kter´ a vede z tohoto uzlu do u.
(3)
Jakmile zvol´ıme u podle (2) a vygenerujeme nejkratˇs´ı cestu do tohoto uzlu, stane se u ∈ S. Pˇritom se pro uzly w ∈ / S m˚ uˇze zmˇenit Dist (w). (Vˇsimnˇete si, ˇze jakmile bude na obr´ azku 3.1 uzel
2 Jestliˇ ze ohodnocen´ı hrany hi, ji interpretujeme jako d´elku cesty z uzlu i do j, pak hled´ ame nejkratˇs´ı cestu. Ohodnocen´ı hrany ovˇsem m˚ uˇze tak´e znamenat napˇr. cenu pˇrechodu z i do j. Proto se tak´e obˇcas hovoˇr´ı o probl´emu nejlacinˇ ejˇs´ı cesty (a m˚ uˇzeme se setkat i s jin´ ymi n´ azvy).
´ PROGRAMOVAN ´ ´I 3.3. DYNAMICKE
49
ˇc´ıslo 3 ∈ S, zmenˇs´ı se Dist (1). V takov´em pˇr´ıpadˇe bude Dist (w) = Dist (u) + C (u, v), kde C (u, v) je ohodnocen´ı hrany u → v. Z toho, ˇze se zmˇenila Dist (w), totiˇz plyne, ˇze existuje hrana hu, vi = u → v. Na z´ akladˇe tˇechto u ´vah jiˇz m˚ uˇzeme formulovat algoritmus pro vyhled´ an´ı nejkratˇs´ıch cest ze zadan´eho poˇca ´teˇcn´ıho uzlu do vˇsech ostatn´ıch uzl˚ u grafu. Zap´ıˇseme jej v Pseudopascalu, nebot’ podrobn´ a formulace by vˇec sp´ıˇse zatemnila. type uzel = 1..n; {n je poˇ cet uzl˚ u v grafu} var Dist: array [uzel] of real; {pˇ ri
∈U / bude} Cena: array [uzel, uzel] of real; {Cena[i,j] =∞ } procedure NC(v,n: uzel); {v´ ychoz´ ı uzel a poˇ cet uzl˚ u} var S: set of uzel; u, num, i, w: uzel; begin for i := 1 to n do Dist[i] = Cena[v, i]; {inicializace} S := [v]; {na poˇ ca ´tku obsahuje S pouze v} Dist[v] := 0; for num := 2 to n do begin {*1* n-1 cest z uzlu v} zvol u tak, aby Dist [u] = min {Dist [w]}, minimum pˇres vˇsechna w ∈ / S“; ” S := S + u;
{pˇ ridej v do S}
uloˇz nalezenou nejkratˇs´ı cestu z uzlu v do uzlu u“; ” for (vˇ sechna w, not(w S)) do Dist[w] := min(Dist[w], Dist[u]+Cena[u,v]); end; end;
{aktualizace vzd´ alenost´ ı}
Vˇsimnˇeme si jeˇstˇe ˇcasov´e n´ aroˇcnosti tohoto algoritmu. Cyklus for v ˇra ´dce, oznaˇcen´e *1*, se prov´ ad´ı n-1kr´ at. Pˇri hled´ an´ı minim´ aln´ıho u a pˇri aktualizaci vzd´ alenost´ı mus´ıme v cyklu proj´ıt vˇsechny uzly, kter´e nejsou v S. Vnoˇren´e cykly tedy budou obsahovat n − 1, n − 2, . . . , 1 iterac´ı. Protoˇze n − 1 + n − 2 + · · · + 1 = (n − 1) n/2, bude ˇcasov´ a n´ aroˇcnost popsan´eho algoritmu O n2 .
3.3
Dynamick´ e programov´ an´ı3
Dynamick´e programov´ an´ı pˇredstavuje zp˚ usob n´ avrhu algoritmu pro ˇreˇsen´ı optimalizaˇcn´ıho probl´emu, kter´ y lze uplatnit v pˇr´ıpadˇe, ˇze na ˇreˇsen´ı dan´eho probl´emu m˚ uˇzeme nahl´ıˇzet jako na posloupnost rozhodnut´ı. Pˇri ˇreˇsen´ı probl´em˚ u metodou dynamick´eho programov´ an´ı se op´ır´ ame o princip optimality. Ten ˇr´ık´ a, ˇze optim´ aln´ı posloupnost rozhodnut´ı m´ a tu vlastnost, ˇze at’ je poˇca ´teˇcn´ı stav a rozhodnut´ı jak´ekoli, mus´ı b´yt vˇsechna n´ asleduj´ıc´ı rozhodnut´ı optim´ aln´ı vzhledem k v´ysledk˚ um rozhodnut´ı prvn´ıho. Na rozd´ıl od hladov´eho algoritmu, kde generujeme vˇzdy jen jedinou posloupnost rozhodnut´ı, m˚ uˇze pouˇzit´ı dynamick´eho programov´ an´ı v´est ke generov´ an´ı v´ıce posloupnost´ı. Zkoum´ ame vˇsechny posloupnosti, kter´e by mohly b´ yt optim´ aln´ı, a snaˇz´ıme se vylouˇcit ty, o kter´ ych je jasn´e, ˇze optim´ aln´ı nebudou. Nejjednoduˇsˇs´ı, ale nejm´enˇe u ´ˇcinnou metodou pro ˇreˇsen´ı probl´em˚ u, na kter´e se m˚ uˇzeme d´ıvat jako na posloupnost rozhodnut´ı, je metoda hrub´e s´ıly: Vygenerujeme vˇsechny moˇzn´e posloupnosti a mezi nimi vyhled´ ame posloupnost optim´ aln´ı. Pouˇzit´ım dynamick´eho programov´ an´ı m˚ uˇzeme v´ yznamnˇe zredukovat poˇcet zkouman´ ych posloupnost´ı. 3 Term´ ın ”programov´ an´ı” se pouˇz´ıv´ a mj. jako oznaˇcen´ı nˇekter´ ych optimalizaˇcn´ıch u ´loh. M˚ uˇzeme se setkat napˇr. s line´ arn´ım programov´ an´ım, celoˇc´ıseln´ ym programov´ an´ım apod.
´ ˚ KAPITOLA 3. METODY NAVRHU ALGORITMU
50
2 10 1
7 2
V1
20 5
11 3 4 V2
1 7
13 19
6
9
V3
V4
´ Obr. 3.2: Urovˇ nov´ y graf se 4 u ´rovnˇemi Pˇ r´ıklad 3.4 (pouˇ zitelnost dynamick´ eho programov´ an´ı na probl´ em nejkratˇ s´ı cesty) ´ Vezmˇeme ohodnocen´ y orientovan´ y graf G = (V, U ). Ukolem je naj´ıt nejkratˇs´ı cestu z uzlu i do uzlu j. Uk´ aˇzeme si, ˇze tento probl´em bychom mohli zkusit ˇreˇsit i metodou dynamick´eho programov´ an´ı. ˇ sen´ı tohoto probl´emu pˇredstavuje posloupnost rozhodnut´ı: Je-li i prvn´ım uzlem cesty, je tˇreba urˇcit, kter´ Reˇ y uzel bude druh´ y, kter´ y bude tˇret´ı atd. Uk´ aˇzeme, ˇze v t´eto u ´loze plat´ı princip optimality: Je-li i, i1 , . . . , is , is+1 , . . . , j nejkratˇs´ı cesta z uzlu i do uzlu j, mus´ı b´ yt i, i1 , . . . , is nejkratˇs´ı cesta z uzlu i do uzlu is a is , is+1 , . . . , j nejkratˇs´ı cesta z uzlu is do uzlu j. Pokud by totiˇz bylo moˇzn´e naj´ıt kratˇs´ı cestu napˇr. z uzlu i do is , ˇreknˇeme i, l1 , . . . , is pˇredstavovala by posloupnost i, l1 , . . . , is , is+1 , . . . , j kratˇs´ı cestu z i do j neˇz je i, i1 , . . . , is , is+1 , . . . , j. Pˇri hled´ an´ı nejkratˇs´ı cesty z i do j budeme postupovat takto: Oznaˇc´ıme si P j mnoˇzinu vˇsech uzl˚ u sdruˇzen´ ych s koncov´ ym uzlem j (Pj je tedy mnoˇzina vˇsech uzl˚ u, ze kter´ ych vede orientovan´ a hrana do j, tj. k ∈ P j pr´ avˇe kdyˇz hk, ji ∈ E). Pro kaˇzd´ y uzel k ∈ Pj bude Γk nejkratˇs´ı cesta z i do k (tedy z v´ ychoz´ıho uzlu do pˇredposledn´ıho, sdruˇzen´eho s posledn´ım). Podle principu optimality bude nejkratˇs´ı cesta z i do j nejkratˇs´ı z cest tvaru {Γk , j |k ∈ Pj }. Stoj´ıme tedy pˇred u ´lohou naj´ıt nejkratˇs´ı cesty do vˇsech uzl˚ u z mnoˇziny P j . Princip optimality zpravidla aplikujeme rekurzivnˇe, takˇze dynamick´e programov´ an´ı n´ as pˇrivede k rekurentn´ım vztah˚ um pro veliˇciny, kter´e popisuj´ı optim´ aln´ı ˇreˇsen´ı. Pˇri ˇreˇsen´ı u ´loh dynamick´eho programov´ an´ı rozliˇsujeme dopˇredn´y a zpˇetn´y pˇr´ıstup. Necht’ x 1 , x2 , . . . , xn jsou promˇenn´e, o jejichˇz hodnot´ ach budeme pˇri ˇreˇsen´ı rozhodovat. Pˇri dopˇredn´em pˇr´ıstupu rozhodujeme o hodnotˇe xl na z´ akladˇe optim´ aln´ıch rozhodnut´ı pro x1+1 , . . . , xn , zat´ımco pˇri zpˇetn´em pˇr´ıstupu rozhodujeme o hodnotˇe xl na z´ akladˇe posloupnosti optim´ aln´ıch rozhodnut´ı pro x1 , . . . , xl−1 . Pˇ r´ıklad 3.5: Probl´ em nejkratˇ s´ı cesty v u ´rovˇ nov´ em grafu ´ nov´y graf s k u Urovˇ ´rovnˇemi je orientovan´ y graf, jehoˇz mnoˇzina uzl˚ u U je rozdˇelena do disjunktn´ıch podmnoˇzin ´ (´ urovn´ı) V1 , . . . , Vk . Urovnˇ e V1 ,a Vk obsahuj´ı pouze 1 uzel, ostatn´ı jich mohou obsahovat v´ıce. Uzel s, kter´ y leˇz´ı na u ´rovni V1 , oznaˇcujeme jako poˇca ´teˇcn´ı, uzel t, kter´ y leˇz´ı na u ´rovni V k , jako koncov´y. Hrana, kter´ a vych´ az´ı z uzlu na u ´rovni i, mus´ı konˇcit v uzlu na u ´rovni i + 1. To znamen´ a, ˇze kaˇzd´ a cesta z poˇca ´teˇcn´ıho do koncov´eho uzlu (s → t) mus´ı proj´ıt postupnˇe vˇsemi u ´rovnˇemi grafu. ´ Urovˇ nov´e grafy mohou popisovat napˇr. varianty technologick´eho procesu a jejich ceny. Pˇr´ıklad jednoduch´eho u ´rovˇ nov´eho grafu vid´ıme na obr. 3.2. Naˇs´ım u ´kolem bude sestavit algoritmus pro nalezen´ı nejkratˇs´ı cesty z s do t. Na rozd´ıl od obecn´eho grafu zde v´ıme, ˇze nejkratˇs´ı cesta se bude skl´ adat z k − 1 hran, pˇriˇcemˇz kaˇzd´ a hrana bude spojovat uzly na dvou r˚ uzn´ ych u ´rovn´ıch. Pokus´ıme se toho vyuˇz´ıt pˇri ˇreˇsen´ı naˇs´ı u ´lohy.
´ PROGRAMOVAN ´ ´I 3.3. DYNAMICKE
51
Pˇritom budeme pouˇz´ıvat n´ asleduj´ıc´ı oznaˇcen´ı: P (i, j) bude nejkratˇs´ı cesta z uzlu j na u ´rovni V i , Cena (i, j) bude jej´ı cena a c (j, l) je ohodnocen´ı hrany hj, li. O uzlech grafu pˇredpokl´ ad´ ame, ˇze jsou oˇc´ıslov´ any tak, ˇze uzly na u ´rovni Vi maj´ı niˇzˇs´ı poˇradov´ a ˇc´ısla neˇz uzly na u ´rovni Vi+1 . Kaˇzd´ a cesta z s do t v k -´ urovˇ nov´em grafu je v´ ysledkem k−2 rozhodnut´ı; i-t´e rozhodnut´ı spoˇc´ıv´ a v urˇcen´ı, kter´ y uzel z u ´rovnˇe Vi+1 bude souˇca ´st´ı cesty. Naˇse u ´loha je zvl´ aˇstn´ım pˇr´ıpadem probl´emu nejkratˇs´ı cesty, o kter´em jsme hovoˇrili v pˇr´ıkladu 3.4; proto i zde plat´ı princip optimality. Z nˇej pro Cena (i, j) dostaneme Cena (i, j) =
min
leVi+1 ,eU
{c (i, 1) + Cena (i + 1, 1)} ,
(3.2)
tj. cenu nejkratˇs´ı cesty z uzlu j na u ´rovni Vi do t najdeme jako minimum souˇctu ceny cesty z uzlu l na u ´rovni Vi+1 a ohodnocen´ı hrany hi, li. Toto minimum poˇc´ıt´ ame pˇres vˇsechny uzly l na u ´rovni V i+1 , sdruˇzen´e s j. Pro u ´roveˇ n k − 1 plat´ı Cena (k − 1, j) = c (j, t), pokud hj, ti ∈ U ; jinak poloˇz´ıme Cena (k − 1, j) = +∞. Naˇsi u ´lohu m˚ uˇzeme ˇreˇsit tak, ˇze nejprve vypoˇc´ıt´ ame na z´ akladˇe vztahu (3.2) Cena (k − 2, j) pro vˇsechny uzly j Vk−2 , pak Cena (k − 3, j) pro vˇsechny uzly j ∈ Vk−2 atd. Nakonec dospˇejeme k Cena (1, s). Pod´ıvejme se, jak bychom pomoc´ı tohoto postupu hledali nejkratˇs´ı cestu v grafu na obr. 3.2. Ceny cest ze tˇret´ı u ´rovnˇe (uzly 5 a 6) do c´ılov´eho uzlu 7 jsou Cena (3, 5) = 1, Cena (3, 6) = 9. Ceny nejkratˇs´ıch cest ze druh´e u ´rovnˇe (uzly 2, 3 a 4) jsou Cena (2, 2) = 21 (zde existuje pouze jedin´ a cesta), Cena (2, 3) = min {c (3, 1) + Cena (3, 1)} = min {c (3, 5) + Cena (3, 5) ; c (3, 6) + Cena (3, 6)} = 12, le{5,6}
Cena (2, 4) = 28 (zde existuje opˇet pouze jedin´ a cesta). Pro nejkratˇs´ı cestu z uzlu 1 do 7 nakonec dostaneme Cena (1, j) = min {c (1, 1) + Cena (2, 1)} = min {c (1, 2) + Cena (2, 2) ; le V2
; c (1, 3) + Cena (2, 3) ; c (1, 4) + Cena (2, 4)} = 19 Tato cesta je na obr. 3.2 vyznaˇcena silnˇejˇs´ımi ˇcarami. Na z´ avˇer vyj´ adˇr´ıme algoritmus pro hled´ an´ı nejkratˇs´ı cesty v u ´rovˇ nov´em grafu ve tvaru procedury, kterou opˇet zap´ıˇseme v Pseudopascalu. V proceduˇre N C budeme do pole D ukl´ adat hodnotu l, kter´ a minimalizuje v´ yraz c (j, l) + Cena (i + 1, l) v rovnici (2). To znamen´ a, ˇze D[i,j ] bude obsahovat index l uzlu na u ´rovni i + 1, pˇres kter´ y vede nejkratˇs´ı cesta z uzlu j na i-t´e u ´rovni. Vzhledem ke zp˚ usobu, jak´ ym jsou uzly oˇc´ıslov´ any, m˚ uˇzeme vynechat indexy u ´rovn´ı. Vstupn´ı parametry procedury N C budou: mnoˇzina hran grafu, poˇcet n uzl˚ u grafu a poˇcet k u ´rovn´ı grafu. V´ ystupn´ım parametrem bude pole, obsahuj´ıc´ı indexy uzl˚ u, kter´e tvoˇr´ı nejkratˇs´ı cestu. procedure Nejkratˇ sı ´Cesta(U: set of hrana; k,n: integer; var P: array [1..k] of integer); var CENA: array [1..n] of real; D: array [1..n] of integer; r,j: integer; begin for j:=n-1 downto 1 do begin {v´ ypoˇ cet CENA[j]} najdi uzel r takov´ y, ˇze hj, ri ∈ U a z´ aroveˇ n c (j, r) + CEN A [r] je minim´ aln´ı“; ” CENA[j] := c(j,r) + CENA[r]; {cena cesty z uzlu j} D[j] := r; {optim´ aln´ ı cesta vede pˇ res r} end; {nalezen´ ı nejkratˇ sı ´ cesty} P[1] := 1; P[k]= n; {prvn´ ı a posledn´ ı uzel je jasn´ y} for j := 2 to k-1 do P[j] := D[P[j-1]] {v D byl v´ ysledek posunut´ y o 1 index} end;
´ ˚ KAPITOLA 3. METODY NAVRHU ALGORITMU
52
Pokusme se jeˇstˇe odhadnout sloˇzitost tohoto algoritmu. V u ´rovˇ nov´em grafu s k u ´rovnˇemi je celkem n uzl˚ u. Na prvn´ı u ´rovni je 1 uzel, ne druh´e u ´rovni je n1 uzl˚ u, ... na u ´rovni k − 1 je nk−1 uzl˚ u, na k -t´e -´ urovni je 1 uzel. Budeme pˇredpokl´ adat, ˇze z kaˇzd´eho uzlu na u ´rovni i vede cesta do kaˇzd´eho z uzl˚ u na u ´rovni i + 1. V uveden´em algoritmu postupnˇe hled´ ame nejkratˇs´ı cestu do t z uzl˚ uu ´rovn´ı k − 1, k − 2 atd. Nalezen´ı nejkratˇs´ı cesty z u ´rovnˇe k − 1 znamen´ a proj´ıt vˇsech nk−1 uzl˚ u t´eto u ´rovnˇe, tedy nk−1 krok˚ u. Nalezen´ı nejkratˇs´ı cesty z u ´rovnˇe k − 2 znamen´ a proj´ıt vˇsech nk−2 a pro kaˇzd´ y z nich zkoumat, pˇres kter´ y z nk−1 uzl˚ uu ´rovnˇe k − 1 vede nejkratˇs´ı cesta do t, to znamen´ a nk−2 .nk−1 operac´ı atd. Nalezen´ı nejkratˇs´ı cesty z u ´rovnˇe 1 znamen´ a proj´ıt vˇsech n2 uzl˚ uu ´rovnˇe 2 a zjistit, pˇres kter´ y z nich vede hledan´ı nejkratˇs´ı cesta. Celkem tedy dostaneme p = n2 + nk−1 +
k−1 X
ni ni−1
i=2
operac´ı. Pokud budou napˇr. na vˇsech u ´rovn´ıch stejn´e poˇcty uzl˚ u, bude pro i = 2, . . . , k − 1 poˇcet uzl˚ u na jednotliv´ ych u ´rovn´ıch roven ni = (n − 2) / (k − 2). V takov´em pˇr´ıpadˇe bude 2 n−2 n − 2 (n − 2)2 n−2 p=2 =2 + (k − 2) + k−2 k−2 k−2 k−2 Poˇcet operac´ı vyjde tedy O n2 .
3.4
Metoda hled´ an´ı s n´ avratem (backtracking)
Metoda hled´ an´ı s n´ avratem je jednou z metod, zaloˇzen´ ych na prohled´ av´ an´ı tzv. stavov´eho stromu probl´emu. Dalˇs´ı oznaˇcen´ı, pod kter´ ymi m˚ uˇzeme tuto metodu naj´ıt, jsou napˇr. metoda pokus˚ u a oprav, metoda zpˇetn´eho sledov´ an´ı, metoda prohled´ av´ an´ı do hloubky.
3.4.1
´ Uvod
Metodu hled´ an´ı s n´ avratem m˚ uˇzeme pouˇz´ıt v pˇr´ıpadˇe, ˇze ˇreˇsen´ım probl´emu je vektor (n-tice) (x 1 , . . . , xn ), jehoˇz jednotliv´e sloˇzky vyb´ır´ ame z mnoˇzin Si , xi ∈Si . Zpravidla potˇrebujeme naj´ıt n-tici, kter´ a minimalizuje nebo maximalizuje nˇejakou u ´ˇcelovou funkci P (x1 , . . . , xn ). M˚ uˇzeme ale tak´e hledat vˇsechny n-tice, kter´e splˇ nuj´ı podm´ınku P (x1 , . . . , xn ). Q Oznaˇcme symbolem mi = |Si | mohutnost mnoˇziny Si . Potom z nich lze sestavit celkem M = ni=1 mi n-tic. Nejjednoduˇsˇs´ı metoda ˇreˇsen´ı takov´ehoto probl´emu, zaloˇzen´ a na hrub´e s´ıle“, spoˇc´ıv´ a v tom, ˇze projdeme vˇsech ” M moˇznost´ı a z nich vybereme spr´ avn´e ˇreˇsen´ı. Rozhodneme-li se pouˇz´ıt metodu hled´ an´ı s n´ avratem, vytv´ aˇr´ıme n-tice jednu sloˇzku po druh´e. Pˇritom pouˇz´ıv´ ame u ´ˇcelovou funkci (pˇr´ıpadnˇe vhodnou pomocnou funkci, odvozenou od u ´ˇcelov´e funkce) a pro kaˇzdou novˇe vytvoˇrenou sloˇzku testujeme, zda by takov´ a n-tice v˚ ubec mohla b´ yt optim´ aln´ı nebo splˇ novat dan´e podm´ınky. Jestliˇze pro nˇejak´e xi zjist´ıme, ˇze ˇza ´dn´ y vektor, zaˇc´ınaj´ıc´ı (x1 , . . . , xi ), nem˚ uˇze b´ yt optim´ aln´ı (resp. nem˚ uˇze splˇ novat dan´e podm´ınky), nebudeme uˇz ˇza ´dn´ y takov´ y vektor testovat a vezmeme dalˇs´ı moˇznou hodnotu i-t´e sloˇzky. T´ım vylouˇc´ıme z testov´ an´ı dalˇs´ıch mi+1 × mi+2 × · · · × mn vektor˚ u. Jestliˇze jsme vyˇcerpali vˇsechny moˇzn´e hodnoty i-t´e sloˇzky, vr´ at´ıme se o jednu u ´roveˇ n zpˇet a budeme zkouˇset dalˇs´ı moˇznou hodnotu x i−1 . (To je onen n´ avrat“, o kter´em se hovoˇr´ı v n´ azvu metody.) ” Jako ˇreˇsen´ı oznaˇc´ıme n-tici, kter´ a vyhovuje dan´ ym omezen´ım (pˇr´ıp. optimalizuje u ´ˇcelovou funkci). Poznamenejme, ˇze poˇcet sloˇzek vektoru ˇreˇsen´ı nemus´ı b´ yt pro vˇsechna ˇreˇsen´ı stejn´ y; to znamen´ a, ˇze jedno ˇreˇsen´ı m˚ uˇze obsahovat n1 sloˇzek, jin´e n2 sloˇzek. Pˇ r´ıklad 3.6 (probl´ em osmi dam) Probl´em 8 dam je klasick´ a kombinatorick´ au ´loha, kterou formuloval jiˇz kolem r. 1850 nˇemeck´ y matematik K. ´ F. Gauss. Ukolem je postavit na ˇsachovnici 8 dam tak, aby ˇza ´dn´ a z nich podle ˇsachov´ ych pravidel neohroˇzovala jinou (pˇritom na rozd´ıl od norm´ aln´ıho“ ˇsachu pˇredpokl´ ad´ ame, ˇze vˇsechny d´ amy jsou rovnocenn´e a vˇsechny ”
´ ´I S NAVRATEM ´ 3.4. METODA HLEDAN (BACKTRACKING)
53
Obr. 3.3: jedno z moˇzn´ ych ˇreˇsen´ı probl´emu 8 dam jsou navz´ ajem nepˇra ´telsk´e, tedy kaˇzd´ a z nich m˚ uˇze ohroˇzovat kteroukoli ze zb´ yvaj´ıc´ıch). Jedno z 92 moˇzn´ ych ˇreˇsen´ı vid´ıte na obr. 3.3. D´ ale se setk´ ame s obecnˇejˇs´ım probl´emem n dam, ve kter´em je d´ ana ˇsachovnice s n × n poli, na kterou je tˇreba um´ıstit n dam, aby se navz´ ajem neohroˇzovaly. Pro n = 1 tato u ´loha nem´ a v´ yznam; snadno uk´ aˇzeme, ˇze pro n = 2, 3 tato u ´loha nem´ a ˇreˇsen´ı. Nejmenˇs´ı hodnota, pro kterou se s n´ı budeme zab´ yvat, bude n = 4. Nyn´ı se vˇsak vr´ at´ıme k p˚ uvodn´ımu zad´ an´ı pro n = 8. Obecnˇe lze 8 dam um´ıstit na ˇsachovnici 64 usoby. Je ale jasn´e, ˇze kaˇzd´ a z dam mus´ı b´ yt v 8 = 4426165368 zp˚ jin´em sloupci ˇsachovnice. To znamen´ a, ˇze ˇreˇsen´ı naˇs´ı u ´lohy m˚ uˇzeme vyj´ adˇrit jako osmici (x 1 , . . . , x8 ), kde xi budou ˇc´ısla ˇra ´dk˚ u. Napˇr. ˇreˇsen´ı, kter´e vid´ıme na obr. 3.3, lze vyj´ adˇrit vektorem (4,6,8,2,7,1,3,5). Odtud plyne podm´ınka: Si = ˆ 8 = {1, . . . , 8} pro kaˇzd´e i = ˆ 8 . To omezuje mnoˇzinu moˇzn´ ych osmic na pouh´ ych 16777216. ˇ adn´e dvˇe d´ Z pravidel ˇsachu plynou dalˇs´ı omezen´ı: Z´ amy nesmˇej´ı leˇzet ve stejn´e ˇra ´dce nebo na stejn´e diagon´ ale. Prvn´ı z tˇechto podm´ınek zmenˇsuje prostor moˇzn´ ych osmic na 8! = 40320. Pod´ıvejme se na postup ˇreˇsen´ı. Zaˇcneme t´ım, ˇze um´ıst´ıme prvn´ı d´ amu do prvn´ıho ˇra ´dku; vektor ˇreˇsen´ı tedy bude zaˇc´ınat (1, . . .). Z podm´ınky, ˇze ˇza ´dn´e dvˇe d´ amy nesmˇej´ı leˇzet na t´eˇze ˇra ´dce nebo na t´eˇze diagon´ ale, dost´ av´ ame, ˇze nem´ a smysl d´ ale zkoumat ˇza ´dnou n-tici, kter´ a by zaˇc´ınala (1, 1, . . .) nebo (1, 2, . . .). Zkus´ıme tedy (1, 3, . . .) a pokus´ıme se um´ıstit dalˇs´ı d´ amu do tˇret´ıho sloupce. Podobnˇe uvaˇzujeme i pˇri obsazov´ an´ı ostatn´ıch pozic vektoru ˇreˇsen´ı. Postup pˇri pouˇzit´ı metody hled´ an´ı s n´ avratem lze snadno zn´ azornit pomoc´ı stavov´eho stromu probl´emu. Koˇren stromu odpov´ıd´ a poˇca ´teˇcn´ımu stavu; kaˇzd´ a z hran, kter´e vych´ azej´ı z koˇrene, odpov´ıd´ a jedn´e moˇzn´e volbˇe prvn´ı sloˇzky ˇreˇsen´ı x1 . Obecnˇe vrcholy stromu na i-t´e u ´rovni odpov´ıdaj´ı stav˚ um probl´emu ve chv´ıli, kdy zn´ ame (x1 , . . . , xi−1 ), a hrany vych´ azej´ıc´ı z vrcholu na i-t´e u ´rovni odpov´ıdaj´ı moˇzn´ ym hodnot´ am x i . Vˇsechny cesty od koˇrene do ostatn´ıch vrchol˚ u popisuj´ı stavov´y prostor probl´emu. Metodu hled´ an´ı s n´ avratem pak m˚ uˇzeme popsat jako postupn´e proch´ azen´ı stavov´eho stromu probl´emu a jeho postupn´e proˇrez´ av´ an´ı“: Jakmile o nˇejak´e vˇetvi zjist´ıme, ˇze nem˚ uˇze v´est k optim´ aln´ımu ˇreˇsen´ı, pˇrestaneme ji ” proch´ azet. Na obr´ azku 3.4 vid´ıme stavov´ y strom probl´emu 4 dam, ve kter´em bereme v u ´vahu podm´ınku, ˇze ˇreˇsen´ı mus´ı b´ yt permutac´ı mnoˇziny {1, 2, 3, 4}.
´ ˚ KAPITOLA 3. METODY NAVRHU ALGORITMU
54 1
1 2 3 8
2 3 2 9
4
1
18 3
4
3 4
1
34 2
50 2
1
4
3
19
13 4 2 11 14
2
35 61 40 24 29 51 45 56 4 1 4 1 3 2 2 2 3 1 3 1 4 1 4 1 2 22 25 27 30 32 36 38 41 43 46 48 52 54 57 59 62 64
3 4
4 6
4 5
3 4 2 3 24 3 4 1 3 14 7 10 12 15 17 21 23 26 28 31 33 37
3 3 16 20
13 2 4 1 2 39 42 44 47 49 53
2 3 1 2 1 55 58 60 63 65
´ Obr. 3.4: Upln´ y stavov´ y strom probl´emu 4 dam 1
1 2 3 4
2 3 2 5
1
4 7 3 4 2 6 8 10 3 9
2
12
11 3 13
4
3 4
1
19 14 4 1 3 2 15 16 20 21 3 17
2 22
18 2 23
4
1 24
25 2 30
26 3 2 3 1 27 28 31 32
3 33
2 29
Obr. 3.5: Stavov´ y strom probl´emu 4 dam, generovan´ y pˇri hled´ an´ı s n´ avratem ˇ arkovanˇe je oznaˇcen jeden z podstrom˚ C´ u. Vrcholy jsou oˇc´ıslov´ any tak, jak by se proch´ azely pˇri u ´pln´em prohled´ av´ an´ı. Hrany jsou oznaˇceny hodnotami, kter´e bychom v odpov´ıdaj´ıc´ıch kroc´ıch volili. Metoda hled´ an´ı s n´ avratem umoˇzn ˇuje vyhnout se prohled´ av´ an´ı nˇekter´ ych podstrom˚ u a tak zrychlit ˇreˇsen´ı probl´emu. Na obr. 3.5 vid´ıte t´ yˇz strom, prostˇr´ıhan´ y“ metodou hled´ an´ı s n´ avratem. Obsahuje vˇsechny generovan´e stavy; stavy, ” kter´e pˇredstavuj´ı ˇreˇsen´ı, jsou vyznaˇceny tuˇcnˇe. Poznamenejme, ˇze v pro nˇekter´e probl´emy nemus´ı b´ yt stavov´ y strom symetrick´ y. Z toho plyne, ˇze ˇreˇsen´ı jsou ty stavy S, pro kter´e cesta od koˇrene k S definuje n-tici vyhovuj´ıc´ı vˇsem omezen´ım, pˇr´ıpadnˇe optimalizuj´ıc´ı u ´ˇcelovou funkci (na obr´ azku 3.4 jsou to pouze listy). Strom stavov´eho prostoru (stavov´ y strom) probl´emu se skl´ ad´ a z podstrom˚ u; tomu odpov´ıd´ a skuteˇcnost, ˇze stavov´ y prostor probl´emu lze rozdˇelit na podprostory. Pˇritom kaˇzd´emu prvku prostoru ˇreˇsen´ı odpov´ıd´ a nˇejak´ y vrchol ve stavov´em stromˇe.
3.4.2
Podrobnˇ ejˇ s´ı formulace pro zvl´ aˇ stn´ı pˇ r´ıpad
V tomto odstavci sestav´ıme obecnou formulaci metody hled´ an´ı s n´ avratem pro pˇr´ıpad, kdy hled´ ame vˇsechny stavy, kter´e vyhovuj´ı dan´ ym omezen´ım. Bud’ (x1 , . . . , xi ) cesta od koˇrene k nˇejak´emu vrcholu xi ve stavov´em stromˇe. Oznaˇc´ıme symbolem T (x1 , . . . , xi ) mnoˇzinu vˇsech moˇzn´ ych hodnot xi+1 takov´ ych, ˇze (x1 , . . . , xi ) je tak´e cesta pro nˇejak´ y stav probl´emu. D´ ale pˇredpokl´ ad´ ame, ˇze omezuj´ıc´ı podm´ınky m˚ uˇzeme vyj´ adˇrit jako predik´ aty (logick´e funkce) B i takov´e, ˇze Bi+1 (x1 , . . . , xi+1 ) je nepravdiv´e pro cestu (x1 , . . . , xi+1 ), kterou nelze prodlouˇzit tak, abychom dostali ˇreˇsen´ı. To znamen´ a, ˇze kandid´ atem na obsazen´ı pozice i + 1 ve vektoru ˇreˇsen´ı X budou ty hodnoty x i+1 , kter´e vygeneruje T a kter´e splˇ nuj´ı Bi+1 . Proceduru, popisuj´ıc´ı metodu hled´ an´ı s n´ avratem, lze formulovat jak rekurzivnˇe tak i nerekurzivnˇe. Uk´ aˇzeme si obˇe moˇznosti. V obou budeme pˇredpokl´ adat, ˇze X je glob´ aln´ı pole, ve kter´em se konstruuj´ı jednotliv´ a ˇreˇsen´ı. Jakmile procedura dospˇeje k ˇreˇsen´ı, uloˇz´ı je (vytiskne, zakres´ı ...) a pokraˇcuje d´ al.
´ METODY PROHLEDAV ´ AN ´ ´I STAVOVEHO ´ 3.5. OBECNE STROMU
55
var X: array [1..n] of stav; {deklarace pro obˇ e verze} procedure BT; {nerekurzivn´ ı verze} var k: integer; begin k := 1; while k > 0 do begin if( zb´ yv´ a nevyzkouˇsen´e X [k] ∈ ” T (X [1] , . . . , X [k − 1]) takov´e, ˇze Bk (X [1] , . . . , X [k − 1]) = true“) then begin if( X [1] , . . . , X [k − 1] tvoˇr´ı odpovˇed“) then Uloˇ z(X) else ” k := k+1 {vezmi n´ asleduj´ ıc´ ı moˇ znost} end else k := k-1; {vrat’ se k pˇ redchoz´ ı moˇ znosti} end; end; Rekurzivn´ı verze je jednoduˇsˇs´ı: procedure BT(k: integer); {rekurzivn´ ı verze} begin pro vˇsechna X [k] takov´ a, ˇze X [k] ∈ T (X [1] , . . . , X [k − 1]) ∧Bk (X [1] , . . . , X [k]) = true“ do ” begin if ( X [1] , . . . , X [k] tvoˇr´ı odpovˇed’“) then Uloˇ z(X) else BT(k+1) ” end; end; Pozn´ amky k rekurzivn´ı verzi: Jednotliv´ a vol´ an´ı procedury BT najdou vˇzdy nejv´ yˇse jednu sloˇzku vektoru ˇreˇsen´ı. V cyklu pro vˇsechna X [k] . . .“ se vyp´ıˇse v´ ysledek, pokud se nˇejak´ y naˇsel; jinak se rekurzivnˇe zavol´ a procedura ” BT s hodnotou k + 1, tj. bude se hledat dalˇs´ı sloˇzka. Pokud vhodn´e X [k] neexistuje, nestane se v˚ ubec nic a toto rekurzivn´ı vol´ an´ı procedury BT skonˇc´ı, ˇc´ımˇz se automaticky vr´ at´ıme o u ´roveˇ n zpˇet. Rekurzivn´ı formulace vych´ az´ı pˇrirozenˇejˇs´ı (podobnˇe jako vˇetˇsina algoritm˚ u, kter´e se nˇejak t´ ykaj´ı strom˚ u). ˇ Casov´ a n´ aroˇcnost algoritmu z´ avis´ı struktuˇre stavov´eho stromu, na n´ aroˇcnosti generov´ an´ı nov´ ych stav˚ u a testov´ an´ı podm´ınek a pˇredevˇs´ım na u ´spˇeˇsnosti a na vˇcasnosti odˇrez´ av´ an´ı“ vˇetv´ı stavov´eho stromu, kter´e ” nevedou k ˇreˇsen´ı - tedy na s´ıle“ podm´ınek Bi . ”
3.5
Obecn´ e metody prohled´ av´ an´ı stavov´ eho stromu
Z v´ ykladu v pˇredchoz´ım odstavci je zˇrejm´e, ˇze metoda hled´ an´ı s n´ avratem pˇredstavuje jen jednu z moˇznost´ı, jak proch´ azet stavov´ y strom dan´eho probl´emu. Pod´ıvejme se tedy na nˇekter´e dalˇs´ı moˇznosti. Nejprve zavedeme nˇekolik n´ azv˚ u. Jako ˇziv´y oznaˇc´ıme vrchol stavov´eho podstromu, kter´ y jsme jiˇz generovali (tj. generovali jsme odpov´ıdaj´ıc´ı stav probl´emu), ale od kter´eho dosud nebyli generov´ ani vˇsichni potomci. Rozv´ıjen´y vrchol je vrchol, jehoˇz potomky pr´ avˇe generujeme. Mrtv´y vrchol je vrchol, jehoˇz vˇsichni potomci jiˇz byli generov´ an´ı nebo kter´ y jiˇz nebude d´ ale rozv´ıjen z jin´ ych d˚ uvod˚ u. Vˇsechny metody ˇreˇsen´ı dan´eho probl´emu, zaloˇzen´e na prohled´ av´ an´ı stavov´eho stromu, vyˇzaduj´ı, abychom si uchov´ avali seznam ˇziv´ ych vrchol˚ u. Pˇri proch´ azen´ı stromu m˚ uˇzeme postupovat r˚ uzn´ ymi zp˚ usoby. Prvn´ı moˇzn´ y pˇr´ıstup pˇredstavuje metoda hled´ an´ı s n´ avratem (backtracking) a spoˇc´ıv´ a v tom, ˇze jakmile vygenerujeme potomka P pr´ avˇe rozv´ıjen´eho vrcholu R, stane se P rozv´ıjen´ ym vrcholem. Po vyˇcerp´ an´ı vˇsech potomk˚ u vrcholu P se stane R znovu rozv´ıjen´ ym uzlem. (Odtud poch´ az´ı alternativn´ı oznaˇcen´ı prohled´ av´ an´ı do hloubky). Druhou krajnost pˇredstavuje metoda prohled´ av´ an´ı do ˇs´ıˇrky. Pˇri n´ı z˚ ustane rozv´ıjen´ y vrchol rozv´ıjen´ ym vrcholem aˇz do u ´pln´eho vyˇcerp´ an´ı vˇsech potomk˚ u. Novˇe vygenerovan´e vrcholy se ukl´ adaj´ı do fronty. Jakmile se vygeneruj´ı vˇsichni bezprostˇredn´ı potomci aktu´ aln´ıho rozv´ıjen´eho vrcholu, vezme se jako dalˇs´ı rozv´ıjen´ y vrchol ten, kter´ y
´ ˚ KAPITOLA 3. METODY NAVRHU ALGORITMU
56
1
6 17
33
19
9
8
7 18
4
3
2
20
21
11
10 22
23
34
12
5
24
25
35
36
26
27
16
15
14
13
28
29
37
38
30
41
31
32
39
40 R
Obr. 3.6: Stavov´ y strom probl´emu s jedin´ ym ˇreˇsen´ım a jeho prohled´ av´ an´ı do ˇs´ıˇrky je ve frontˇe na ˇradˇe. Pˇri prohled´ av´ an´ı do ˇs´ıˇrky tedy proch´ az´ıme stavov´ y strom po patrech“. Ve stromu na ” obr´ azku 3.6 je vyznaˇceno poˇrad´ı, v jak´em bychom touto metodou generovali jednotliv´e vrcholy. Pˇri prohled´ av´ an´ı do ˇs´ıˇrky pouˇz´ıv´ ame - podobnˇe jako pˇri prohled´ av´ an´ı do hloubky - omezuj´ıc´ıch podm´ınek k zab´ıjen´ı“ ˇziv´ ych vrchol˚ u, to znamen´ a k urˇcen´ı, zda dan´ y vrchol m˚ uˇze v´est k ˇreˇsen´ı. ” Jak metoda prohled´ av´ an´ı do ˇs´ıˇrky tak i metoda prohled´ av´ an´ı do hloubky pevnˇe pˇredepisuj´ı poˇrad´ı proch´ azen´ı stav˚ u probl´emu. Pod´ıvejme se na stavov´ y strom na obr. 3.6 Jedin´e ˇreˇsen´ı dan´eho probl´emu je na nˇem vyznaˇceno p´ısmenem R“. ” Pouˇzijeme-li prohled´ av´ an´ı do hloubky, bude z´ aleˇzet na tom, jak oˇc´ıslujeme stavy (tedy zda budeme stavov´ y strom proch´ azet z lev´e nebo z prav´e strany). V nejlepˇs´ım pˇr´ıpadˇe najdeme ˇreˇsen´ı po 5 kroc´ıch, v nejhorˇs´ım po 37 kroc´ıch, jako u ´plnˇe posledn´ı moˇznost. Pˇri prohled´ av´ an´ı do ˇs´ıˇrky najdeme ˇreˇsen´ı jako posledn´ı. ´ Upln´ e prohled´ av´ an´ı stavov´eho stromu m˚ uˇze b´ yt prakticky neprovediteln´e. Nejzn´ amˇejˇs´ım pˇr´ıkladem probl´emu, kter´ y takto (na souˇcasn´ ych poˇc´ıtaˇc´ıch) nelze ˇreˇsit, je ˇsachov´ a hra. Jej´ı stavov´ y strom je sice koneˇcn´ y, avˇsak velice rozs´ ahl´ y. Ud´ av´ a se, ˇze pr˚ umˇern´ y faktor vˇetven´ı tohoto stromu je cca 38 (to znamen´ a, ˇze po kaˇzd´em p˚ ultahu m´ a soupeˇr pr˚ umˇernˇe 38 moˇznost´ı, jak pokraˇcovat, a tedy ˇze z kaˇzd´eho vrcholu vych´ az´ı v pr˚ umˇeru ´ 38 hran [21]). Upln´ e prohled´ an´ı stavov´eho prostoru na 15 p˚ ultah˚ u dopˇredu (tedy zdaleka ne cel´eho stavov´eho stromu ˇsachov´e hry) by pak vyˇzadovalo zpracovat v´ıce neˇz 4, 910 23 moˇznost´ı. Kdybychom zvl´ adli jeden mili´ on moˇznost´ı za sekundu, potˇrebovali bychom k prohled´ an´ı vymezen´eho podprostoru v´ıce neˇz 10 10 let, coˇz je doba srovnateln´ a se st´ aˇr´ım vesm´ıru podle dneˇsn´ıch odhad˚ u. Pˇredchoz´ı pˇr´ıklad ukazuje, ˇze pevnˇe pˇredepsan´e poˇrad´ı prohled´ av´ an´ı nemus´ı b´ yt v´ yhodn´e. Proto se ˇcasto hled´ a rozumn´ a“ v´ ahov´ a funkce F , definovan´ a na ˇziv´ ych vrcholech, podle kter´e bychom se mohli rozhodnout, ” kter´ y uzel bude nejv´ yhodnˇejˇs´ı rozv´ıjet jako n´ asleduj´ıc´ı. Hodnota F (C) t´eto funkce pro vrchol C pˇredstavuje napˇr´ıklad odhad pravdˇepodobnosti, ˇze pˇres C vede cesta k ˇreˇsen´ı. Pˇri rozhodov´ an´ı, kter´ y z vrchol˚ u Ci budeme rozv´ıjet jako n´ asleduj´ıc´ı, m˚ uˇzeme pouˇz´ıt pˇr´ımo hodnot F (C i ). Lze-li pomoc´ı funkce G odhadnout pro vrchol Ci poˇcet operac´ı, potˇrebn´ y k dosaˇzen´ı odpovˇedi z dan´eho vrcholu, m˚ uˇzeme pˇri rozhodov´ an´ı o n´ asleduj´ıc´ım rozv´ıjen´em vrcholu vych´ azet hodnoty souˇctu f (F (C i )) + G (Ci ), kde f je vhodn´ a neklesaj´ıc´ı funkce. V ide´ aln´ım pˇr´ıpadˇe by F (Ci ) ud´ avalo vzd´ alenost vrcholu Ci od ˇreˇsen´ı. Takov´ a funkce by vedla pˇr´ımo k poˇzadovan´emu v´ ysledku; jej´ı nalezen´ı je ovˇsem u ´loha ekvivalentn´ı ˇreˇsen´ı p˚ uvodn´ı u ´lohy. Proto se pˇri volbˇe n´ asleduj´ıc´ıho rozv´ıjen´eho vrcholu pouˇz´ıv´ a odhadu, pˇri jehoˇz konstrukci se vych´ az´ı z ˇca ´steˇcn´ ych znalost´ı o ˇreˇsen´ı dan´e u ´lohy. Tento postup se zpravidla oznaˇcuje jako metoda nejlacinˇejˇs´ı cesty (least cost path method ).
Kapitola 4
Rekurze Jako rekurzivn´ı oznaˇcujeme strukturu, kter´e obsahuje odkaz na sebe nebo na strukturu stejn´eho druhu. Z reklam zn´ ame napˇr. rekurzivn´ı obr´ azky: d´ıvka pˇredv´ ad´ı poˇc´ıtaˇc, na jehoˇz obrazovce vid´ıme tut´eˇz d´ıvku, jak pˇredv´ ad´ı t´ yˇz poˇc´ıtaˇc atd. Ve 2. kapitole jsme se setkali s rekurzivn´ımi datov´ ymi strukturami: kaˇzd´ a sloˇzka stromu nebo seznamu obsahuje odkaz (ukazatel) na sloˇzku stejn´eho typu. V t´eto kapitole se budeme zab´ yvat rekurz´ı v algoritmech a v odpov´ıdaj´ıc´ıch programov´ ych struktur´ ach - v procedur´ ach a funkc´ıch.
4.1
Rekurzivn´ı algoritmy a podprogramy
Rekurzivn´ı algoritmus dostaneme, jestliˇze pˇri rozkladu probl´emu na element´ arn´ı kroky dojdeme k probl´emu stejn´eho druhu, ale (v nˇejak´em smyslu) menˇs´ıho rozsahu. Rekurzivita algoritmu je ˇcasto d˚ usledkem rekurzivn´ı povahy zpracov´ avan´ ych dat. Pˇripomeˇ nme si napˇr. algoritmus zpracov´ an´ı bin´ arn´ıho stromu, kter´ y je pˇrirozenˇe rekurzivn´ı: 1. Zpracujeme u ´daj v koˇreni. 2. Pokud m´ a dan´ y strom lev´ y podstrom, zpracujeme ho. 3. Pokud m´ a dan´ y strom prav´ y podstrom, zpracujeme ho. ˇ s´ıme Ve druh´em a tˇret´ım kroku se zde odvol´ av´ ame na tento algoritmus jako celek ( zpracujeme podstrom“). Reˇ ” tedy t´ yˇz probl´em, ovˇsem pro menˇs´ı mnoˇzinu dat. Obecn´ y tvar rekurzivn´ıho algoritmu P bychom mohli popsat formul´ı P ≡ C [Si , P ] ,
(4.1)
kde C znamen´ a kompozici krok˚ u Si , kter´e neobsahuj´ı odkaz na P , a samotn´eho algoritmu P . Pˇripomeˇ nme si ale, ˇze i rekurzivn´ı algoritmus mus´ı b´ yt koneˇcn´ y. To znamen´ a, ˇze odkaz na sebe sama v (4.1) mus´ı b´ yt v´ az´ an na splnˇen´ı urˇcit´e podm´ınky, kterou oznaˇc´ıme B: P ≡ C [Si , if B then P ] .
(4.2)
V mnoha pˇr´ıpadech je rekurzivn´ı vol´ an´ı v´ az´ ano na hodnotu pˇrirozen´eho ˇc´ısla n, takˇze podm´ınka (4.2) m´ a tvar P (n) ≡ C [Si , if n > 0 then P (n − 1)] . Tato formulace zd˚ urazˇ nuje, ˇze poˇcet rekurzivn´ıch vol´ an´ı mus´ı b´ yt koneˇcn´ y. 57
58
KAPITOLA 4. REKURZE
4.1.1
Rekurze v programu
Rekurzi v programu realizujeme na u ´rovni podprogram˚ u, tj. procedur a funkc´ı. Jako rekurzivn´ı oznaˇcujeme takov´e vol´ an´ı procedury (funkce), pˇri kter´em dojde k aktivaci tˇela podprogramu dˇr´ıve, neˇz se ukonˇc´ı aktivace pˇredchoz´ı. Nˇekdy je vhodn´e rozliˇsovat pˇr´ımou a nepˇr´ımou rekurzi. Jako pˇr´ımou rekurzi oznaˇcujeme situaci, kdy tˇelo podprogramu f obsahuje vol´ an´ı sebe sama. Nepˇr´ım´ a rekurze nastane, jestliˇze podprogram f vol´ a podprogram g 1 , podprogram g1 vol´ a podprogram g2 , . . . , podprogram gn vol´ a f . Vzhledem k nepˇr´ım´e rekurzi nebo k moˇznosti volat podprogram pomoc´ı ukazatel˚ u nen´ı moˇzn´e prohl´ıdkou zdrojov´eho textu zjistit, zda se v programu m˚ uˇze vyskytnout rekurzivn´ı vol´ an´ı. Kaˇzd´e vol´ an´ı podprogramu znamen´ a vytvoˇren´ı lok´ aln´ıch promˇenn´ ych a pˇridˇelen´ı m´ısta pro skuteˇcn´e parametry. Jako skryt´ y parametr se pˇri vol´ an´ı podprogramu pˇred´ av´ a tak´e n´ avratov´ a adresa. Pˇri nˇekolikan´ asobn´em rekurzivn´ım vol´ an´ı procedury se tyto lok´ aln´ı objekty vytv´ aˇrej´ı zvl´ aˇst’ pro kaˇzdou aktivaci. Vzhledem k tomu, ˇze pamˇet’ dneˇsn´ıch poˇc´ıtaˇc˚ u nen´ı nekoneˇcn´ a (a stˇeˇz´ı kdy bude), m˚ uˇze se snadno st´ at, ˇze rekurzivn´ı program skonˇc´ı chybou, kdyˇz vyˇcerp´ a veˇskerou volnou pamˇet’. Proto jestliˇze nap´ıˇseme rekurzivn´ı podprogram, mus´ıme nejen vˇedˇet, ˇze poˇcet rekurzivn´ıch vol´ an´ı bude koneˇcn´ y, ale tak´e ˇze hloubka rekurze nebude velk´ a. Pˇ r´ıklad 4.1 Jednoduch´ ym pˇr´ıkladem na rekurzi m˚ uˇze b´ yt funkce faktori´ al. Z definice n Y n! = i i=1
pro n > 0, n! = 1 pro n = 0 plyne vztah n! = n (n − 1)!
Rekurzivn´ı funkce pro v´ ypoˇcet faktori´ alu nez´ aporn´eho cel´eho ˇc´ısla by tedy mohla m´ıt tvar function f(n: integer): integer; begin if n = 0 then f := 1 else f := n*f(n-1) end; Na prvn´ı pohled je zˇrejm´e, ˇze rekurzivn´ı v´ ypoˇcet faktori´ alu nen´ı pˇr´ıliˇs v´ yhodn´ y. Daleko efektivnˇejˇs´ı je vyn´ asobit ˇc´ısla 1, . . . , n v cyklu. Rekurze a programovac´ı jazyky Vˇetˇsina dnes pouˇz´ıvan´ ych programovac´ıch jazyk˚ u pˇripouˇst´ı rekurzivn´ı vol´ an´ı podprogram˚ u. Mezi v´ yjimky patˇr´ı napˇr. Fortran. Pˇri nepˇr´ım´e rekurzi se nelze vyhnout situaci, kdy jeden z podprogram˚ u jin´ y podprogram, kter´ y jsme jeˇstˇe nedefinovali. Ke spr´ avn´emu pˇrekladu vol´ an´ı podprogramu ovˇsem staˇc´ı, aby pˇrekladaˇc znal rozhran´ı (hlaviˇcku) volan´e funkce. Pˇrekladaˇci Pascalu ji sdˇel´ıme pomoc´ı deklarace s direktivou forward, v ANSI C a v C++ pouˇzijeme prototyp funkce. Programovac´ı jazyky, kter´e neprov´ adˇej´ı kontrolu typ˚ u pˇred´ avan´ ych parametr˚ u, nemus´ı pˇredbˇeˇzn´e informace poˇzadovat, (jako je tomu ve starˇs´ıch variant´ ach jazyka C).
4.1.2
Kdy se rekurzi vyhnout
Jestliˇze pracujeme s rekurzivn´ımi daty nebo vych´ az´ıme z rekurzivn´ıch definic, dospˇejeme obvykle zcela pˇrirozenˇe k rekurzivn´ım algoritm˚ um. To ale neznamen´ a, ˇze rekurzivn´ı implementace je nejlepˇs´ım ˇreˇsen´ım (pokud n´ am ji programovac´ı jazyk v˚ ubec dovoluje). Typick´ y algoritmus, u kter´eho lze se rekurzi vyhnout, m˚ uˇzeme charakterizovat pomoc´ı sch´ematu P = [S, if B then P ] .
(4.3)
4.1. REKURZIVN´I ALGORITMY A PODPROGRAMY
59
(v tomto z´ apisu z´ avorky [ ] pˇredepisuj´ı sekvenˇcn´ı proveden´ı operac´ı, kter´e jsou v nich zaps´ any). Takov´ ato sch´emata se zpravidla objevuj´ı u v´ ypoˇct˚ u na z´ akladˇe jednoduch´ ych rekurentn´ıch vztah˚ u jako je faktori´ al v pˇr´ıkladu 4.1. Sch´ema (4.3) pˇredepisuje prov´est kroky S, a pokud je splnˇena podm´ınka B, zavolat P - tedy znovu prov´est S, a pokud je splnˇena podm´ınka B, zavolat ... atd. Je tedy zˇrejm´e, ˇze sch´ema (4.3) je ekvivalentn´ı iterativn´ımu sch´ematu P = [S, while B do S] . (4.4) kter´e vede k efektivnˇejˇs´ımu programu. Jin´ y, trochu sloˇzitˇejˇs´ı pˇr´ıklad, kdy je pouˇzit´ı rekurze nev´ yhodn´e, pˇredstavuj´ı v´ ypoˇcty zaloˇzen´e na rekurentn´ıch vztaz´ıch tvaru xn = f (xn−1 , xn−2 , . . . , xn−k ) (4.5) ve kter´ ych zn´ ame x0 , . . . , xk . Je jasn´e, ˇze pˇri v´ ypoˇctu xn m˚ uˇzeme postupovat shora dol˚ u“, tj. rekurzivnˇe, nebo ” zdola nahoru“, iterativnˇe. N´ asleduj´ıc´ı pˇr´ıklad n´ am vˇsak uk´ aˇze, ˇze rekurzivn´ı v´ ypoˇcet b´ yv´ a v tomto pˇr´ıpadˇe ” mimoˇra ´dnˇe neefektivn´ı. Pˇ r´ıklad 4.2 Jednoduch´ ym pˇr´ıkladem rekurentn´ı posloupnosti, zadan´e sch´ematem tvaru (4.5), jsou Fibonacciova ˇc´ısla f n , definovan´ a takto: fn+1 = fn + fn−1 pro n > 1, f0 = 0, f1 = 1. Pod´ıvejme se, jak to dopadne, naprogramujeme-li v´ ypoˇcet Fibonacciov´ ych ˇc´ısel jako rekurzivn´ı funkci: function F(n: integer): integer; begin if (n = 0) or (n = 1) then f := n else f := f(n-1) + f(n-2) end;
{Fibonacciova c ˇı ´sla jako} {program´ atorsk´ y horor}
V´ ypoˇcet f (n) pro jak´ekoli n > 1 znamen´ a automaticky dalˇs´ı dvˇe vol´ an´ı f . Oznaˇc´ıme-li p n poˇcet vol´ an´ı funkce f potˇrebn´ y pro v´ ypoˇcet fn , bude pro pn platit rekurentn´ı vztah pn = pn−1 + pn−2 + 1 (4.6) Protoˇze pˇri v´ ypoˇctu pn−1 budeme muset poˇc´ıtat i pn−2 , mus´ı b´ yt pn−1 > pn−2 , takˇze ze vztahu (4.6) plyne pn > 2pn−2 . (4.7) Protoˇze p0 = p1 = 1, plyne ze (4.7), ˇze poˇcet vol´ an´ı pn roste rychleji neˇz posloupnost 2n/2 . Graf rekurzivn´ıho vol´ an´ı pro n = 5 vid´ıte na obr. 4.1. Vˇsimnˇete si, ˇze hodnota f (0) se poˇc´ıt´ a tˇrikr´ at a hodnota f (1) dokonce pˇetkr´ at! Pouˇzijeme-li nerekurzivn´ıho v´ ypoˇctu, napˇr. function F(n: integer): integer; {Fibonacciova c ˇı ´sla } var a,b,c,i: integer; {nerekurzivnˇ e} begin if (n = 0) or (n = 1) then f := n else begin a := 0; b := 1; for i:=2 to n do begin c := a + b; a := b; b := c; end; f := c; end; bude se kaˇzd´ a z hodnot posloupnosti poˇc´ıtat pouze jednou. S rekurentn´ımi vztahy tohoto typu se setk´ ame v matematice pomˇernˇe ˇcasto. Napˇr. Besselovy funkce vyhovuj´ı rekurentn´ımu vztahu 2v Jv (x) − Jv−1 (x) , v ∈ R. Jv+1 (x) = x
60
KAPITOLA 4. REKURZE
5 4
3
3 2
1
2
2
1
1
1 0
0
1
0
Obr. 4.1: Graf rekurzivn´ıch vol´ an´ı pˇri v´ ypoˇctu f (5) Podobn´e vztahy plat´ı i pro dalˇs´ı speci´ aln´ı funkce. Jin´ ym pˇr´ıkladem mohou b´ yt kombinaˇcn´ı ˇc´ısla, kter´ a splˇ nuj´ı rekurzivn´ı vztah n+1 n n = + . k+1 k k+1
4.2
Jak odstranit rekurzi
Rekurzivn´ı algoritmy, jak uˇz v´ıme, vznikaj´ı ˇcasto jako pˇrirozen´ y d˚ usledek postupu shora dol˚ u pˇri n´ avrhu algoritmu. Nˇekdy je ovˇsem nezbytn´e transformovat algoritmus do nerekurzivn´ı podoby - napˇr. proto, ˇze pouˇzit´ y programovac´ı jazyk ji nedovoluje. Uk´ aˇzeme si jednoduch´ y postup, s jehoˇz pomoc´ı odstran´ıme z podprogramu rekurzivn´ı vol´ an´ı (jde o pˇr´ımou rekurzi, kter´ a se vyskytuje podstatnˇe ˇcastˇeji neˇz rekurze nepˇr´ım´ a). N´ asleduj´ıc´ı postup je zaloˇzen na vyuˇzit´ı z´ asobn´ıku. Pro jednoduchost v nˇem budeme pod oznaˇcen´ım procedura“ rozumˇet i funkci. ” 1. Na poˇca ´tku procedury deklarujeme z´ asobn´ık jako glob´ aln´ı objekt; tak´e ukazatel na vrchol z´ asobn´ıku bude glob´ aln´ı. Tento z´ asobn´ık bude slouˇzit k ukl´ ad´ an´ı parametr˚ u, lok´ aln´ıch promˇenn´ ych, n´ avratov´ ych adres a vypoˇcten´ ych hodnot pˇri rekurzivn´ım vol´ an´ı. 2. Pˇred prvn´ı pˇr´ıkaz tˇela procedury vloˇz´ıme n´ avˇeˇst´ı L 1 . D´ ale kaˇzd´e rekurzivn´ı vol´ an´ı dan´e procedury nahrad´ıme n´ asleduj´ıc´ı posloupnost´ı pˇr´ıkaz˚ u: 3. Uloˇz´ıme na z´ asobn´ık lok´ aln´ı promˇenn´e a form´ aln´ı parametry. 4. Vytvoˇr´ıme i-t´e nov´e n´ avˇeˇst´ı Li , i = 2, . . . , a hodnotu i uloˇz´ıme do z´ asobn´ıku. Tuto hodnotu pozdˇeji pouˇzijeme ke stanoven´ı n´ avratov´e adresy. Vytvoˇren´e n´ avˇeˇst´ı um´ıst´ıme v kroku 7. 5. Vyhodnot´ıme skuteˇcn´e parametry nov´eho vol´ an´ı a uloˇz´ıme je do form´ aln´ıch parametr˚ u (nikoli do z´ asobn´ıku). 6. Vloˇz´ıme nepodm´ınˇen´ y skok na poˇca ´tek procedury, na n´ avˇeˇst´ı L 1 . 7. Jestliˇze takto upravujeme funkci, um´ıst´ıme n´ avˇeˇst´ı, vytvoˇren´e ve 4. kroku, k pˇr´ıkazu, kter´ ym vyjmeme hodnotu funkce ze z´ asobn´ıku, a pˇripoj´ıme k´ od, kter´ y tuto hodnotu v rekurzivn´ı funkci vyuˇz´ıv´ a. V proceduˇre toto n´ avˇeˇst´ı pˇripoj´ıme k prvn´ımu pˇr´ıkazu bezprostˇrednˇe za skokem, vloˇzen´ ym v 6. kroku. Na konci procedury provedeme tyto u ´pravy: 8. Je-li z´ asobn´ık pr´ azdn´ y, skonˇc´ıme. 9. Jinak vezmeme aktu´ aln´ı hodnoty v´ ystupn´ıch parametr˚ u a pˇred´ ame je odpov´ıdaj´ıc´ım promˇenn´ ym na vrcholu z´ asobn´ıku (t´ım vrac´ıme vypoˇcten´e hodnoty parametr˚ u do pˇredchoz´ıho vol´ an´ı). 10. Vloˇz´ıme k´ od, kter´ y odstran´ı ze z´ asobn´ıku index n´ avratov´e adresy (pokud tam nˇejak´ y je) a uloˇz´ıme jej do nepouˇzit´e promˇenn´e. 11. Vyjmeme ze z´ asobn´ıku hodnoty lok´ aln´ıch promˇenn´ ych a parametr˚ u a pˇridˇel´ıme je odpov´ıdaj´ıc´ım promˇenn´ ym.
ˇ ´IKLADY 4.3. DALSˇ´I PR
61
12. Je-li to funkce, vloˇz´ıme instrukce pro vyhodnocen´ı vracen´e hodnoty a v´ ysledek uloˇz´ıme na vrchol z´ asobn´ıku. 13. Index n´ avratov´e adresy pouˇzijeme ke skoku na pˇr´ısluˇsn´e n´ avˇeˇst´ı L i . Pˇ r´ıklad 4.3 Vezmeme rekurzivn´ı verzi funkce faktori´ al z pˇr´ıkladu 4.2 a uprav´ıme ji podle pˇredchoz´ıho n´ avodu. Pˇritom budeme pˇredpokl´ adat, ˇze m´ ame k dispozici objektov´ y typ z´ asobn´ık na ukl´ ad´ an´ı cel´ ych ˇc´ısel s konstruktorem vytvoˇr a s metodami vloˇz, vyjmi a pr´ azdn´y. V n´ asleduj´ıc´ım v´ ypisu odkazuj´ı ˇc´ısla v koment´ aˇr´ıch na kroky pˇredchoz´ıho n´ avodu. Citlivˇejˇs´ı ˇcten´ aˇre bych r´ ad pˇredem upozornil, ˇze v´ ysledek je v pˇr´ıkr´em rozporu s pravidly program´ atorsk´e sluˇsnosti. Je ale jasn´e, ˇze takovouto transformac´ı z´ısk´ ame polotovar, kter´ y budeme d´ ale upravovat. function f(n: integer): integer; var a: integer; z: z´ asobn´ ık; label L1, L2; begin z.Vytvoˇ r; {1} L1: {2} if n = 0 then a := 1 {vypoˇ ctenou hodnotu uloˇ zı ´me do a} else begin z.vloˇ z(n); {3} n := n-1; {5} goto L1; {6} L2: {7} a := z.vyjmi; {vyjmeme v´ ysledek z pˇ redchoz´ ıho vol´ an´ ı} a := a*n; {a pouˇ zijeme jej} end; if z.pr´ azdn´ y then f := a {8} else begin n := z.vyjmi; {11} z.vloˇ z(a); {12} goto L2; {13} end; end; Vzhledem k tomu, ˇze funkce f obsahovala jedin´e rekurzivn´ı vol´ an´ı, nepotˇrebujeme ukl´ adat n´ avratovou adresu na z´ asobn´ık - ta je vˇzdy stejn´ a. Odpad´ a tedy t´emˇeˇr cel´ y krok 4 a krok 10. Funkce nem´ a ˇza ´dn´e v´ ystupn´ı parametry, takˇze odpad´ a i krok 9. V´ ysledek, vypoˇctenou hodnotu, ukl´ ad´ ame do pomocn´e promˇenn´e a. To nen´ı p˚ uvodn´ı lok´ aln´ı promˇenn´ a, takˇze ji ve 3. kroku nemus´ıme ukl´ adat do z´ asobn´ıku. Jak jsme jiˇz ˇrekli v u ´vodu, z´ısk´ ame touto transformac´ı polotovar, kter´ y m˚ uˇzeme (a mus´ıme) jeˇstˇe d´ ale upravovat. Napˇr´ıklad z toho, ˇze f obsahovala jedin´e rekurzivn´ı vol´ an´ı, tak´e plyne, vypoˇcten´ a hodnota, kter´ a je v a a kterou ve 12. kroku ukl´ ad´ ame na z´ asobn´ık, je vˇzdy stejn´ a jako hodnota, kter´ a je v a a kterou vyj´ım´ ame ze z´ asobn´ıku v 7. kroku. M˚ uˇzeme tedy vypustit instrukce z.vloˇz (a) a z.vyjmi. D´ ale se m˚ uˇzeme pokusit odstranit ˇ pˇr´ıkazy goto a nahradit je cykly1 atd. Casto se tak podaˇr´ı dospˇet k rozumn´e iterativn´ı verzi dan´eho algoritmu.
4.3
Dalˇ s´ı pˇ r´ıklady
Na z´ avˇer kapitoly o rekurzi si uk´ aˇzeme nˇekolik pˇr´ıklad˚ u. 1 V naˇ sem pˇr´ıpadˇe dokonce mus´ıme, pokud budeme uveden´ y program pˇrekl´ adat pˇrekladaˇcem, kter´ y se ˇr´ıd´ı normou ISO jazyka Pascal. Tato norma, jak zn´ amo, zakazuje skok dovnitˇr sloˇzen´eho pˇr´ıkazu, takˇze konstrukce goto L2 je syntakticky nespr´ avn´ a.
62
KAPITOLA 4. REKURZE
A
+
P
AOP
(C P
−
D)
AOP P
PV
PV
PV
ˇclen
ˇclen
ˇclen
*
B
MCP P
JAV JAV PV ˇclen
PV
ˇclen
JAV JAV
Obr. 4.2: Syntaktick´ a anal´ yza jednoduch´eho aritmetick´eho v´ yrazu (P znamen´ a promˇennou)
4.3.1
Syntaktick´ a anal´ yza
Syntaktick´e definice programovac´ıch jazyk˚ u jsou obvykle pˇrirozenˇe rekurzivn´ı. Proto tak´e m˚ uˇzeme oˇcek´ avat, ˇze pˇrekladaˇce budou pˇri syntaktick´e anal´ yze pouˇz´ıvat rekurzivn´ıch procedur. Jako pˇr´ıklad vezmeme syntaktickou definici jednoduch´eho aritmetick´eho v´yrazu (JAV). Za jednotliv´ ymi syntaktick´ ymi kategoriemi uvedeme v z´ avork´ ach zkratky, pomoc´ı kter´ ych se na nˇe budeme d´ ale odvol´ avat. Symbol ˇ |“ slouˇz´ı k oddˇelov´ an´ı jednotliv´ ych alternativ, CBZ znamen´ a cel´e ˇc´ıslo bez znam´enka“. ” ” aditivn´ı oper´ ator (AOP): multiplikativn´ı oper´ ator (MOP): prvotn´ı v´yraz (PV ): ˇclen: JAV :
+|*|/ ˇ CBZ | promˇenn´ a | (JAV ) PV | ˇclen MOP PV ˇclen | AOP ˇclen | JAV AOP ˇclen
Pˇri pˇrekladu potˇrebujeme urˇcit vˇseobecnou syntaktickou tˇr´ıdu dan´eho ˇretˇezce znak˚ u. Na obr´ azku vid´ıme pˇr´ıklad anal´ yzy ˇretˇezce A + (C - D)*B“. ” Analyz´ ator zpracov´ av´ a zdrojov´ y soubor znak po znaku. M˚ uˇze to b´ yt napˇr. procedura, jej´ımiˇz vstupn´ımi parametry jsou ukazatel na znakov´ y ˇretˇezec (vstupn´ı data) a c´ıl (syntaktick´ a kategorie). Jestliˇze lze znaky, leˇz´ıc´ı bezprostˇrednˇe za m´ıstem, urˇcen´ ym ukazatelem, ch´ apat jako instanci c´ıle, vr´ at´ı v´ ysledek ANO (true) a ukazatel se posune o jeden znak dopˇredu, jinak vr´ at´ı NE (false) a ukazatel se nezmˇen´ı. ˇ Pod´ıvejme se, jak by prob´ıhala anal´ yza ˇretˇezce A + (C - D)*B“ popsan´ ym analyz´ atorem. Sipkou ↑“ oznaˇc´ıme ” ” pozici ukazatele ve vstupn´ım ˇretˇezci, ˇsipkou →“ vol´ an´ı analyz´ atoru. ” → A + (C - D) * B c´ıl: JAV ↑ JAV m˚ uˇze b´ yt ˇclen → A + (C - D) * B c´ıl: ˇclen ↑ ˇclen m˚ uˇze b´ yt prvotn´ı v´ yraz (PV) → A + (C - D) * B c´ıl: PV ↑ A“ je instance kategorie promˇenn´ a“, PV ” ” m˚ uˇze b´ yt promˇenn´ a - vrat’ ANO, nalezen PV ANO, nalezen ˇclen ANO, nalezen JAV
ˇ ´IKLADY 4.3. DALSˇ´I PR
63
Nyn´ı si ale mus´ıme poloˇzit ot´ azku: lze naj´ıt delˇs´ı ˇretˇezec, kter´ y by t´eˇz byl JAV? Podle syntaktick´e definice zkus´ıme konstrukci JAV AOP ˇclen“. ” → + (C - D) * B c´ıl: AOP ↑ ANO, nalezen AOP → (C - D) * B c´ıl: ˇclen ↑ ˇclen m˚ uˇze b´ yt PV; PV m˚ uˇze zaˇc´ınat (“, mus´ı to b´ yt konstrukce (JAV)“ ” ” → C - D) * B c´ıl: JAV ↑ C“ je promˇenn´ a, to je PV - vrat’ ANO ” Lze naj´ıt delˇs´ı ˇretˇezec, kter´ y by byl t´eˇz JAV? Zkus´ıme moˇznost JAV AOP ˇclen“. ” → - D) * B c´ıl: AOP ↑ ANO, AOP nalezen → D) * B c´ıl: ˇclen ↑ D“ je promˇenn´ a, tedy PV, tedy ˇclen - ANO ” Lze naj´ıt delˇs´ı ˇretˇezec, kter´ y by byl t´eˇz JAV? → )*B c´ıl: AOP ↑ NE Naˇsli jsme nejdelˇs´ı JAV. Aby to byl PV, mus´ı konˇcit z´ avorkou. → )*B c´ıl: )“ ” ↑ ANO Naˇsli jsme prvotn´ı v´ yraz, tedy ˇclen. Lze naj´ıt delˇs´ı ˇclen? Jedin´ a moˇzn´ a konstrukce je ˇclen MOP PV“. ” → *B c´ıl: MOP ↑ ANO, nalezen MOP → B c´ıl: PV ↑ B“ je promˇenn´ a, tedy PV - ANO, nalezen PV ” ANO, byl nalezen JAV a ˇretˇezec byl plnˇe analyzov´ an.
4.3.2
Ackermannova funkce
Na z´ avˇer si uk´ aˇzeme, jak se tak´e m˚ uˇze chovat rekurzivnˇe definovan´ a funkce. Ackermannova funkce je d´ ana pˇredpisem pro m = 0, n+1 A (m − 1, 1) pro n = 0 A(m, n) = A (m − 1, A (m, n − 1)) jinak.
Jej´ı definice je zd´ anlivˇe jednoduch´ a. Pokud ji budete chv´ıli zkoumat nebo pokud si ji naprogramujete, zjist´ıte, ˇze uˇz pro velmi n´ızk´e hodnoty parametr˚ u se vyˇcerpaj´ı moˇznosti poˇc´ıtaˇce. V n´ asleduj´ıc´ı tabulce najdete pro nˇekolik hodnot parametr˚ u hodnotu funkce, poˇcet rekurzivn´ıch vol´ an´ı, maxim´ aln´ı hloubku rekurze a maxim´ aln´ı hodnotu parametru n, se kter´ ym je funkce vol´ ana. m 0 1 2 3
n n n n n
A (m, n) n+1 n+2 2n + 3 8.2n − 3
poˇcet vol´ an´ı 1 2n + 2 2n2 + 7n + 5 fn
max. hloubka 1 n+1 2n + 4 8.2n − 1
max. n n n+2 2n + 2 8.2n − 4
Tab. 4.1 Vlastnosti Ackermannovy funkce V tab. 4.1 je fn posloupnost, rostouc´ı pˇribliˇznˇe jako 4n , jej´ıˇz osm´ y ˇclen m´ a napˇr. hodnotu 2785999. Tato tabulka mimo jin´e ukazuje, ˇze hloubka rekurze a poˇcet rekurzivn´ıch vol´ an´ı se mohou podstatnˇe liˇsit. Poznamenejme, ˇze pˇri v´ ypoˇctu A (4, 1) nestaˇcil z´ asobn´ık a program skonˇcil chybou.
64
KAPITOLA 4. REKURZE
Kapitola 5
Tˇ r´ıdˇ en´ı Pod pojmem tˇr´ıdˇen´ı budeme rozumˇet uspoˇra ´d´ an´ı zadan´e posloupnosti dat podle urˇcit´eho kl´ıˇce v neklesaj´ıc´ım nebo nerostouc´ım poˇrad´ı. Prvky posloupnosti, kterou tˇr´ıd´ıme, mohou b´ yt jak´ehokoli typu, samozˇrejmˇe vˇsechny stejn´eho. Obecnˇe p˚ ujde o z´ aznamy (struktury); kl´ıˇcem, podle kter´eho budeme prvky posloupnosti porovn´ avat, m˚ uˇze b´ yt bud’ pˇr´ımo nˇekter´ a ze sloˇzek tohoto z´ aznamu nebo funkce, vypoˇc´ıtan´ a na z´ akladˇe cel´eho z´ aznamu. My budeme pro jednoduchost pˇredpokl´ adat, ˇze kaˇzd´ y z prvk˚ u posloupnosti obsahuje numerickou sloˇzku jm´enem kl´ıˇc. Obˇcas budeme pro struˇcnost mluvit pouze o porovn´ av´ an´ı prvk˚ u; budeme t´ım ovˇsem m´ıt na mysli porovn´ av´ an´ı jejich kl´ıˇc˚ u. Podobnˇe budeme-li hovoˇrit o nejmenˇs´ım prvku“, budeme m´ıt na mysli prvek s nejmenˇs´ım kl´ıˇcem atd. ” Pˇri v´ ykladu metod tˇr´ıdˇen´ı budeme rozliˇsovat dvˇe z´ akladn´ı situace: 1. Jestliˇze zn´ ame pˇredem poˇcet prvk˚ u posloupnosti a vˇsechny prvky tˇr´ıdˇen´e posloupnosti jsou uloˇzeny ve vnitˇrn´ı pamˇeti poˇc´ıtaˇce, kde k nim m˚ uˇzeme pˇristupovat v libovoln´em poˇrad´ı (tedy tˇr´ıd´ıme data, uloˇzen´ a v poli), hovoˇr´ıme o vnitˇrn´ım tˇr´ıdˇen´ı. 2. Jestliˇze poˇcet prvk˚ u tˇr´ıdˇen´e posloupnosti pˇredem nezn´ ame a prvky tˇr´ıdˇen´e posloupnosti jsou uloˇzeny ve vnˇejˇs´ı pamˇeti se sekvenˇcn´ım pˇr´ıstupem (tedy tˇr´ıd´ıme soubor na magnetick´e p´ asce nebo disku), hovoˇr´ıme o vnˇejˇs´ım tˇr´ıdˇen´ı. Metody vnitˇrn´ıho a vnˇejˇs´ıho tˇr´ıdˇen´ı se podstatn´ ym zp˚ usobem liˇs´ı, proto o nich budeme hovoˇrit zvl´ aˇst’.
5.1
Vnitˇ rn´ı tˇ r´ıdˇ en´ı
Jedn´ım z nejd˚ uleˇzitˇejˇs´ıch poˇzadavk˚ u pˇri tˇr´ıdˇen´ı pol´ı je u ´sporn´e vyuˇz´ıv´ an´ı operaˇcn´ı pamˇeti poˇc´ıtaˇce. To znamen´ a, ˇze bychom mˇeli pracovat na m´ıstˇe“, pˇr´ımo uvnitˇr tˇr´ıdˇen´eho pole, aniˇz bychom deklarovali pole pomocn´e. ” Jin´ ymi slovy v´ ychoz´ı (zdrojov´ a) posloupnost mus´ı b´ yt uloˇzena na stejn´em m´ıstˇe pamˇeti jako posloupnost c´ılov´ a. M´ a-li tˇr´ıdˇen´e pole n prvk˚ u, mˇeli bychom tedy spotˇrebu pamˇeti vyj´ adˇrit ˇc´ıslem n + c, kde c je mal´ a konstanta. Dalˇs´ım poˇzadavkem je samozˇrejmˇe efektivita algoritmu. Vhodn´ ym mˇeˇr´ıtkem efektivity m˚ uˇze b´ yt poˇcet porovn´ an´ı kl´ıˇc˚ u a poˇcet pˇresun˚ u prvk˚ u v poli. Obvykle je v´ yznamnˇejˇs´ı poˇcet pˇresun˚ u, nebot’ b´ yvaj´ı ˇcasovˇe n´ aroˇcnˇejˇs´ı. Existuj´ı ale i pˇr´ıklady, ve kter´ ych je porovn´ an´ı sloˇzitˇejˇs´ı neˇz pˇresun dat. Pˇri naˇsich u ´vah´ ach si budeme vˇs´ımat z´ avislosti obou tˇechto veliˇcin na poˇctu prvk˚ u n tˇr´ıdˇen´eho pole. V n´ asleduj´ıc´ıch odstavc´ıch se sezn´ am´ıme s nˇekolika jednoduch´ ymi metodami pro vnitˇrn´ı tˇr´ıdˇen´ı, kter´e vyˇzaduj´ı ˇra ´dovˇe n2 operac´ı. Tyto algoritmy jsou velice jednoduch´e a vhodn´e pro mal´e objemy tˇr´ıdˇen´ ych dat. Nav´ıc se na nich snadno demonstruj´ı principy tˇr´ıdˇen´ı. Kvalitn´ı algoritmy pro tˇr´ıdˇen´ı pol´ı vyˇzaduj´ı ˇra ´dovˇe n log n porovn´ an´ı, tedy podstatnˇe m´enˇe. Jednotliv´e operace mohou ale b´ yt sloˇzitˇejˇs´ı, takˇze pro mal´ a n mohou b´ yt v´ yhodnˇejˇs´ı metody jednoduch´e. V n´ asleduj´ıc´ıch odstavc´ıch budeme zpravidla pˇredpokl´ adat, ˇze tˇr´ıdˇen´ a posloupnost je uloˇzena v poli a, deklarovan´em pˇr´ıkazem 65
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
66
3
(2
17
1
8
5)
2
3
(17
1
8
5)
2
3
17
(1
8
5)
1
2
3
17
(8
5)
1
2
3
8
17
(5)
1
2
3
5
8
17
neutˇr´ıdˇen´e
plnˇe utˇr´ıdˇen´e
Obr. 5.1: Tˇr´ıdˇen´ı pˇr´ım´ ym vkl´ ad´ an´ım var a: array [1..n] of data; V nˇekter´ ych pˇr´ıpadech toto pole dopln´ıme zleva o nˇekolik prvk˚ u, kter´e poslouˇz´ı jako zar´ aˇzky. Vedle toho budeme pˇredpokl´ adat deklaraci typu index: type index = 1..n
5.1.1
Tˇ r´ıdˇ en´ı pˇ r´ım´ ym vkl´ ad´ an´ım
Tato metoda pˇripom´ın´ a zp˚ usob, jak´ ym si karetn´ı hr´ aˇci obvykle ˇrad´ı karty v ruce: z bal´ıˇcku rozdan´ ych karet berou jednu kartu po druh´e a zaˇrad´ı ji na spr´ avn´e m´ısto podle velikosti a barvy. Je-li tˇreba, odsunou doprava karty, kter´e zaˇradili jiˇz dˇr´ıve. Pˇri popisu tˇr´ıdˇen´ı pole pˇr´ım´ ym vkl´ ad´ an´ım vyjdeme z pˇredstavy dvou posloupnost´ı, zdrojov´e a c´ılov´e. V i-t´em kroku vezmeme i-t´ y prvek zdrojov´e posloupnosti a zaˇrad´ıme jej na spr´ avn´e m´ısto v c´ılov´e posloupnosti. Protoˇze zdrojov´ a i c´ılov´ a posloupnost leˇz´ı na t´emˇze m´ıstˇe pamˇeti, budeme postupovat takto: • Prvn´ı prvek pole ponech´ ame na m´ıstˇe. • Vezmeme druh´ y prvek a porovn´ ame jej s prvn´ım. Je-li vˇetˇs´ı, ponech´ ame jej na m´ıstˇe, jinak jej zaˇrad´ıme na prvn´ı m´ısto a prvek z prvn´ıho m´ısta odsuneme na druh´e m´ısto. • Vezmeme tˇret´ı prvek, zjist´ıme, zda patˇr´ı na prvn´ı, druh´e nebo tˇret´ı m´ısto, zaˇrad´ıme jej a prvky za n´ım podle potˇreby odsuneme. atd. Pˇ r´ıklad 5.1 ˇ Chceme utˇr´ıdit posloupnost (3, 2, 17, 1, 8, 5), viz obr. 5.1. Sipky na obr´ azku ukazuj´ı smˇery pˇresun˚ u v jednotliv´ ych kroc´ıch; v z´ avork´ ach je dosud netˇr´ıdˇen´ a ˇca ´st pole. Neˇz se pust´ıme do z´ apisu algoritmu pˇr´ım´eho vkl´ ad´ an´ı v Pascalu, mus´ıme si ujasnit, jak vloˇzit prvek na spr´ avn´e m´ısto v utˇr´ıdˇen´e ˇca ´sti posloupnosti. Nejjednoduˇsˇs´ı moˇznost je asi tato: vkl´ adan´ y prvek je na i-t´em m´ıstˇe, vlevo od nˇej je jiˇz setˇr´ıdˇen´ a ˇca ´st pole. Porovn´ ame ho tedy s prvkem bezprostˇrednˇe vlevo od nˇej. Pokud je vkl´ adan´ y prvek vˇetˇs´ı nebo roven sv´emu lev´emu sousedu, je jiˇz na spr´ avn´em m´ıstˇe. V opaˇcn´em pˇr´ıpadˇe tyto dva prvky prohod´ıme a vkl´ adan´ y prvek opˇet porovn´ ame s prvkem, kter´ y je v poli bezprostˇrednˇe vlevo od nˇej atd. Pod´ıvejme se na vkl´ ad´ an´ı prvku 1 v pˇr´ıkladu 5.1 (tˇret´ı ˇra ´dek na obr. 5.1). Tento prvek stoj´ı na 4. m´ıstˇe. Porovn´ ame jej tedy s prvkem na tˇret´ım m´ıstˇe (17); protoˇze je menˇs´ı, prohod´ıme je, takˇze dostaneme posloupnost 2, 3, 1, 17, . . . .
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
67
D´ ale porovn´ ame prvek 1 s prvkem na druh´em m´ıstˇe (2). Protoˇze 1 je menˇs´ı, opˇet je prohod´ıme, takˇze dostaneme posloupnost 2, 1, 3, 17 . . . . Po dalˇs´ım porovn´ an´ı a prohozen´ı dostaneme posloupnost, kterou vid´ıme ve 4. ˇra ´dku obr´ azku. Hled´ an´ı spr´ avn´eho m´ısta skonˇc´ı, jestliˇze bude prvek vlevo menˇs´ı neˇz vkl´ adan´ y prvek nebo jestliˇze jsme jiˇz dospˇeli na prvn´ı m´ısto v poli. To jsou dvˇe podm´ınky; jedn´e z nich se m˚ uˇzeme zbavit (a tak algoritmus ponˇekud zjednoduˇsit), jestliˇze pouˇzijeme zar´ aˇzku. Pole a bude m´ısto a [1] zaˇc´ınat prvkem a [0], tj. deklarujeme je pˇr´ıkazem var a: array [0..n] of prvek; a do a[0] uloˇz´ıme hodnotu vkl´ adan´eho prvku. Porovn´ av´ an´ı pak skonˇc´ı v nejhorˇs´ım pˇr´ıpadˇe tˇesnˇe pˇred zar´ aˇzkou, nebot’ vkl´ adan´ y prvek bude m´ıt stejn´ y kl´ıˇc jako zar´ aˇzka. Procedura pro tˇr´ıdˇen´ı pˇr´ım´ ym vkl´ ad´ an´ım m˚ uˇze vypadat takto (n je poˇcet prvk˚ u ve tˇr´ıdˇen´e posloupnosti): procedure Pˇ rı ´m´ eVkl´ ad´ an´ ı; var i,j: index; x: prvek; {pomocn´ a promˇ enn´ a} begin for i := 2 to n do begin x := a[i]; a[0] := x; {definice zar´ az ˇky} j := i-1; while x.kl´ ıc ˇ < a[j].kl´ ıc ˇ do begin a[j+1] := a[j]; {odsouv´ an´ ı prvk˚ u doprava} dec(j) end; a[j+1] := x; {vloˇ zen´ ı prvku na spr´ avn´ e m´ ısto} end; end; Anal´ yza algoritmu pˇ r´ım´ eho vkl´ ad´ an´ı Pˇri rozboru t´eto metody se budeme op´ırat o proceduru Pˇr´ım´eVkl´ ad´ an´ı. Je zˇrejm´e, ˇze nejhorˇs´ı moˇzn´ y pˇr´ıpad nastane, jestliˇze bude zdrojov´ a posloupnost uspoˇra ´d´ ana v opaˇcn´em poˇrad´ı - jako prvn´ı bude nejvˇetˇs´ı prvek, jako posledn´ı prvek nejmenˇs´ı. Potom budeme muset v i-t´em kroku, pˇri vkl´ ad´ an´ı i-t´eho prvku, prov´est C i = i−1 porovn´ an´ı a Mi = i + 1 pˇresun˚ u vˇcetnˇe uloˇzen´ı hodnoty vkl´ adan´eho prvku do zar´ aˇzky. Celkov´ y poˇcet porovn´ an´ı a pˇresun˚ u v nejhorˇs´ım pˇr´ıpadˇe potom bude n n n n X X X X n2 − n n2 + n − 2 Cmax = Ci = (i − 1) = , Mmax = Mi = (i + 1) = . 2 2 i=2 i=2 i=2 i=2
(5.1)
Nejlepˇs´ı moˇzn´ y pˇr´ıpad nastane, kdyˇz bude zdrojov´ a posloupnost jiˇz spr´ avnˇe uspoˇra ´d´ ana. V takov´em pˇr´ıpadˇe potˇrebujeme v kaˇzd´em kroku pouze jedno porovn´ an´ı, Ci = 1. Poˇcet pˇresun˚ u v popsan´e proceduˇre bude v kaˇzd´em kroku1 Mi = 3. Celkov´ y poˇcet porovn´ an´ı a pˇresun˚ u v nejlepˇs´ım pˇr´ıpadˇe potom bude n X Cmin = 1 = n − 1, Mmin = 3 (n − 1) . (5.2) i=2
Budeme-li pˇredpokl´ adat, ˇze vˇsechny permutace zdrojov´e posloupnosti jsou stejnˇe pravdˇepodobn´e, m˚ uˇzeme tvrdit, ˇze pr˚ umˇern´ y poˇcet porovn´ an´ı bude Ci = i/2 a poˇcet pˇresun˚ u bude Mi = Ci + 2. Odtud pro pr˚ umˇern´e poˇcty operac´ı odvod´ıme n n n n X X X X 1 n2 + 9n − 10 n2 + n − 2 , M = Mi = i+2 = . (5.3) C = Ci = i= 4 2 4 i=2 i=2 i=2 i=2 Pr˚ umˇern´ y poˇcet porovn´ an´ı i pˇresun˚ u je tedy O n2 . 1 Algoritmus tˇ r´ıdˇen´ı pˇr´ım´ ym vkl´ ad´ an´ım lze naprogramovat tak, ˇze tyto zbyteˇcn´e pˇresuny odpadnou, v´ ysledn´ a procedura bude vˇsak sloˇzitˇejˇs´ı.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
68
Poznamenejme, ˇze metoda pˇr´ım´eho vkl´ ad´ an´ı je stabiln´ı v tom smyslu, ˇze prvky se stejnou hodnotou kl´ıˇce budou v c´ılov´e posloupnosti uloˇzeny ve stejn´em poˇrad´ı jako v posloupnosti zdrojov´e.
5.1.2
Tˇ r´ıdˇ en´ı bin´ arn´ım vkl´ ad´ an´ım
Bin´ arn´ı vkl´ ad´ an´ı pˇredstavuje jist´e vylepˇsen´ı pˇr´ım´eho vkl´ ad´ an´ı. Jestliˇze si uvˇedom´ıme, ˇze zvolen´ y prvek ukl´ ad´ ame do posloupnosti, kter´ a je jiˇz setˇr´ıdˇen´ a, m˚ uˇzeme zrychlit hled´ an´ı m´ısta, na kter´e patˇr´ı. Pˇritom nebudeme potˇrebovat zar´ aˇzku. M´ısto pro vkl´ adan´ y prvek budeme zjiˇst’ovat metodou bin´ arn´ıho vyhled´ av´ an´ı, kterou m˚ uˇzeme povaˇzovat za typick´ y pˇr´ıklad algoritmu vytvoˇren´eho podle modelu rozdˇel a panuj : c´ılovou posloupnost rozdˇel´ıme na dvˇe podposloupnosti a porovn´ an´ım zjist´ıme, do kter´e z nich vkl´ adan´ y prvek patˇr´ı. Opakov´ an´ım tohoto postupu nakonec urˇc´ıme m´ısto, na kter´e prvek vloˇz´ıme. Pˇresnˇe pop´ıˇseme tento algoritmus opˇet v Pascalu: procedure Bin´ arn´ ıVkl´ ad´ an´ ı; var i,j,k,l,r,m: index; x: prvek; begin for i := 2 to n do begin x := a[i]; l := 1; r := i-1; {l,r jsou meze podposloupnosti} while l <= r do begin {bin´ arn´ ı hled´ an´ ı} m := (l+r) div 2; {rozdˇ el´ ıme na poloviny} if x.kl´ ıc ˇ < a[m].kl´ ıc ˇ then r := m-1 else l := m+1 end;{while} for j := i-1 downto l do a[j+1] := a[j]; a[l] := x; end; {for i} end; Anal´ yza algoritmu bin´ arn´ıho vkl´ ad´ an´ı M´ısto, na kter´e uloˇz´ıme nov´ y prvek, je pops´ ano podm´ınkou a[j].kl´ ıc ˇ <= x.kl´ ıc ˇ <= a[j+1].kl´ ıc ˇ. Zkouman´ y interval d´elky i pˇritom mus´ıme rozdˇelit podle okolnost´ı bud’ [log 2 i]-kr´ at, kde [z ] oznaˇcuje celou ˇca ´st ˇc´ısla z, nebo ([log2 i] + 1)-kr´ at. To znamen´ a, ˇze celkov´ y poˇcet porovn´ an´ı C bude omezen souˇcty n X i=1
[log2 i] ≤ C ≤
n X
([log2 i] + 1)
Hodnotu souˇctu na lev´e stranˇe (5.4) m˚ uˇzeme pˇribliˇznˇe odhadnout pomoc´ı integr´ alu: Z n n X n [log2 i] ≈ log2 x dx = [x (log2 x − s)]1 = n (log2 n − s) + s, i=1
(5.4)
i=1
(5.5)
1
(5) kde s = log2 e = 1/ ln 2 = 1, 44269... . Pˇritom poˇcet porovn´ an´ı bude prakticky nez´ avisl´ y na uspoˇra ´d´ an´ı prvk˚ u zdrojov´e posloupnosti. Poˇcet pˇresun˚ u z˚ ustane ovˇsem v podstatˇe stejn´ y jako v pˇr´ıpadˇe pˇr´ım´eho vkl´ ad´ an´ı, odpadnou pouze operace se zar´ a ˇ z kou. To znamen´ a, ˇze poˇcet porovn´ an´ı bude podle (5.5) jen O (n log2 n), avˇsak poˇcet pˇresun˚ u z˚ ustane O n2 . Vzhledem k tomu, ˇze pˇresuny jsou zpravidla ˇcasovˇe n´ aroˇcnˇejˇs´ı neˇz porovn´ an´ı, nemus´ı j´ıt o u ´sporu nijak v´ yznamnou.
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
69
3
2
17
1
8
5
1
2
17
3
8
5
1
2
17
3
8
5
1
2
3
17
8
5
1
2
3
5
8
17
1
2
3
5
8
17
neutˇr´ıdˇen´e
plnˇe utˇr´ıdˇen´e
Obr. 5.2: Tˇr´ıdˇen´ı pˇr´ım´ ym v´ ybˇerem
5.1.3
Tˇ r´ıdˇ en´ı pˇ r´ım´ ym v´ ybˇ erem
V metodˇe pˇr´ım´eho i bin´ arn´ıho vkl´ ad´ an´ı jsme vˇzdy brali jeden pˇredem urˇcen´ y prvek zdrojov´e posloupnosti a ten jsme vkl´ adali do c´ılov´e posloupnosti (tj. prob´ırali jsme celou c´ılovou posloupnost). Metoda pˇr´ım´eho v´ ybˇeru je zaloˇzena na opaˇcn´em principu: ze zdrojov´e posloupnosti vybereme vhodn´ y prvek (uvaˇzujeme tedy vlastnˇe celou zdrojovou posloupnost) a ten vloˇz´ıme na pˇredem pˇresnˇe urˇcen´e m´ısto c´ılov´e posloupnosti. Algoritmus pˇr´ım´eho v´ ybˇeru bude zaloˇzen na n´ asleduj´ıc´ı myˇslence: 1. Najdeme ve zdrojov´e posloupnosti prvek s nejmenˇs´ım kl´ıˇcem. 2. Vymˇen´ıme ho s prvkem na prvn´ı pozici. 3. Nyn´ı je na prvn´ı pozici nejmenˇs´ı prvek z cel´e posloupnosti. Potˇrebujeme utˇr´ıdit zbytek posloupnosti, proto opakujeme tyto kroky se zbyl´ ymi n − 1 prvky, dokud je n > 1. Pˇ r´ıklad 5.2 ˇ Na obr´ azku 5.2 vid´ıme postup tˇr´ıdˇen´ı posloupnosti (3, 2, 17, 1, 8, 5) metodou pˇr´ım´eho v´ ybˇeru. Sipky naznaˇcuj´ı v´ ymˇeny. Poznamenejme, ˇze ve druh´em kroku nedojde k v´ ymˇenˇe prvk˚ u, nebot’ nejmenˇs´ı prvek ze zdrojov´e posloupnosti je pr´ avˇe na druh´em m´ıstˇe; podobnˇe nedojde k v´ ymˇenˇe v p´ at´em kroku. Procedura pro tˇr´ıdˇen´ı pˇr´ım´ ym v´ ybˇerem b´ yv´ a obvykle uv´ adˇena ve tvaru procedure Pˇ rı ´m´ yV´ ybˇ er; var i,j,k: index; x: prvek; begin for i := 1 to n-1 do begin {na prvn´ ı druh´ e,... m´ ısto} k := i; x := a[i]; {*} for j := i+1 to n do {najdi nejmenˇ sı ´} if a[j].kl´ ıc ˇ < x.kl´ ıc ˇ then begin k := j; x := a[j]; {**} end; a[k] := a[i]; {prohod’ ho s prvkem na i-t´ em m´ ıstˇ e} a[i] := x; {***} end; end; Ponˇekud v´ yhodnˇejˇs´ı m˚ uˇze b´ yt n´ asleduj´ıc´ı varianta t´eˇze procedury, ve kter´e uˇsetˇr´ıme nˇekolik pˇresun˚ u prvk˚ u: procedure Pˇ rı ´m´ yV´ ybˇ er1; var i,j,k: index;
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
70
x: prvek; begin for i := 1 to n-1 do begin {na prvn´ ı druh´ e,... m´ ısto} k := i; for j := i+1 to n do {najdi index nejmenˇ sı ´ho} if a[j].kl´ ıc ˇ < a[k].kl´ ıc ˇ then begin k := j; end; x := a[k]; {prohod’ nejmenˇ sı ´ s prvkem na i-t´ em m´ ıstˇ e} a[k] := a[i]; a[i] := x; end; end; Anal´ yza tˇ r´ıdˇ en´ı pˇ r´ım´ ym v´ ybˇ erem Pokud jde o poˇcet porovn´ an´ı kl´ıˇc˚ u, je tato metoda horˇs´ı neˇz pˇr´ım´ y v´ ybˇer. Poˇcet porovn´ an´ı totiˇz nez´ avis´ı na poˇca ´teˇcn´ım uspoˇra ´d´ an´ı prvk˚ u v poli, nebot’ v i-t´em kroku vˇzdy prohled´ av´ ame cel´e pole a od pozice i + 1 do konce. To znamen´ a, ˇze poˇcet porovn´ an´ı je 1 C = n (n − 1) . 2 Pokud jde o poˇcet pˇresun˚ u, z´ aleˇz´ı na tom, zda pouˇzijeme proceduru Pˇr´ım´yV´ybˇer nebo Pˇr´ım´yV´ybˇer1. V pˇr´ıpadˇe procedury Pˇr´ım´ yV´ ybˇer bude minim´ aln´ı poˇcet pˇresun˚ u Mmin = 3 (n − 1)
(5.6)
a to v pˇr´ıpadˇe, ˇze pole je jiˇz uspoˇra ´dan´e. Maxim´ aln´ı poˇcet pˇresun˚ u nastane v pˇr´ıpadˇe, ˇze pole je uspoˇra ´dan´e v opaˇcn´em poˇrad´ı. V tom pˇr´ıpadˇe se pˇri kaˇzd´em pr˚ uchodu vnˇejˇs´ım cyklem provedou pˇr´ıkazy oznaˇcen´e jednou a tˇremi hvˇezdiˇckami. Ty obsahuj´ı celkem 3 pˇresuny. Pˇriˇrazovac´ı pˇr´ıkaz, oznaˇcen´ y dvˇema hvˇezdiˇckami, se pˇri i = 1 provede (n − 1)-kr´ at, pˇri dalˇs´ım pr˚ uchodu (n − 2)-kr´ at, atd. (Pˇri prvn´ım pr˚ uchodu se na posledn´ı m´ısto uloˇzil nejvˇetˇs´ı prvek; ve druh´em pr˚ uchodu se zaˇc´ın´ a u druh´eho a skonˇc´ı u pˇredposledn´ıho prvku a pˇritom se na pˇredposledn´ı m´ısto uloˇz´ı druh´ y nejvˇetˇs´ı prvek atd.) Pro i > n/2 se jiˇz pˇr´ıkaz se dvˇema hvˇezdiˇckami neprov´ ad´ı, nebot’ pole je jiˇz srovn´ ano. Pˇri sˇc´ıt´ an´ı ˇrady n − 1 + n − 2 + . . . mus´ıme rozliˇsit nˇekolik pˇr´ıpad˚ u podle hodnoty n. Dostaneme, ˇze maxim´ aln´ı poˇcet pˇresun˚ u bude 2 n + 3 (n − 1) , (5.7) Mmax = 4 kde [z ] opˇet znamen´ a celou ˇca ´st ˇc´ısla z. Pˇri stanoven´ı pr˚ umˇern´eho poˇctu pˇresun˚ u se pˇredpokl´ ad´ a, ˇze vˇsechny moˇzn´e permutace prvk˚ u pole a jsou stejnˇe pravdˇepodobn´e. Lze uk´ azat, ˇze pr˚ umˇern´ y poˇcet pˇresun˚ u bude pˇribliˇznˇe roven n (ln n + γ), kde γ je Eulerova konstanta, γ = 0, 577 . . . Viz [1], str. 101. Pouˇzijeme-li proceduru Pˇr´ım´yV´ybˇer1, bude tˇreba v kaˇzd´em kroku 3 pˇresuny bez ohledu na uspoˇra ´d´ an´ı prvk˚ u, tedy celkem 3 (n − 1) pˇresun˚ u.
5.1.4
Bublinkov´ e tˇ r´ıdˇ en´ı a tˇ r´ıdˇ en´ı pˇ retˇ r´ as´ an´ım
Tyto dvˇe metody bychom mohli shrnout pod oznaˇcen´ı tˇr´ıdˇen´ı pˇr´ımou v´ ymˇenou“, nebot’ v´ ymˇeny prvku jsou ” jejich dominantn´ım rysem. Podobnˇe jako vˇetˇsina metod vnitˇrn´ıho tˇr´ıdˇen´ı jsou tyto dvˇe metody jsou zaloˇzeny na opakovan´em proch´ azen´ı polem. Pˇritom se vˇzdy porovn´ avaj´ı dva sousedn´ı prvky, a pokud nejsou uloˇzeny ve spr´ avn´em poˇrad´ı, prohod´ı se. Algoritmus bublinkov´eho tˇr´ıdˇen´ı (bubblesort) v nejjednoduˇsˇs´ı podobnˇe m˚ uˇzeme vyj´ adˇrit n´ asleduj´ıc´ı pascalskou procedurou: procedure Bubl´ an´ ı; var i,j: index; x: prvek; begin
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
71
z
i=2
44 55 12 42 94 18 6 67
3
4
5
6
7
8
6 44 55 12 42
6 12 44 55 18
6 12 18 42 44 55
6 12
42 94 67
6 12 18 42 44 55 67 94
6 12 18 42
94 18 67
6 12 18 44 55 42 67 94
67 94
44 55 67 94
18 42 44 55 67 94
Obr. 5.3: Bublinkov´e tˇr´ıdˇen´ı for i := 2 to n do begin {n-1 kr´ at projdeme polem} for j := n downto i do {pr˚ uchod polem} if a[j-1].kl´ ıc ˇ > a[j].kl´ ıc ˇ then begin x := a[j-1]; {pokud prvky nejsou} a[j-1] := a[j]; {ve spr´ avn´ em poˇ rad´ ı,} a[j] := x; {prohod´ ıme je} end; end; end;{bubl´ an´ ı} Jestliˇze pole proch´ az´ıme odzadu“, jako je tomu v proceduˇre Bubl´ an´ı, dostane se pˇri prvn´ım pr˚ uchodu nejmenˇs´ı ” prvek na prvn´ı m´ısto. Ve druh´em pr˚ uchodu uˇz nemus´ıme proch´ azet cel´e pole, staˇc´ı skonˇcit na druh´em m´ıstˇe, kam se pˇri druh´em pr˚ uchodu dostane druh´ y nejmenˇs´ı prvek; atd. Pˇ r´ıklad 5.3 Uk´ aˇzeme si efekt procedury Bubl´ an´ı pˇri tˇr´ıdˇen´ı posloupnosti (44, 55, 12, 42, 9, 18, 6, 67). Na obr´ azku 5.3 jsme ji zapsali svisle ve sloupci nadepsan´em Z. V dalˇs´ıch sloupc´ıch vid´ıme tˇr´ıdˇen´e pole pˇri jednotliv´ ych pr˚ uchodech cyklem s parametrem i. S trochou fantazie si m˚ uˇzeme pˇredstavovat, ˇze v prvn´ım pr˚ uchodu vyplave“ nejlehˇc´ı prvek na prvn´ı m´ısto ” (jako bublinka - odtud n´ azev), ve druh´em pr˚ uchodu druh´ y nejlehˇc´ı prvek atd. Na prvn´ı pohled je zˇrejm´e, ˇze algoritmus, popsan´ y procedurou Bubl´ an´ı, lze vylepˇsit. Tato procedura totiˇz nerozpozn´ a utˇr´ıdˇen´e pole, takˇze napˇr. posledn´ı tˇri pr˚ uchody pˇri tˇr´ıdˇen´ı posloupnosti, uveden´e v pˇr´ıkladu 5.3, byly naprosto zbyteˇcn´e. To m˚ uˇzeme napravit napˇr. tak, ˇze budeme pˇri kaˇzd´em pr˚ uchodu zaznamen´ avat poˇcet v´ ymˇen. Jestliˇze nedojde k ˇza ´dn´e v´ ymˇenˇe, je tˇr´ıdˇen´ı skonˇceno. Cyklus for s parametrem i nahrad´ıme cyklem repeat s podm´ınkou v minul´em pr˚ uchodu doˇslo k alespoˇ n jedn´e v´ ymˇenˇe“. ” Dalˇs´ıho vylepˇsen´ı m˚ uˇzeme dos´ ahnout t´ım , ˇze si zapamatujeme i index prvku, kter´eho se posledn´ı v´ ymˇena t´ ykala. Za t´ımto prvkem jiˇz nebyly ˇza ´dn´e dalˇs´ı v´ ymˇeny, takˇze prvky zde jsou jiˇz uspoˇra ´d´ any, a proto tento u ´sek jiˇz pˇr´ıˇstˇe nemus´ıme proch´ azet. Vedle toho je zˇrejm´ a nesymetrie algoritmu bublinkov´eho tˇr´ıdˇen´ı: na obr´ azku 5.3 je vidˇet, ˇze nejmenˇs´ı prvek (zde 6) vybubl´ a“ na sv´e m´ısto ihned pˇri prvn´ım pr˚ uchodu, i kdyˇz byl p˚ uvodnˇe na pˇredposledn´ı pozici, zat´ımco ” nejvˇetˇs´ı prvek (94) kles´ a ke dnu“ velmi pomalu, pˇri kaˇzd´em pr˚ uchodu jen o jednu pozici. ” Kdybychom proch´ azeli tˇr´ıdˇenou posloupnost v obr´ acen´em poˇrad´ı, tj. od prvn´ıho k posledn´ımu prvku, chovaly by se prvky obr´ acenˇe. Nejvˇetˇs´ı prvek by se dostal na sv´e m´ısto ihned, zat´ımco nejmenˇs´ı by postupoval pomalu. Odtud lze usuzovat, ˇze bychom mohli dos´ ahnout dalˇs´ıho vylepˇsen´ı, kdybychom pravidelnˇe stˇr´ıdali smˇery pr˚ uchodu.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
72
Tomuto vylepˇsen´ı algoritmu bublinkov´eho tˇr´ıdˇen´ı se ˇr´ık´ a tˇr´ıdˇen´ı pˇretˇra ´s´ an´ım (shakesort). jeho algoritmus m˚ uˇzeme v Pascalu zapsat ve tvaru n´ asleduj´ıc´ı procedury: procedure Tˇ resen´ ı; var j,k,l,r : index; x: prvek; begin l := 2; r := n; k := n; repeat for j := r downto l do {pr˚ uchod od konce} if a[j-1].kl´ ıc ˇ > a[j].kl´ ıc ˇ then begin x := a[j-1]; {v´ ymˇ ena} a[j-1] := a[j]; a[j] := x; k := j; {index posledn´ ı v´ ymˇ eny} end; l := k+1; {pˇ rı ´s ˇtˇ e proch´ az´ ıme jen od l} for j := l to r do if a[j-1].kl´ ıc ˇ > a[j].kl´ ıc ˇ then begin x := a[j-1]; {v´ ymˇ ena} a[j-1] := a[j]; a[j] := x; k := j; {index posledn´ ı v´ ymˇ eny} end; r := k-1; {pˇ rı ´s ˇtˇ e proch´ az´ ıme jen od r} until l > r; {kdyˇ z se l a r se setkaj´ ı, konec} end; {Tˇ resen´ ı} Anal´ yza bublinkov´ eho tˇ r´ıdˇ en´ı a tˇ r´ıdˇ en´ı pˇ retˇ r´ as´ an´ım Poˇcet porovn´ an´ı pˇri bublinkov´em tˇr´ıdˇen´ı v nejjednoduˇsˇs´ı podobˇe nez´ avis´ı na uspoˇra ´d´ an´ı pole a je vˇzdy roven
C=
n−1 X i=1
(n − i) =
1 2 n −n . 2
(5.8)
Minim´ aln´ı poˇcet pˇresun˚ u je zˇrejmˇe Mmin = 0, nebot’ pokud je pole jiˇz uspoˇra ´dan´e, nebude nikdy splnˇena podm´ınka v pˇr´ıkazu if v proceduˇre Bubl´ an´ı. Maxim´ aln´ı poˇcet pˇresun˚ u dostaneme pro pole uspoˇra ´dan´e obr´ acenˇe. V tomto pˇr´ıpadˇe bude doch´ azet k v´ ymˇenˇe prvk˚ u pˇri kaˇzd´em porovn´ an´ı v kaˇzd´em z pr˚ uchod˚ u; vezmeme-li v u ´vahu, ˇze na jednu v´ ymˇenu prvk˚ u potˇrebujeme 3 pˇresuny, dostaneme podobnˇe jako ve vztahu (5.8) 3 2 n −n . (5.9) Mmax = 2 Pr˚ umˇern´ y poˇcet pˇresun˚ u bychom pak mohli odhadnout hodnotou 3 2 M = n −n . 4
Pod´ıvejme se nyn´ı na algoritmus tˇr´ıdˇen´ı pˇretˇra ´s´ an´ım, popsan´ y procedurou Tˇresen´ı. Je-li tˇr´ıdˇen´e pole jiˇz uspoˇra ´dan´e, bude m´ıt promˇenn´ a k po skonˇcen´ı prvn´ıho cyklu for hodnotu n, nebot’ podm´ınka v pˇr´ıkazu if nebude splnˇena ani jednou. Promˇenn´ a l t´ım z´ısk´ a hodnotu n + 1 a tˇelo druh´eho cyklu for se neprovede jiˇz ani jednou. Promˇenn´ a r pak dostane hodnotu n − 1, cyklus repeat skonˇc´ı. Nejmenˇs´ı poˇcet porovn´ an´ı proto bude Cmin = n − 1. Pokud jde o pr˚ umˇern´ y poˇcet v´ ymˇen, D. Knuth uk´ azal, ˇze je roven 1 2 n − n (r + ln n) , C = 2
kde r je jist´ a konstanta [1]. Poˇcet v´ ymˇen z˚ ust´ av´ a u tˇr´ıdˇen´ı pˇretˇra ´s´ an´ım stejn´ y jako u bublinkov´eho tˇr´ıdˇen´ı. To znamen´ a, ˇze ve vˇetˇsinˇe pˇr´ıpad˚ u nebudou m´ıt uveden´ a vylepˇsen´ı metody bublinkov´eho tˇr´ıdˇen´ı velk´ y v´ yznam.
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
5.1.5
73
Shellovo tˇ r´ıdˇ en´ı (tˇ r´ıdˇ en´ı se zmenˇ sov´ an´ım kroku)
Pˇr´ıˇcinou neefektivnosti nˇekter´ ych z pˇredchoz´ıch algoritm˚ u, napˇr. tˇr´ıdˇen´ı pˇr´ım´ ym vkl´ ad´ an´ım nebo bublinkov´eho tˇr´ıdˇen´ı, je, ˇze v nich vymˇen ˇujeme pouze sousedn´ı prvky. Pokud je napˇr´ıklad nejmenˇs´ı prvek uloˇzen na konci pole, potˇrebujeme n v´ ymˇen, aby se dostal na spr´ avn´e m´ısto. Proto se nˇekter´e algoritmy snaˇz´ı preferovat v´ ymˇeny prvk˚ u na velk´e vzd´ alenosti“. Jedn´ım z takov´ ych algoritm˚ u je i Shellovo tˇr´ıdˇen´ı (Shellsort), navrˇzen´e D. L. ” Shellem. Z´ akladn´ı myˇslenkou Shellova tˇr´ıdˇen´ı je pˇreuspoˇra ´dat pole tak, ˇze kdyˇz vezmeme kaˇzd´ y h-t´ y prvek, dostaneme setˇr´ıdˇen´e pole (takov´eto pole se naz´ yv´ a setˇr´ıdˇen´e s krokem h). Pole setˇr´ıdˇen´e s krokem h pˇredstavuje vlastnˇe h proloˇzen´ ych nez´ avisl´ ych pol´ı. Pˇri tˇr´ıdˇen´ı pole s krokem h m˚ uˇzeme pˇresunovat prvky na vˇetˇs´ı vzd´ alenosti (nejm´enˇe h) a tak dos´ ahnout menˇs´ıho poˇctu v´ ymˇen pˇri tˇr´ıdˇen´ı s menˇs´ım krokem. Pˇri Shellovˇe tˇr´ıdˇen´ı tedy utˇr´ıd´ıme pole s krokem h 1 , pak s krokem h2 < h1 atd.; nakonec je utˇr´ıd´ıme s krokem 1 a dostaneme plnˇe utˇr´ıdˇen´e pole. Pˇri kaˇzd´em z n´ asleduj´ıc´ıch pr˚ uchod˚ u tˇr´ıd´ıme pole, kter´e je jiˇz ˇca ´steˇcnˇe setˇr´ıdˇen´e, a proto m˚ uˇzeme oˇcek´ avat, ˇze bude tˇreba jen m´ alo v´ ymˇen. Dosud jsme neurˇcili, jak utˇr´ıd´ıme pole s krokem h. Pouˇzijeme napˇr. metody pˇr´ım´eho vkl´ ad´ an´ı. V odd´ılu 5.1.1. jsme pˇri formulaci algoritmu pˇr´ım´eho vkl´ ad´ an´ı pouˇzili zar´ aˇzku, abychom zjednoduˇsili podm´ınku pro ukonˇcen´ı pr˚ uchodu polem. Budeme-li cht´ıt pouˇz´ıt zar´ aˇzek i v Shellovˇe tˇr´ıdˇen´ı, mus´ıme jich zav´est v´ıce. Je-li h t nejvˇetˇs´ı z krok˚ u, se kter´ ymi dan´e pole a tˇr´ıd´ıme, mus´ıme je deklarovat pˇr´ıkazem var a: array [-ht..n] of prvek; ˇ aˇr jistˇe Pro vˇetˇs´ı hodnoty ht to nemus´ı b´ yt v´ yhodn´e. V n´ asleduj´ıc´ı proceduˇre Shell zar´ aˇzky nepouˇzijeme. Cten´ dok´ aˇze napsat proceduru, vyuˇz´ıvaj´ıc´ı zar´ aˇzek s´ am (viz t´eˇz [1], str. 107). V t´eto proceduˇre pˇredpokl´ ad´ ame, ˇze t je glob´ aln´ı konstanta, ud´ avaj´ıc´ı poˇcet r˚ uzn´ ych krok˚ u pro tˇr´ıdˇen´ı. procedure Shell; var i,j,k: index; x: prvek; m: 1..t; h: array [1..t] of integer; {pole d´ elek krok˚ u} begin h[t] := 1; {v´ ypoˇ cet posloupnosti krok˚ u} for i := t-1 downto 1 do h[i] := 3*h[i+1]+1; for m := 1 to t do begin{tˇ rı ´dˇ en´ ı s krokem h[m]} k := h[m]; {krok} for i := k+1 to n do begin {pˇ rı ´m´ e vkl´ ad´ an´ ı} x := a[i]; j := i-k; while (x.kl´ ıc ˇ < a[j].kl´ ıc ˇ) and (j >= k) do begin a[j+k] := a[j]; {bez zar´ az ˇky} j := j-k; end; a[j+k] := x; end; end; end; Pˇ r´ıklad 5.4 Pod´ıvejme se opˇet na posloupnost (44, 55, 12, 42, 9, 18, 6, 67). Zkus´ıme ji utˇr´ıdit pomoc´ı Shellova algoritmu; pˇritom zvol´ıme t = 3 a kroky h1 = 4, h2 = 2 a h3 = 1. Jak se d´ ale dozv´ıme, nejde o pˇr´ıliˇs v´ yhodnou volbu; pro n´ aˇs pˇr´ıklad ale postaˇc´ı. Anal´ yza Shellova tˇ r´ıdˇ en´ı Anal´ yza Shellova tˇr´ıdˇen´ı vede ke komplikovan´ ym matematick´ ym probl´em˚ um, kter´e pˇresahuj´ı r´ amec tohoto textu. Uvedeme proto pouze nˇekter´e v´ ysledky; podrobnˇejˇs´ı informace najde ˇcten´ aˇr napˇr. v [5].
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
74
Zdrojov´a posloupnost: 44
55
12
42
94
18
6
67
55
12
67
55
94
67
67
94
Po prvn´ım pr˚ uchodu s krokem 4: 44
18
6
42
94
Po druh´em pr˚ uchodu s krokem 2: 6
18
12
42
44
V´ ysledek po tˇret´ım pr˚ uchodu s krokem 1: 6
12
18
42
44
55
Obr. 5.4: Shellovo tˇr´ıdˇen´ı (tˇr´ıdˇen´ı se zmenˇsov´ an´ım kroku)
Prvn´ı z probl´em˚ u, o kter´ ych se zm´ın´ıme, je volba posloupnosti krok˚ u h i . Ot´ azka optim´ aln´ı volby nen´ı, jak se zd´ a, dosud uspokojivˇe vyˇreˇsena. Je vˇsak zn´ amo, ˇze obecnˇe horˇs´ı v´ ysledky dostaneme, jestliˇze bude h i n´ asobkem hi−1 . Napˇr. posloupnost . . . , 8, 4, 2, 1 nevede k pˇr´ıliˇs efektivn´ımu tˇr´ıdˇen´ı, nebot’ pˇri t´eto volbˇe porovn´ ame prvky na sud´ ych m´ıstech s prvky na lich´ ych m´ıstech aˇz v posledn´ım pr˚ uchodu. Na druh´e stranˇe je zn´ amo, ˇze pomˇernˇe dobr´e v´ ysledky dostaneme pro posloupnost krok˚ u definovanou vztahy ht = 1, kk−1 = 3hk + 1; t vol´ıme podle vztahu t = [log3 n] − 1, kde [z] opˇet oznaˇcuje celou ˇca ´st ˇc´ısla z. Posledn´ı prvky t´eto posloupnosti jsou 1, 4, 13, 40, 121, . . .. Lze dok´ azat, ˇze pro tuto volbu krok˚ u nepˇres´ ahne poˇcet porovn´ an´ı hodnotu n 3/2 [5]. Jin´ a doporuˇcovan´ a posloupnost m´ a tvar ht = 1, hk−1 = 2hk + 1; v tomto pˇr´ıpadˇe urˇc´ıme t ze vztahu t = [log2 n] − 1. Tato posloupnost konˇc´ı ˇc´ısly 1, 3, 7, 15, 31, . . . . Celkov´ a sloˇzitost Shellova algoritmu v tomto pˇr´ıpadˇe je O n1,2 . [1]
5.1.6
Stromov´ e tˇ r´ıdˇ en´ı a tˇ r´ıdˇ en´ı haldou
Pˇredchoz´ı metody byly zaloˇzeny za opakovan´em prohled´ av´ an´ı pole - nejprve vˇsech n prvk˚ u, potom u n − 1 prvk˚ atd. Pokud se n´ am nepodaˇr´ı sn´ıˇzit poˇcet prohled´ av´ an´ı, bude poˇcet porovn´ an´ı vˇzdy ˇra ´du O n2 . Pˇritom by se mohlo zd´ at, ˇze staˇc´ı pouh´e jedno prohled´ an´ı pole, nebot’ pˇri nˇem uˇz z´ısk´ ame vˇsechny potˇrebn´e informace. Je zˇrejm´e, ˇze vˇsechny pˇredchoz´ı metody informac´ı pˇr´ıliˇs optim´ alnˇe nevyuˇz´ıvaly. Pokus´ıme se tedy navrhnout metodu, kter´ a bude vyuˇz´ıvat informac´ı ponˇekud efektivnˇeji. Rozdˇel´ıme-li prvky v poli na dvojice, m˚ uˇzeme pomoc´ı n/2 porovn´ an´ı urˇcit menˇs´ı prvek z kaˇzd´e dvojice. Pomoc´ı dalˇs´ıch n/4 porovn´ an´ı urˇc´ıme menˇs´ı prvek z kaˇzd´e ˇctveˇrice (staˇc´ı porovnat menˇs´ı prvky z kaˇzd´e dvojice) atd. Takto m˚ uˇzeme pomoc´ı n − 1 porovn´ an´ı sestrojit porovn´ avac´ı strom, jehoˇz koˇren obsahuje nejmenˇs´ı prvek cel´eho pole a koˇren kaˇzd´eho podstromu obsahuje nejmenˇs´ı prvek tohoto podstromu. Pˇ r´ıklad 5.5 Provn´ avac´ı strom, zkonstruovan´ y z posloupnosti (6, 11, 13, 1, 8, 17, 9, 30) vid´ıme na obr. 5.5. Nyn´ı z cel´eho stromu odstran´ıme nejmenˇs´ı prvek. List, kter´ y tuto hodnotu obsahoval, nahrad´ıme vrcholem s kl´ıˇcem +∞, a ostatn´ı vrcholy porovn´ avac´ıho stromu, kter´e obsahovaly nejmenˇs´ı hodnotu, nahrad´ıme vˇzdy
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
75
1 1
8
6
6
1
1
13
11
8
8
9
9
17
30
Obr. 5.5: Porovn´ avac´ı strom
6 6
8
6
6
8
13
11
13
∞
8
9
17
9
30
Obr. 5.6: Porovn´ avac´ı strom z obr. 5.5 po odstranˇen´ı nejmenˇs´ıho prvku druh´ ym z dvojice vrchol˚ u, ve kter´e se tato hodnota vyskytovala. Nyn´ı m˚ uˇzeme odstranit druh´ y nejmenˇs´ı prvek atd. Odstranˇen´e hodnoty tvoˇr´ı posloupnost seˇrazenou podle velikosti. Stromov´e tˇr´ıdˇen´ı skonˇc´ı v okamˇziku, kdy vrcholu pˇriˇrad´ıme hodnotu +∞. Pˇ r´ıklad 5.5 (pokraˇ cov´ an´ı) V porovn´ avac´ım stromu na obr. 5.5 je nejmenˇs´ı hodnota 1. List, kter´ y ji obsahoval, nahrad´ıme listem s hodnotou +∞. Pˇredch˚ udce tohoto listu bude obsahovat hodnotu 13, pˇredch˚ udce na u ´rovni 2 bude obsahovat hodnotu 6 a koˇren pak taky 6. Strom, kter´ y vznikne touto u ´pravou, vid´ıme na obr. 5.6. V n´ asleduj´ıc´ım kroku odstran´ıme hodnotu 6 a do koˇrene se dostane 8. Dalˇs´ı kroky jistˇe zvl´ adne ˇcten´ aˇr s´ am. Na sestrojen´ı stromu jsme potˇrebovali n krok˚ u; na jednotliv´e v´ ybˇery potˇrebujeme log 2 n krok˚ u (poˇcet tˇechto krok˚ u je roven poˇctu u ´rovn´ı stromu). Protoˇze v´ ybˇer˚ u ze stromu je celkem n, dost´ av´ ame, ˇze stromov´e tˇr´ıdˇen´ ı potˇrebuje O (n log2 n) krok˚ u. Pro velk´ a n tedy bude v´ yhodnˇejˇs´ı neˇz Shellovo tˇr´ıdˇen´ı, kter´e vyˇzaduje O n1,2 krok˚ u. Pod´ıvejme se nyn´ı na nev´ yhody stromov´eho tˇr´ıdˇen´ı. Tento algoritmus potˇrebuje celkem 2n − 1 pamˇet’ov´ ych m´ıst, zat´ımco vˇsechny pˇredchoz´ı metody potˇrebovaly pouze n m´ıst. Nav´ıc v z´ avˇeru stromov´eho tˇr´ıdˇen´ı se strom zapln´ı vrcholy s hodnotou +∞, kter´e se naprosto zbyteˇcnˇe porovn´ avaj´ı. Obˇe tyto nev´ yhody se pokouˇs´ı odstranit metoda tˇr´ıdˇen´ı haldou (heapsort), kterou navrhl J. Williams [23]. Halda je zde vlastnˇe bin´ arn´ı strom, v nˇemˇz jsou uloˇzen´ a data jist´ ym zp˚ usobem uspoˇra ´d´ ana a kter´ y je uloˇzen v poli. Form´ aln´ı definice haldy zn´ı: Halda je posloupnost prvk˚ u hl , hl+1 , . . . , hr takov´ ych, ˇze pro vˇsechny indexy i = l, . . . , r/2 plat´ı nerovnosti hi ≤ h2i , hi ≤ h2i+1 (5.10) Uvaˇzujme haldu h1 , h2 , . . . , hn . Pak je h1 koˇrenem stromu a h2 a h3 jsou jeho lev´ y a prav´ y n´ asledovn´ık. Vrchol h2 m´ a n´ asledovn´ıky h4 a h5 , vrchol h3 m´ a n´ asledovn´ıky h6 a h7 . Viz t´eˇz obr. 5.7 Vezmˇeme nyn´ı prvky al , . . . , an tˇr´ıdˇen´eho pole a. Je-li l ≥ n/2, tvoˇr´ı podle definice haldu, nebot’ ve tˇr´ıdˇen´em poli pro ˇza ´dn´ y index i = n/2 . . . n neexistuje a2i nebo a2i+1 . Tyto prvky pˇredstavuj´ı vlastnˇe nejniˇzˇs´ı u ´roveˇ n bin´ arn´ıho stromu. Abychom toho mohli vyuˇz´ıt, potˇrebujeme uk´ azat, jak pˇridat do haldy nov´ y prvek.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
76
h1
h2
h3
h4 h8
h5 h9
h10
h6 h11
h12
h7 h13
h14
h15
Obr. 5.7: Halda z patn´ acti prvk˚ u, uloˇzen´ ych v poli h1 , h2 , . . . , h15
30
8
h1 8 29
33
31
39
32
29
22
(a)
88
30
55
24
34
28
22
(b)
77
99
33
31
39
32
55
24
34
88
28
77
99
Obr. 5.8: (a) Pˇrid´ av´ an´ı prvku s hodnotou 30 do haldy; (b) v´ ysledek Pˇridejme k haldˇe al+j , . . . , an prvek al . Novˇe pˇridan´ y prvek v posloupnosti al , . . . , an je na m´ıstˇe koˇrene (nemus´ı j´ıt o koˇren cel´eho stromu, ale nˇejak´eho podstromu). Pod´ıv´ ame se, zda pro nˇej plat´ı podm´ınky (5.10). Pokud ano, je vˇse v poˇra ´dku a posloupnost al , . . . , an st´ ale tvoˇr´ı haldu. Pokud ne, mus´ıme tento prvek prohodit s menˇs´ım z jeho n´ asledovn´ık˚ u tak, aby byly podm´ınky (5.10) splnˇeny. Nyn´ı opˇet zkontrolujeme, zda plat´ı podm´ınky (5.10); pokud ne, mus´ıme novˇe vloˇzen´ y prvek opˇet zamˇenit s nˇekter´ ym z n´ asledovn´ık˚ u atd. Novˇe vloˇzen´ y prvek tedy posunujeme z p˚ uvodn´ı polohy v koˇreni stromu na niˇzˇs´ı u ´rovnˇe tak, aby platily podm´ınky (5.10), kter´e definuj´ı haldu. Pˇ r´ıklad 5.6 Uvaˇzujme haldu z obr. 5.8 (a), kterou tvoˇr´ı vlastnˇe dva samostatn´e podstromy. Do t´eto haldy chceme pˇridat prvek s hodnotou 30. Nejprve jej vloˇz´ıme na pozici h1 , takˇze se stane koˇrenem stromu. Protoˇze ale nesplˇ nuje podm´ınky (5.10), mus´ıme jej prohodit s menˇs´ım z jeho n´ asledovn´ık˚ u , kter´ y m´ a hodnotu 8. Ani zde nejsou podm´ınky (5.10) splnˇeny, proto jej opˇet prohod´ıme s menˇs´ım z n´ asledovn´ık˚ u (m´ a hodnotu 29). V´ ysledek vid´ıme na obr. 5.8 (b). Jakmile um´ıme pˇridat do haldy nov´ y prvek, um´ıme vytvoˇrit haldu z prvk˚ u cel´eho pole: 1. Vyjdeme od prvk˚ u al , . . . , an tˇr´ıdˇen´eho pole a pro l = [n/2] + 1; tyto prvky tvoˇr´ı haldu. 2. K ˇca ´sti haldy, kter´ a je jiˇz hotov´ a, postupnˇe pˇrid´ ame prvky l − 1, l − 2, . . . , 1. Proceduru, kter´ a pˇrid´ a jeden prvek do haldy, nazveme v´ ystiˇznˇe VytvoˇrHaldu, nebot’ s jej´ı pomoc´ı haldu opravdu vytvoˇr´ıme. procedure Vytvoˇ rHaldu(l,r: integer); label 13; var i,j: index; x: prvek;
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
77
begin i := l; j := 2*i; x := a[i]; {pˇ rid´ avan´ y prvek} while j <= r do begin {V tomto cyklu se x zaˇ rad´ ı} if j < r then {Porovnat s menˇ sı ´m z} if a[j].kl´ ıc ˇ > a[j+1].kl´ ıc ˇ then inc(j); {n´ asledovn´ ık˚u} if x.kl´ ıc ˇ <= a[j].kl´ ıc ˇ then goto 13; {x je vˇ etˇ sı ´ - konec} a[i] := a[j]; i := j; j := 2*i; end; 13: a[i] := x; end;{vytvoˇ r haldu} Haldu vytvoˇr´ıme opakovan´ ym vol´ an´ım t´eto procedury pro l = [n/2] , [n/2] − 1, . . . , 1. Ve vytvoˇren´e haldˇe bude na prvn´ım m´ıstˇe nejmenˇs´ı prvek ze tˇr´ıdˇen´eho pole; dalˇs´ı prvky vˇsak jiˇz seˇrazen´e nejsou. Proto budeme cel´ y postup opakovat pro prvky a2 , . . . , an : vytvoˇr´ıme z nich haldu, kter´ a bude m´ıt na prvn´ım m´ıstˇe (tj. ve druh´e pozici tˇr´ıdˇen´eho pole) nejmenˇs´ı hodnotu zbyl´e ˇca ´sti posloupnosti. V dalˇs´ım kroku pak vytvoˇr´ıme haldu z prvk˚ u a3 , . . . , an atd. Zde ovˇsem vytv´ aˇr´ıme haldu z n prvk˚ u, pak haldu z n−1 prvk˚ u atd. V´ yhodnˇejˇs´ı bude, jestliˇze po prvn´ım kroku, po vytvoˇren´ı haldy z n prvk˚ u, uklid´ıme nalezen´ y nejmenˇs´ı prvek na konec pole, tj. vymˇen´ıme prvn´ı prvek s posledn´ım. Snadno se pˇresvˇedˇc´ıme, ˇze pokud prvky a1 , . . . , an tvoˇrily haldu, tvoˇr´ı ji i prvky a2 , . . . , an−1 . Proto nyn´ı bude staˇcit pˇridat k t´eto haldˇe an a dostaneme haldu tvoˇrenou a 2 , . . . , an . Druh´ y nejmenˇs´ı prvek pole, kter´ y bude v koˇreni t´eto haldy, opˇet uklid´ıme na konec“, vymˇen´ıme ho s pˇredposledn´ım prvkem pole atd. ” Pˇ r´ıklad 5.7 Uvaˇ zujme posloupnost (3, 5, 8, 2, 1, 4, 7, 6) . Z´ akladem haldy budou prvky s indexy 5 - 8. k nim postupnˇe pˇrid´ ame prvky s indexy 4, 3, 2 a 1 a vytvoˇr´ıme tak haldu (1, 2, 4, 3, 5, 8, 7, 6) . Nyn´ı uklid´ıme“ nalezen´ y nejmenˇs´ı prvek na konec pole, tj. prohod´ıme ho s posledn´ım prvkem. Tak dostaneme ” posloupnost (6, 2, 4, 3, 5, 8, 7, 1) . Protoˇze prvky 2, 4, 3, 5, 8, 7 tvoˇr´ı haldu, staˇc´ı k n´ı pˇridat prvek 6. (Nyn´ı vytv´ aˇr´ıme samozˇrejmˇe haldu pouze z prvn´ıch n − 1 prvk˚ u.). Po zaˇrazen´ı prvku 6 do haldy pomoc´ı procedury VytvoˇrHaldu dostaneme posloupnost (2, 3, 4, 6, 5, 8, 7, 1) . Ve zbyl´e posloupnosti je tedy nejmenˇs´ı hodnota 2. To opˇet uklid´ıme“ na konec, vymˇen´ıme ji za pˇredposledn´ı ” prvek a dostaneme (7, 3, 4, 6, 5, 8, 2, 1) . Je tedy tˇreba pˇridat k haldˇe tvoˇren´e prvky a [2] . . . a [6] prvek 7... atd. V´ ysledkem bude pole, seˇrazen´e obr´ acenˇe: (8, 7, 6, 5, 4, 3, 2, 1) . Popsan´ y postup vede k poli, seˇrazen´em v obr´ acen´em poˇrad´ı. Pokud si pˇrejeme pole, seˇrazen´e od nejmenˇs´ıho prvku k nejvˇetˇs´ımu (jako ve vˇsech pˇredchoz´ıch metod´ ach), staˇc´ı prostˇe obr´ atit nerovnosti v proceduˇre VytvoˇrHaldu (tedy vlastnˇe obr´ atit nerovnosti v definici haldy ve vztaz´ıch (5.10)). Vyloˇzen´ y postup zachycuje procedura Tˇr´ıdˇen´ıHaldou. Jej´ım j´ adrem je procedura VytvoˇrHaldu se zmˇenˇen´ ymi nerovnostmi. procedure Vytvoˇ rHaldu(l,r: integer); label 13; var i,j: index; x: prvek; begin
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
78
i := l; j := 2*i; x := a[i]; while j <= r do begin {Zaˇ rad´ ı x na m´ ısto} if j < r then {Porovn´ an´ ı s vˇ etˇ sı ´m} if a[j].kl´ ıc ˇ < a[j+1].kl´ ıc ˇ then inc(j); {prvkem} if x.kl´ ıc ˇ >= a[j].kl´ ıc ˇ then goto 13; {Jsou-li n´ asl. menˇ sı ´} a[i] := a[j]; i := j; j := 2*i; {tak konec} end; 13: a[i] := x; end;{vytvoˇ r haldu} procedure Tˇ rı ´dˇ en´ ıHaldou; var l, r: index; x: prvek; begin l := (n div 2) + 1; r := n; while l > 1 do begin dec(l); Vytvoˇ rHaldu(l,r) end; while r > 1 do begin x := a[1]; a[1] := a[r]; a[r] := x; dec(r); Vytvoˇ rHaldu(1,r) end; end;{tˇ rı ´dˇ en´ ı haldou}
{Prvky s indexy l..r} {jiˇ z tvoˇ rı ´ haldu} {Pˇ rid´ av´ an´ ı prvk˚ u k haldˇ e}
{Nalezen´ y nejmenˇ sı ´ prvek dej} {na konec pole} {a ze zbytku zase} {vytvoˇ r haldu}
Anal´ yza tˇ r´ıdˇ en´ı haldou Proceduru VytvoˇrHaldu budeme volat pˇri poˇca ´teˇcn´ım vytvoˇren´ı haldy n/2-kr´ at. Poˇcet m´ıst, pˇres kter´e se budou pˇri jednotliv´ ych vol´ an´ıch pˇrem´ıst’ovat vkl´ adan´e prvky, bude postupnˇe [log 2 (n/2)] , [log2 (1 + n/2)] , . . . , [log2 (n − 1)]. Celkem jde tedy o m´enˇe neˇz [(n/2) log 2 n] pˇresun˚ u pˇri konstrukci haldy. Vlastn´ı tˇr´ıdˇen´ı (cyklus while r > 1 v proceduˇre Tˇr´ıdˇen´ıHaldou) vyˇzaduje n − 1 pr˚ uchod˚ u. Jednotliv´ a vol´ an´ı procedury VytvoˇrHaldu v tomto cyklu vyˇzaduj´ı nejv´ yˇse [log 2 (n − 1)] , [log2 (n − 2)] , . . . , 1 pˇresun˚ u; celkem p˚ ujde pˇribliˇznˇe o (n − 1) (log2 (n − 1) − s) pˇresun˚ u, kde s = log2 e = 1, 44269 . . . (viz vztah (5.5) v odd´ılu o tˇr´ıdˇen´ı bin´ arn´ım vkl´ ad´ an´ım). Vedle toho potˇrebujeme pˇri kaˇzd´em pr˚ uchodu t´ımto cyklem 3 pˇresuny na pˇrem´ıstˇen´ı nalezen´eho prvku na konec pole, tedy celkem dalˇs´ıch 3 (n − 1) pˇresun˚ u. Z toho plyne, ˇze v celkov´ y poˇcet pˇresun˚ u je O (n log2 n). To je v´ ysledek lepˇs´ı neˇz u Shellova algoritmu. Na druh´e stranˇe je prvn´ı pohled je zˇrejm´e, ˇze jednotliv´e kroky tˇr´ıdˇen´ı haldou jsou sloˇzitˇejˇs´ı neˇz tomu bylo u pˇredchoz´ıch metod. Napˇr. nejvˇetˇs´ı prvek se po vytvoˇren´ı haldy dostane na prvn´ı m´ısto a teprve pak jej odsuneme na spr´ avn´e m´ısto na konci pole. Z toho plyne, ˇze tˇr´ıdˇen´ı haldou bude v´ yhodn´e zejm´ena pro rozs´ ahl´ a pole, kde se v´ yraznˇe uplatn´ı mal´ y poˇcet operac´ı.
5.1.7
Rychl´ e tˇ r´ıdˇ en´ı (quicksort)
Nejˇcastˇejˇs´ı oznaˇcen´ı, pod kter´ ym se s t´ımto algoritmem setk´ ame, je quicksort. Vedle ˇcesk´eho pˇrekladu rychl´e tˇr´ıdˇen´ı se tak´e setk´ ame s oznaˇcen´ım tˇr´ıdˇen´ı rozdˇelov´ an´ım. Algoritmus pro rychl´e vnitˇrn´ı tˇr´ıdˇen´ı publikoval Hoare [24] a jak se bystr´ y ˇcten´ aˇr podle n´ azvu jistˇe dovt´ıpil, je (alespoˇ n ve vˇetˇsinˇe pˇr´ıpad˚ u) velmi rychl´ y. Myˇslenka tohoto algoritmu vych´ az´ı ze skuteˇcnosti, ˇze nejefektivnˇejˇs´ı jsou v´ ymˇeny prvk˚ u v poli na velk´e vzd´ alenosti. Jestliˇze napˇr. vezmeme pole s n prvky seˇrazen´ ymi v obr´ acen´em poˇrad´ı, m˚ uˇzeme je utˇr´ıdit po-
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
79
moc´ı pouh´ ych n/2 v´ ymˇen. Porovn´ ame prvky na obou konc´ıch pole a zamˇen´ıme je; pak postoup´ıme o jedno pole smˇerem ke stˇredu a zopakujeme porovn´ an´ı. Pokud pole nen´ı seˇrazen´e v obr´ acen´em poˇrad´ı, nem˚ uˇze b´ yt n´ aˇs postup tak pˇr´ımoˇcar´ y. Vyjdeme z principu rozdˇel a panuj , o kter´em jsme hovoˇrili v kap. 3.1. Zvol´ıme n´ ahodnˇe prvek x a uspoˇra ´d´ ame pole a tak, aby vlevo od x leˇzely prvky s kl´ıˇci menˇs´ımi neˇz je kl´ıˇc x a vpravo prvky s kl´ıˇci vˇetˇs´ımi neˇz je kl´ıˇc x. T´ım jsme pole rozdˇelili na tˇri ˇca ´sti. Prvek x je jiˇz na sv´em m´ıstˇe, vlevo od nˇej jsou prvky menˇs´ı a vpravo od nˇej jsou prvky vˇetˇs´ı. Pokud pole obsahuje pouze 3 prvky a za x jsme zvol´ıme prostˇredn´ı (prostˇredn´ı pokud jde o velikost, tedy medi´ an), z´ısk´ ame t´ımto procesem uspoˇra ´dan´e pole. Pokud m´ a pole d´elku 2, m˚ uˇzeme za x zvolit kter´ ykoli z nich - v´ ysledkem bude opˇet uspoˇra ´dan´e pole. Odtud je jiˇz zˇrejm´ y algoritmus rychl´eho tˇr´ıdˇen´ı: • Zvol´ıme x a rozdˇel´ıme popsan´ ym zp˚ usobem tˇr´ıdˇen´e pole na u ´seky a [1] , . . . , a [s] , . . . , a [n]. Pˇritom plat´ı, ˇze a [s] = x, prvky a [1] , . . . , a [s − 1] jsou menˇs´ı nebo rovny x a prvky a [s + 1] , . . . , a [n] jsou vˇetˇs´ı nebo rovny x. • Proces rozdˇelen´ı opakujeme pro u ´seky a [1] , . . . , a [s] a a [s + 1] , . . . a [n] a d´ ale pak pro jejich ˇca ´sti, aˇz dospˇejeme k u ´sek˚ um d´elky 1. Ty jsou jiˇz automaticky utˇr´ıdˇen´e. Rozdˇ elen´ı pole na dvˇ eˇ c´ asti Pod´ıvejme se nyn´ı podrobnˇeji na proces rozdˇelen´ı pole na dvˇe ˇca ´sti. Z toho, co jsme si ˇrekli v pˇredchoz´ım odd´ılu, plyne: vyjdeme od prvn´ıho prvku pole a a budeme hledat prvek, kter´ y je vˇetˇs´ı neˇz x (m´ a vˇetˇs´ı kl´ıˇc). Z´ aroveˇ n budeme pole a prohled´ avat od konce, aˇz naraz´ıme na prvek, kter´ y je menˇs´ı neˇz x. Pak tyto dva prvky zamˇen´ıme. Aˇz se oba smˇery prohled´ av´ an´ı setkaj´ı uprostˇred pole, skonˇc´ıme. Vzhledem k tomu, ˇze se s takov´ ymto dˇelen´ım pole na dvˇe ˇca ´sti setk´ ame tak´e v n´ asleduj´ıc´ım odd´ılu, vˇenovan´em hled´ an´ı medi´ anu, formulujeme je jako samostatnou proceduru: type pole = array [1..n] of prvek; procedure Rozdˇ el(var a: pole, x: prvek); var w: prvek; i,j: index; begin i := 1; j := n; repeat {Tyto dva cykly while hledaj´ ı} while a[i].kl´ ıc ˇ < x.kl´ ıc ˇ do inc(i); {prvky, kter´ e se} while x.kl´ ıc ˇ < a[j].kl´ ıc ˇ do dec(j); {navz´ ajem vymˇ en´ ı} if i <= j then begin w := a[i]; a[i] := a[j]; a[j] := w; {V´ ymˇ ena} inc(i); dec(j); end; until i > j; {Aˇ z do setk´ an´ ı uprostˇ red pole} end; Pˇ r´ıklad 5.8 Uvaˇzujme posloupnost (17, 12, 15, 11, 20, 13). Za rozdˇelovac´ı prvek x zvol´ıme hodnotu 15. Prvn´ı cyklus while najde prvek 17, kter´ y je vˇetˇs´ı neˇz x a leˇz´ı vlevo od x. Druh´ y cyklus while najde prvek 13, kter´ y je menˇs´ı neˇz x a leˇz´ı vpravo od x. Proto se tyto dva prvky zamˇen´ı. Tak dostaneme posloupnost (13, 12, 15, 11, 20, 17).
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
80
D´ ale prvn´ı cyklus while skonˇc´ı u hodnoty 15 (tedy u prvku x) a druh´ y cyklus while u hodnoty 11. Po v´ ymˇenˇe tˇechto prvk˚ u bude m´ıt naˇse posloupnost tvar (13, 12, 11, 15, 20, 17). T´ım procedura rozdˇelov´ an´ı skonˇc´ı. Vˇsimnˇete si, ˇze se pˇrem´ıstila i zar´ aˇzka x. Poznamenejme, ˇze v algoritmu pro rychl´e tˇr´ıdˇen´ı pouˇzijeme lehce upravenou verzi t´eto procedury. Vlastn´ı algoritmus rychl´ eho tˇ r´ıdˇ en´ı Vlastn´ı implementace algoritmu quicksort bude jiˇz velice jednoduch´ a. Z´ akladem bude rekurzivn´ı (nebo iterativn´ı) vol´ an´ı lehce upraven´e rozdˇelovac´ı procedury. V n´ asleduj´ıc´ı proceduˇre budeme pro zmˇenu pˇredpokl´ adat, ˇze pole tˇr´ıd´ıme na z´ akladˇe hodnot funkce Poˇrad´ı(X: prvek):integer. Pokud bychom chtˇeli tˇr´ıdit podle kl´ıˇc˚ u, staˇc´ı v´ yrazy tvaru P oad (a [i]) nahradit v´ yrazy tvaru a [i] .kli. Procedura Tˇr´ıdˇen´ı, vnoˇren´ a do procedury Quicksort, je vlastnˇe star´ a zn´ am´ a procedura Rozdˇel, kter´ a m´ a jako vstupn´ı parametry poˇca ´teˇcn´ı a koncov´ y index tˇr´ıdˇen´eho u ´seku pole. Jako x, prvek, podle kter´eho se pole rozdˇeluje, se vol´ı vˇzdy prostˇredn´ı prvek. Na konec t´eto procedury jsme doplnili rekurzivn´ı vol´ an´ı: jestliˇze po rozdˇelen´ı vzniknou u ´seky delˇs´ı neˇz 1, zavol´ a se na nˇe opˇet procedura Tˇr´ıdˇen´ı. Vlastn´ı tˇelo procedury Quicksort obsahuje pouze vol´ an´ı procedury Tˇr´ıdˇen´ı pro cel´e pole, tedy pro u ´sek od indexu 1 do n. procedure Quicksort; {Rekurzivn´ ı verze} {procedura Tˇ rı ´dˇ en´ ı rozdˇ eluje pole na u ´seky a ty uspoˇ ra ´d´ av´ a} {tˇ rı ´d´ ı u ´sek pole a od a[l] do a[r]} procedure Tˇ rı ´dˇ en´ ı(l,r: index); var w,x: prvek; i,j: index; begin {Tˇ rı ´dˇ en´ ı} i := l; j := r; x := a[(l+r) div 2]; {Zar´ az ˇka: prvek uprostˇ red} repeat while Poˇ rad´ ı(a[i]) < Poˇ rad´ ı(x) do inc(i); {Indexy prvk˚u pro v´ ymˇ enu} while Poˇ rad´ ı(a[j]) > Poˇ rad´ ı(x) do dec(j); if i <= j then begin {v´ ymˇ ena} w := a[i]; a[i] := a[j]; a[j] := w; inc(i); dec(j); end; until i>j; if l < j then Tˇ rı ´dˇ en´ ı (l,j); {prvek x na sv´ em m´ ıstˇ e} if i < r then Tˇ rı ´dˇ en´ ı (i,r); {tˇ rı ´d´ ı se ostatn´ ı} end; {tˇ rı ´dˇ en´ ı} begin {Quicksort} Tˇ rı ´dˇ en´ ı(1,n); end;{Quicksort} Pouˇzit´ı rekurze v algoritmu rychl´eho tˇr´ıdˇen´ı je pˇrirozen´ ym d˚ usledkem pouˇzit´ı principu rozdˇel a panuj. Ve skuteˇcnosti ale nemus´ı b´ yt rekurzivn´ı tvar procedury Quicksort v´ yhodn´ y. M˚ uˇzeme pouˇz´ıt napˇr. postupu, se ˇ kter´ ym jsme se sezn´ amili v kap. 4.2., a pˇrev´est Quicksort do nerekurzivn´ı podoby. Casto je ovˇsem v´ yhodnˇejˇs´ı vyj´ıt z rozboru algoritmu a pokusit se dospˇet k nerekurzivn´ı verzi pˇr´ımo. V naˇsem pˇr´ıpadˇe je zˇrejm´e, ˇze pˇri kaˇzd´em pr˚ uchodu, tj. pˇri kaˇzd´em vol´ an´ı procedury Tˇr´ıdˇen´ı, se pole rozdˇel´ı na dva u ´seky. Jeden z nich m˚ uˇzeme ihned zpracovat, meze druh´eho si mus´ıme uschovat. K tomu pouˇzijeme z´ asobn´ık.
ˇ ´I TR ˇ ´IDEN ˇ ´I 5.1. VNITRN
81
Na poˇca ´tku vloˇz´ıme do z´ asobn´ıku meze cel´eho pole, tj. 1 a n. Procedura QuicksortN (nerekurzivn´ı quicksort) si tyto hodnoty ze z´ asobn´ıku vezme, u ´sek pole rozdˇel´ı, jednu dvojici mez´ı uloˇz´ı do z´ asobn´ıku a druhou ihned zpracuje. Zpracov´ an´ı bude prob´ıhat v cyklu repeat, kter´ y skonˇc´ı, jakmile nebude v z´ asobn´ıku ˇza ´dn´ a dalˇs´ı dvojice mez´ı ke zpracov´ an´ı. Z´ asobn´ık bude pole, sloˇzen´e ze struktur, obsahuj´ıc´ıch dvojice index˚ u. Definujeme jej jako objektov´ y typ: type
z´ asobn´ ık = object z: array [1..m] of record {Pole na ukl´ ad´ an´ ı dat} l,r: index end; s: 0..m; {Ukazatel na vrchol z´ asobn´ ıku} constructor Vytvoˇ r; procedure Vloˇ z(i,j: index); procedure Vyjmi(var i,j: index); function Pr´ azdn´ y: boolean; end;
Konstruktor Vytvoˇr uloˇz´ı do atributu s hodnotu 0 (z´ asobn´ık je pr´ azdn´ y). Metody Vloˇz resp. Vyjmi umoˇzn ˇuj´ı vloˇzit do z´ asobn´ıku dvojici index˚ u resp. vyjmout ji ze z´ asobn´ıku. Booleovsk´ a funkce Pr´ azdn´y vr´ at´ı true, bude-li z´ asobn´ık pr´ azdn´ y, tj. bude-li s = 0. Implementace tˇechto metod je trivi´ aln´ı a pˇrenech´ av´ am ji ˇcten´ aˇri. V proceduˇre deklarujeme z´ asobn´ık jako lok´ aln´ı promˇennou z typu z´ asobn´ık. Pˇri tˇr´ıdˇen´ı se budeme - podobnˇe jako v rekurzivn´ı verzi - op´ırat o funkci Poˇrad´ı. procedure QuicksortN; {Nerekurzivn´ ı verze} var i,j,l,r: index; x, w: prvek; z: z´ asobn´ ık; begin z.Vytvoˇ r; z.Vloˇ z(1,n); {Inicializace z´ asobn´ ıku} repeat {Dalˇ sı ´ u ´sek z vrcholu z´ asobn´ ıku} z.Vyjmi(l,r); repeat {Rozdˇ elen´ ı u ´seku a[l]..a[r]} i := l; j := r; x := a[(l+r) div 2]; {Zar´ az ˇka} repeat while Poˇ rad´ ı(a[i]) < x do inc(i); {Indexy prvk˚ u pro v´ ymˇ enu} while Poˇ rad´ ı(a[j]) > x do dec(j); if i <= j then begin {V´ ymˇ ena} w := a[i]; a[i] := a[j]; a[j] := w; inc(i); dec(j); end; until i>j; if i < r then z.Vloˇ z(i,r); {Uloˇ z prav´ y u ´sek do z´ asobn´ ıku} r := j; until l >= r; until z.Pr´ azdn´ y; end; V implementaci nerekurzivn´ı verze algoritmu rychl´eho tˇr´ıdˇen´ı z˚ ustal jeden nevyˇreˇsen´ y probl´em: jak velk´e m´ a b´ yt m, konstanta, kter´ a urˇcuje velikost z´ asobn´ıku? Odpovˇed’ najdeme pˇri anal´ yze algoritmu rychl´eho tˇr´ıdˇen´ı. Anal´ yza rychl´ eho tˇ r´ıdˇ en´ı Vyjdeme od anal´ yzy procesu rozdˇelov´ an´ı pole na u ´seky. M´ a-li tˇr´ıdˇen´e pole n prvk˚ u, potˇrebujeme k rozdˇelen´ı pole nejv´ yˇse n porovn´ an´ı v cyklech while, kde porovn´ ame vˇsechny prvky se zvolenou zar´ aˇzkou x.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
82
D´ ale urˇc´ıme stˇredn´ı poˇcet v´ ymˇen. Bez u ´jmy na obecnosti m˚ uˇzeme pˇredpokl´ adat, ˇze tˇr´ıdˇen´e pole obsahuje kl´ıˇce 1, . . . , n. Vˇsechny permutace kl´ıˇc˚ u, tedy vˇsechna moˇzn´ a uspoˇra ´d´ an´ı prvk˚ u v poli, pokl´ ad´ ame za stejnˇe pravdˇepodobn´e. Jako zar´ aˇzku x jsme zvolili prvek s kl´ıˇcem i. Potom bude potˇrebn´ y poˇcet v´ ymˇen roven souˇcinu poˇctu prvk˚ u v lev´em u ´seku pole, i−1, a pravdˇepodobnosti v´ yskytu kl´ıˇce, kter´ y je vˇetˇs´ı neˇz i (a tedy jej mus´ıme vymˇenit). Tato pravdˇepodobnost je rovna poˇctu takov´ ych kl´ıˇc˚ u, lomen´emu n. Stˇredn´ı poˇcet v´ ymˇen pˇri dˇelen´ı pole na u ´seky potom bude n 1X n−i+1 n 1 n M= (i − 1) = − ≈ n i=1 n 6 6n 6
(5.11)
Kdyby se n´ am podaˇrilo pˇri volbˇe rozdˇelovac´ıho prvku x zvolit vˇzdy medi´ an, tj. kdyby bylo v´ ysledkem vˇzdy rozdˇelen´ı pole na poloviny, potˇrebovali bychom k utˇr´ıdˇen´ı pole log 2 n pr˚ uchod˚ u. To znamen´ a, ˇze v tomto (nejv´ yhodnˇejˇs´ım) pˇr´ıpadˇe potˇrebujeme celkem C = n log2 n porovn´ an´ı a M = (n/6) log2 n v´ ymˇen, tedy (n/2) log2 n pˇresun˚ u, nebot’ jedna v´ ymˇena prvk˚ u vyˇzaduje tˇri pˇresuny. Lze uk´ azat, ˇze pokud budeme rozdˇelovac´ı prvek volit n´ ahodnˇe (rovnomˇernˇe), bude pr˚ umˇern´ au ´ˇcinnost algoritmu rychl´eho tˇr´ıdˇen´ı horˇs´ı o faktor 2 ln 2 [1],[26]. Pod´ıvejme se jeˇstˇe na nejhorˇs´ı pˇr´ıpad. Jeho anal´ yza je zaj´ımav´ a nejen z hlediska u ´ˇcinnosti algoritmu, ale i z hlediska velikosti z´ asobn´ıku (konstanty m) v nerekurzivn´ı verzi. Zˇrejmˇe nejhorˇs´ı moˇzn´ y pˇr´ıpad nastane, jestliˇze jako zar´ aˇzku zvol´ıme nejmenˇs´ı prvek z dan´eho u ´seku. Potom bude m´ıt prav´ yu ´sek d´elku n−1 prvk˚ u a lev´ y d´elku 1. Pokud se n´ am takov´ ato neˇst’astn´ a volba podaˇr´ı v kaˇzd´em kroku, budeme potˇrebovat m´ısto log2 n celkem n rozdˇelen´ı. V tomto pˇr´ıpadˇe bude celkov´ y poˇcet porovn´ an´ı i pˇresun˚ u O n2 a rychl´e tˇr´ıdˇen´ı jiˇz nebude dˇelat ˇcest sv´emu jm´enu. Zm´ınˇen´ y nejhorˇs´ı pˇr´ıpad nastane mj. tehdy, kdyˇz bude pole jiˇz pˇredem utˇr´ıdˇen´e a my budeme volit jako x vˇzdy prvn´ı nebo posledn´ı prvek. Pokud bychom volili vˇzdy prostˇredn´ı prvek, pˇredstavovalo by utˇr´ıdˇen´e pole naopak nejlepˇs´ı pˇr´ıpad; ˇcten´ aˇr ovˇsem jistˇe snadno zkonstruuje pole, kter´e povede k nejhorˇs´ımu pˇr´ıpadu pˇri volbˇe prostˇredn´ıho prvku.
Obvykle se doporuˇcuje bud’ volit x n´ ahodnˇe nebo jako medi´ an mal´eho vzorku (3 - 5 prvk˚ u). Takov´ a volba sice nezmˇen´ı pr˚ umˇern´ y v´ ykon algoritmu, zlepˇs´ı ale jeho chov´ an´ı v nejhorˇs´ım pˇr´ıpadˇe. Pod´ıvejme se nyn´ı na nerekurzivn´ı verzi algoritmu. D´elka z´ asobn´ıku mus´ı vych´ azet z nejhorˇs´ıho moˇzn´eho pˇr´ıpadu. V proceduˇre QuicksortN vˇzdy zpracov´ av´ ame lev´ yu ´sek a meze prav´eho u ´seku uloˇz´ıme do z´ asobn´ıku, kde ˇcekaj´ı na budouc´ı zpracov´ an´ı. Jestliˇze bude m´ıt prav´ yu ´sek vˇzdy d´elku 1 a lev´ y n − 1, nahromad´ı se n´ am v z´ asobn´ıku aˇz n dvojic mez´ı. To znamen´ a, ˇze v nejhorˇs´ım pˇr´ıpadˇe m˚ uˇze b´ yt potˇreba pomocn´e pamˇeti O (n)! Pˇritom pro rekurzivn´ı proceduru nebude situace o nic lepˇs´ı - naopak, spotˇreba pamˇeti bude jeˇstˇe vyˇsˇs´ı. Jak v´ıme, budou se parametry rekurzivn´ıch vol´ an´ı ukl´ adat na implicitn´ı z´ asobn´ık programu; vedle toho ovˇsem pˇribudou jeˇstˇe lok´ aln´ı promˇenn´e a n´ avratov´e adresy. Vrat’me se ale k nerekurzivn´ı verzi. Snadno uk´ aˇzeme, ˇze kdyˇz budeme vˇzdy zpracov´ avat jako prvn´ı kratˇs´ı u ´sek a delˇs´ı odloˇz´ıme na pozdˇeji, bude maxim´ aln´ı d´elka z´ asobn´ıku rovna m = log 2 n (to nastane, jestliˇze budeme za x volit vˇzdy medi´ an). Odpov´ıdaj´ıc´ı u ´pravu procedury QuicksortN zvl´ adne ˇcten´ aˇr jistˇe s´ am.
5.2
Hled´ an´ı k-t´ eho prvku podle velikosti
Tato u ´loha u ´zce souvis´ı s algoritmy pro vnitˇrn´ı tˇr´ıdˇen´ı. M´ ame pole a, mezi jehoˇz prvky chceme naj´ıt k -t´ y podle velikosti. Nejˇcastˇeji p˚ ujde o hled´ an´ı medi´ anu, tedy prvku, kter´ y je menˇs´ı nebo roven jedn´e polovinˇe prvk˚ u pole a z´ aroveˇ n je vˇetˇs´ı nebo roven druh´e polovinˇe prvk˚ u pole. Napˇr. medi´ anem posloupnosti (11, 23, 6, 19, 1) je ˇc´ıslo 11. Obˇcas ale potˇrebujeme naj´ıt obecnˇe k -t´ y prvek podle velikosti, tedy prvek x, pro kter´ y plat´ı: k − 1 prvk˚ u pole je menˇs´ıch nebo rovno x a n − k prvk˚ u je vˇetˇs´ıch nebo rovno x. Pˇripomeˇ nme si, ˇze pokud ˇr´ık´ ame, ˇze prvek x je vˇetˇs´ı neˇz prvek y, m´ ame t´ım na mysli opˇet nerovnost mezi jejich kl´ıˇci.
´ ´I K-TEHO ´ 5.2. HLEDAN PRVKU PODLE VELIKOSTI
83
Prvn´ı moˇznost, kter´ a n´ as napadne, je jednoduch´ a: 1. Pole a utˇr´ıd´ıme podle velikosti od nejmenˇs´ıho prvku k nejvˇetˇs´ımu. 2. Vezmeme prvek a [k]. Uk´ aˇzeme si ale, ˇze existuje efektivnˇejˇs´ı metoda.
5.2.1
Hoar˚ uv algoritmus
Autorem tohoto algoritmu je Hoare [24] a vyuˇz´ıv´ a opˇet dˇelen´ı pole na u ´seky, popsan´e v odd´ılu 5.1.7. Postup je n´ asleduj´ıc´ı: Pouˇzijeme proceduru pro rozdˇelen´ı pole na u ´seky, pˇriˇcemˇz poloˇz´ıme l = 1 a r = n. Jako prvek x pouˇzijeme a [k]. V´ ysledkem bude ˇca ´steˇcnˇe uspoˇra ´dan´e pole a pro indexy i a j, kter´e dostaneme na konci procedury Rozdˇel, bude platit a)
Pro vˇsechna k < i plat´ı a [k] .kl´ıˇc ≤ x.kl´ıˇc.
b)
Pro vˇsechna k > j plat´ı a [k] .kl´ıˇc ≥ x.kl´ıˇc.
c)
i > j.
V´ ysledkem bude jedna z n´ asleduj´ıc´ıch tˇr´ı moˇznost´ı: 1. j < k < i. V´ ymˇeny skonˇcily pˇred dosaˇzen´ım zar´ aˇzky x = a [k]. To znamen´ a, ˇze prvek a [k] dˇel´ı pole a na dva u ´seky tak, jak to poˇzadujeme, a a [k] je hledan´ ym k -t´ ym nejmenˇs´ım prvkem. 2. j < k. Prvek x byl pˇr´ıliˇs mal´ y (resp. jeho kl´ıˇc byl pˇr´ıliˇs mal´ y). Operaci dˇelen´ı mus´ıme zopakovat pro u ´sek a [i] , . . . , [r]. 3. k < i. Prvek x byl pˇr´ıliˇs velk´ y. Operaci dˇelen´ı mus´ıme zopakovat pro u ´sek a [l] , . . . , a [j]. Pˇ r´ıklad 5.9 Pod´ıvejme se na pˇr´ıklady obou nepˇr´ızniv´ ych moˇznost´ı. a) Je d´ ana posloupnost (3, 2, 1, 6, 5, 4, 0, 9, 8, 7). Hled´ ame 5. prvek podle velikosti. Jestliˇze za x zvol´ıme a [5] = 5, dostaneme po prvn´ım pr˚ uchodu rozdˇelovac´ı procedurou posloupnost (3, 2, 1, 0, 4, 5, 6, 9, 8, 7) a hodnoty index˚ u, pˇri kter´ ych se prohled´ av´ an´ı zastavilo, budou i = 6 a j = 5. To znamen´ a, ˇze se prvek x = 5 odsunul z p˚ uvodn´ıho m´ısta doprava, nebot’ byl pˇr´ıliˇs velk´ y. Mus´ıme pokraˇcovat s u ´sekem a [1] , . . . , a [5]. b) Uvaˇzujme opˇet posloupnost (3, 2, 1, 6, 5, 4, 0, 9, 8, 7). Tentokr´ at hled´ ame 6. prvek jako zar´ aˇzku bereme x = a [6] = 4. Po prvn´ım pr˚ uchodu dostaneme posloupnost (3, 2, 1, 0, 4, 5, 6, 9, 8, 7) a indexy i a j budou i = 6, j = 5. Prvek a [6] se odsunul z p˚ uvodn´ıho m´ısta doleva, nebot’ byl pˇr´ıliˇs mal´ y. Mus´ıme pokraˇcovat s u ´sekem a [6] , . . . , a [10]. Proceduru pro vyhled´ an´ı k -t´eho prvku pole m˚ uˇzeme v Pascalu zapsat napˇr. takto (podobnˇe jako v pˇredchoz´ım odd´ılu pˇredpokl´ ad´ ame, ˇze prvky pole a jsou typu prvek a ˇze typ index m´ a rozsah 1 . . . n ): procedure Najdi(k: index); var l,r,i,j:index; w,x: prvek; begin l := 1; r := n; while l < r do begin x := a[k]; i := l; j := r; repeat while a[i].kl´ ıc ˇ < x.kl´ ıc ˇ do inc(i); while a[j].kl´ ıc ˇ > x.kl´ ıc ˇ do dec(j); if i <= j then begin
{Na poˇ ca ´tku cel´ e pole}
{Rozdˇ el}
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
84 w := a[i]; a[i] := a[j]; a[j] := w; inc(i); dec(j); end; until i > j; if j < k then l := i; if k < i then r := j; end; end;
{V´ ymˇ ena}
{Pˇ rı ´liˇ s velk´ y} {Pˇ rı ´liˇ s mal´ y}
Budeme-li pˇredpokl´ adat, ˇze se pˇri kaˇzd´em rozdˇelen´ı intervalu zkr´ at´ı na polovinu d´elka u ´seku, ve kter´em je hledan´ y prvek, dostaneme pro potˇrebn´ y poˇcet porovn´ an´ı pˇribliˇzn´ y v´ yraz n n C = n + + + · · · + 1 ≈ 2n 2 4 To znamen´ a, ˇze v nejlepˇs´ım pˇr´ıpadˇe je tato metoda rychlejˇs´ı neˇz u ´pln´e utˇr´ıdˇen´ı pomoc´ı nejlepˇs´ıch algoritm˚ u pro vnitˇrn´ı tˇr´ıdˇen´ı, nebot’ ty poˇzaduj´ı O (n log2 n) operac´ı. V nejhorˇs´ım pˇr´ıpadˇe, kdy se poˇcet prvk˚ uvu ´seku, ve kter´em leˇz´ı hledan´ y prvek, pˇri kaˇzd´em rozdˇelen´ı zmenˇs´ı pouze o 1, budeme potˇrebovat n − 1 pr˚ uchod˚ u a tedy O n2 porovn´ an´ı.
5.3
Vnˇ ejˇ s´ı tˇ r´ıdˇ en´ı
V tomto odd´ılu se budeme zab´ yvat metodami pro tˇr´ıdˇen´ı sekvenˇcn´ıch soubor˚ u. Pˇripomeˇ nme se nejd˚ uleˇzitˇejˇs´ı rozd´ıly ve srovn´ an´ı s metodami pro tˇr´ıdˇen´ı pol´ı. O souboru pˇredpokl´ ad´ ame, ˇze je uloˇzen na vnˇejˇs´ım, zpravidla magnetick´em m´ediu se sekvenˇcn´ım pˇr´ıstupem. To znamen´ a, ˇze mus´ıme zpracov´ avat jeden z´ aznam po druh´em v poˇrad´ı, v jak´em jsou v souboru uloˇzeny. Vedle toho zde pˇredem nezn´ ame rozsah tˇr´ıdˇen´ ych dat, tj. poˇcet n z´ aznam˚ u v souboru. Lze ale pˇredpokl´ adat, ˇze je tak velk´ y, ˇze se soubor nevejde do operaˇcn´ı pamˇeti poˇc´ıtaˇce. ˇ ı z´ Cten´ aznamu ze souboru nebo uloˇzen´ı (z´ apis) do souboru budeme spoleˇcnˇe oznaˇcovat jako pˇr´ıstup do souboru. Pˇr´ıstup do souboru trv´ a o nˇekolik ˇra ´d˚ u d´ele neˇz porovn´ av´ an´ı z´ aznam˚ u, takˇze je z hlediska efektivity algoritm˚ u pro vnˇejˇs´ı tˇr´ıdˇen´ı rozhoduj´ıc´ı. Proto si pˇri rozboru algoritm˚ u pro vnˇejˇs´ı tˇr´ıdˇen´ı budeme vˇs´ımat jen poˇctu pˇr´ıstup˚ u do souboru. Na druh´e stranˇe m´ ame obvykle k dispozici dostateˇcn´e mnoˇzstv´ı vnˇejˇs´ı pamˇeti, takˇze s n´ı nemus´ıme pˇr´ıliˇs ˇsetˇrit a budeme pˇri tˇr´ıdˇen´ı vyuˇz´ıvat pomocn´ ych soubor˚ u.
5.3.1
Pˇ r´ım´ e sluˇ cov´ an´ı
Algoritmus pˇr´ım´eho sluˇcov´ an´ı (sort-merge) odvod´ıme pomoc´ı metody rozdˇel a panuj . Na nˇem se sezn´ am´ıme se z´ akladn´ımi probl´emy vnˇejˇs´ıho tˇr´ıdˇen´ı. Protoˇze si vˇsak d´ ale uk´ aˇzeme v´ yhodnˇejˇs´ı algoritmus, nebudeme jej formulovat jako proceduru. Budeme uvaˇzovat takto: Protoˇze je tˇr´ıdˇen´ y soubor pˇr´ıliˇs velk´ y, neˇz aby se veˇsel do pamˇeti, kde bychom na nˇej mohli pouˇz´ıt nˇekterou z metod pro vnitˇrn´ı tˇr´ıdˇen´ı, rozdˇel´ıme jej na dva stejnˇe velk´e soubory. Pokud dok´ aˇzeme tyto pomocn´e soubory (nˇejak) setˇr´ıdit, zb´ yv´ a je slouˇcit a jsme hotovi. Na pomocn´e soubory m˚ uˇzeme pouˇz´ıt tut´eˇz u ´vahu. To znamen´ a, ˇze kaˇzd´ y z pomocn´ ych soubor˚ u opˇet rozdˇel´ıme na poloviny atd. Po pˇribliˇznˇe log2 n kroc´ıch dospˇejeme k soubor˚ um, kter´e budou obsahovat pouze jeden z´ aznam (n je nezn´ am´ y poˇcet z´ aznam˚ u v p˚ uvodn´ım souboru). Jenˇze soubor s jedin´ ym z´ aznamem je jiˇz automaticky setˇr´ıdˇen´ y. To znamen´ a, ˇze staˇc´ı umˇet vˇsechny tyto setˇr´ıdˇen´e soubory slouˇcit. Ve skuteˇcnosti si samozˇrejmˇe nem˚ uˇzeme dovolit vytvoˇrit n pomocn´ ych soubor˚ u (i kdyˇz jsme ˇrekli, ˇze vnˇejˇs´ı pamˇet´ı nemus´ıme pˇr´ıliˇs ˇsetˇrit). M´ısto toho ponech´ ame z´ aznamy ˇreknˇeme ve dvou souborech, se kter´ ymi budeme zach´ azet jako s posloupnost´ı soubor˚ u, nebo jeˇstˇe l´epe s posloupnost´ı setˇr´ıdˇen´ ych z´ aznam˚ u.
ˇ Sˇ´I TR ˇ ´IDEN ˇ ´I 5.3. VNEJ
85
Pˇredpokl´ adejme, ˇze P a Q jsou dva vzestupnˇe setˇr´ıdˇen´e soubory. Jak je slouˇc´ıme, aby vznikl vzestupnˇe utˇr´ıdˇen´ y soubor R? Nejjednoduˇsˇs´ı postup je tento: 1. Nejprve pˇreˇcteme prvn´ı z´ aznam ze souboru P a uloˇz´ıme jej do promˇenn´e p. Pak pˇreˇcteme prvn´ı z´ aznam ze souboru Q a uloˇz´ıme jej do promˇenn´e q. 2. Porovn´ ame kl´ıˇce promˇenn´ ych p a q. Je-li p.kl´ıˇc ≤ q.kl´ıˇc, zap´ıˇseme do R promˇennou p a ze souboru P pˇreˇcteme dalˇs´ı z´ aznam. Je-li p.kl´ıˇc ≥ q.kl´ıˇc, zap´ıˇseme do R promˇennou q a ze souboru Q pˇreˇcteme dalˇs´ı z´ aznam. 3. Krok 2 opakujeme, dokud nenaraz´ıme na konec jednoho ze soubor˚ u P nebo Q. 4. Pokud jsme narazili na konec souboru P, okop´ırujeme do souboru R promˇennou q a zbyl´e z´ aznamy ze souboru Q, jinak okop´ırujeme do souboru R promˇennou p a zbyl´e z´ aznamy ze souboru P. V´ ysledkem bude soubor R, kter´ y bude obsahovat vzestupnˇe seˇrazen´e z´ aznamy ze soubor˚ u P a Q. Nyn´ı jiˇz m˚ uˇzeme formulovat z´ aklad algoritmu tˇr´ıdˇen´ı pˇr´ım´ ym sluˇcov´ an´ım. Pˇritom pˇredpokl´ ad´ ame, ˇze tˇr´ıd´ıme soubor A. Pomocn´e soubory oznaˇc´ıme B a C. 1. Soubor A rozdˇel´ıme do soubor˚ u B a C tak, ˇze do kaˇzd´eho z nich pˇrekop´ırujeme stˇr´ıdavˇe jeden z´ aznam. Soubor B tedy nyn´ı obsahuje lich´e z´ aznamy z A, soubor C obsahuje sud´e z´ aznamy z A. 2. Kaˇzd´ y ze soubor˚ u B a C ch´ apeme jako posloupnost utˇr´ıdˇen´ ych soubor˚ u d´elky 1. Z B a C vytvoˇr´ıme soubor A tak, ˇze jednoprvkov´e u ´seky slouˇc´ıme pomoc´ı v´ yˇse popsan´eho postupu do dvouprvkov´ ych u ´sek˚ u. Soubor A nyn´ı obsahuje uspoˇra ´dan´e dvojice. 3. Soubor A rozdˇel´ıme do soubor˚ u B a C tak, ˇze do kaˇzd´eho z nich pˇrekop´ırujeme stˇr´ıdavˇe jednu uspoˇra ´danou dvojici. 4. Slouˇcen´ım dvouprvkov´ ych u ´sek˚ u z B a C z´ısk´ ame soubor A, kter´ y bude obsahovat uspoˇra ´dan´e ˇctveˇrice. 5. Body 3 a 4 opakujeme pro ˇctveˇrice, osmice atd., aˇz dostaneme plnˇe utˇr´ıdˇen´ y soubor. Pˇri sluˇcov´ an´ı soubor˚ u B a C mus´ıme vz´ıt v u ´vahu, ˇze poˇcet n prvk˚ u p˚ uvodn´ıho souboru A nejsp´ıˇs nebude roven ˇza ´dn´e mocninˇe dvou. Pokud napˇr´ıklad bude n lich´e, bude po rozdˇelen´ı obsahovat soubor B o jeden z´ aznam v´ıce neˇz soubor C. Obecnˇe m˚ uˇze jeden ze soubor˚ u na konci obsahovat o jednu k -tici m´enˇe. Pˇri sluˇcov´ an´ı nesm´ıme na tyto z´ aznamy zapomenout. Pˇ r´ıklad 5.10 Uvaˇzujme soubor (posloupnost) A = (11, 8, 77, 54, 21, 64, 46, 0). Soubor A rozdˇel´ıme v prvn´ım kroku do soubor˚ u B a C takto: B = (11, 77, 21, 46) ,
C = (8, 54, 64, 0)
Ve druh´em kroku slouˇc´ıme B a C tak, ˇze vznikne soubor A a s uspoˇra ´dan´ ymi dvojicemi, A = (8, 11, 54, 77, 21, 64, 0, 46). Tento soubor opˇet rozdˇel´ıme do B a C, tentokr´ at tak, ˇze do B d´ ame prvn´ı dvojici, do C druhou dvojici atd. Dostaneme B = (8, 11, 21, 64) , C = (54, 77, 0, 46) . Slouˇcen´ım dvojic dostaneme soubor A, kter´ y bude obsahovat uspoˇra ´dan´e ˇctveˇrice: A = (8, 11, 54, 77, 0, 21, 46, 64). Tento soubor rozdˇel´ıme tak, ˇze do B pˇrijde jedna ˇctveˇrice a do C druh´ a: B = (8, 11, 54, 77) , C = (0, 21, 46, 64) . Slouˇcen´ım tˇechto dvou soubor˚ u dostaneme u ´plnˇe utˇr´ıdˇen´ y soubor A, A = (0, 8, 11, 21, 46, 54, 64, 77).
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
86
Pouˇzijeme-li tento postup pˇri tˇr´ıdˇen´ı soubor˚ u na magnetick´ ych p´ ask´ ach, budeme potˇrebovat 3 p´ asky (3 stojany). Na magnetick´em disku m˚ uˇzeme pracovat s nˇekolika soubory z´ aroveˇ n, takˇze staˇc´ı jedno zaˇr´ızen´ı, pokud m´ a dostateˇcnou kapacitu voln´eho prostoru. Abychom si d´ ale usnadnili vyjadˇrov´ an´ı, zavedeme n´ asleduj´ıc´ı oznaˇcen´ı: Operace s cel´ ym souborem budeme naz´ yvat f´ aze; budeme hovoˇrit napˇr. o f´ azi rozdˇelov´ an´ı, ve kter´e rozdˇelujeme soubor A na pomocn´e soubory B a C, a o f´ azi sluˇcov´ an´ı, ve kter´e ze dvou soubor˚ u B a C, kter´e obsahuj´ı utˇr´ıdˇen´e k -tice, vytvoˇr´ıme soubor A, obsahuj´ıc´ı utˇr´ıdˇen´e 2k -tice. Jako kroky budeme oznaˇcovat nejmenˇs´ı podprocesy pˇri tˇr´ıdˇen´ı. Anal´ yza tˇ r´ıdˇ en´ı pˇ r´ım´ ym sluˇ cov´ an´ım Pˇredpokl´ adejme nejprve, ˇze n = 2k pro nˇejak´e pˇrirozen´e k. Potom budeme v prvn´ım pr˚ uchodu sluˇcovat celkem k 2 jednoprvkov´ ych u ´sek˚ u, ve druh´em pr˚ uchodu 2k−1 dvouprvkov´ ych u ´sek˚ u atd. pˇri k -t´em pr˚ uchodu slouˇc´ıme dva u ´seky o 2k−1 prvc´ıch. Dostaneme tedy celkem k = log2 n pr˚ uchod˚ u. Bude-li 2k−1 ≤ n ≤ 2k , bude poˇcet pr˚ uchod˚ u opˇet roven nejv´ yˇse k. To znamen´ a, ˇze obecnˇe m˚ uˇzeme poˇcet pr˚ uchod˚ u omezit ˇc´ıslem [log2 n] + 1, kde [z] znamen´ a celou ˇca ´st ˇc´ısla z. Kaˇzd´ y pr˚ uchod se skl´ ad´ a z f´ aze rozdˇelov´ an´ı a f´ aze sluˇcov´ an´ı. F´ aze rozdˇelov´ an´ı pˇredstavuje jedno ˇcten´ı a jeden z´ apis do souboru pro kaˇzd´ y z´ aznam, tedy celkem 2n pˇr´ıstup˚ u do souboru. Tak´e ve f´ azi sluˇcov´ an´ı mus´ıme kaˇzd´ y z´ aznam nejprve pˇreˇc´ıst a pozdˇeji jej zase zapsat do souboru. Celkem tedy kaˇzd´ y pr˚ uchod znamen´ a 4n pˇr´ıstup˚ u do souboru. To znamen´ a, ˇze celkov´ y poˇcet pˇr´ıstup˚ u do soubor˚ u je omezen hodnotou 4n ([log 2 n] + 1). Vylepˇ sen´ı algoritmu tˇ r´ıdˇ en´ı pˇ r´ım´ ym sluˇ cov´ an´ım F´ aze rozdˇelov´ an´ı pˇredstavuje pouh´e kop´ırov´ an´ı z´ aznam˚ u. Vyˇzaduje jedno ˇcten´ı a jeden z´ apis do souboru pro kaˇzd´ y z´ aznam a pˇritom je v podstatˇe neproduktivn´ı - ˇze zab´ır´ a pouze ˇcas. Bohuˇzel ji nelze odstranit; jestliˇze si ale m˚ uˇzeme dovolit pouˇz´ıt jeˇstˇe jeden pomocn´ y soubor, m˚ uˇzeme ji f´ azi rozdˇelov´ an´ı spojit s f´ az´ı sluˇcov´ an´ı a tak pˇri kaˇzd´em pr˚ uchodu uˇsetˇrit pro kaˇzd´ y z´ aznam dva pˇr´ıstupy do souboru. Dostaneme tak upraven´ y algoritmus, kter´ y bude pouˇz´ıvat soubor˚ u A, B, C a D. Postup pak vypad´ a takto: Na poˇca ´tku rozdˇel´ıme tˇr´ıdˇen´ y soubor A do soubor˚ u C a D podobnˇe jako v p˚ uvodn´ı verzi algoritmu. Pˇri sluˇcov´ an´ı vˇsak budeme vznikl´e dvojice stˇr´ıdavˇe zapisovat do souboru A resp. B tak, ˇze prvn´ı dvojici zap´ıˇseme do A, druhou do B, tˇret´ı do A atd. To znamen´ a, ˇze soubory A a B budou jiˇz obsahovat utˇr´ıdˇen´e dvojice. V dalˇs´ım pr˚ uchodu budeme sluˇcovat dvojice ze soubor˚ u A a B a vznikl´e ˇctveˇrice zapisovat stˇr´ıdavˇe do C a D. Snadno zjist´ıme, ˇze touto u ´pravou zkr´ at´ıme ˇcas v podstatˇe na polovinu (ovˇsem za cenu dalˇs´ıho souboru - a to nemus´ı b´ yt vˇzdy pˇrijateln´e).
5.3.2
Tˇ r´ıdˇ en´ı pˇ rirozen´ ym sluˇ cov´ an´ım
Pouˇzijeme-li algoritmu pˇr´ım´eho sluˇcov´ an´ı, bude lhostejn´e, zda je soubor na poˇca ´tku jiˇz ˇca ´steˇcnˇe utˇr´ıdˇen nebo zda v nˇem jsou prvky uloˇzeny zcela n´ ahodnˇe. Dokonce i pro zcela setˇr´ıdˇen´ y soubor se provedou vˇsechny pr˚ uchody. Ve skuteˇcnosti i soubor se zcela n´ ahodnˇe uspoˇra ´dan´ ymi prvky m˚ uˇze jiˇz obsahovat u ´seky, v nichˇz prvky za sebou n´ asleduj´ı ve spr´ avn´em poˇrad´ı. Tˇr´ıdˇen´ı pˇrirozen´ym sluˇcov´ an´ım umoˇzn ˇuje takov´e u ´seky pˇrirozen´ ym zp˚ usobem“ ” vyuˇz´ıvat. Neˇz se pust´ıme do dalˇs´ıho v´ ykladu, zavedeme pojem bˇeh. Tak budeme oznaˇcovat kaˇzdou maxim´ aln´ı uspoˇra ´danou podposloupnost v r´ amci tˇr´ıdˇen´e posloupnosti (souboru). To znamen´ a, ˇze v posloupnosti a 1 , . . . , an
ˇ Sˇ´I TR ˇ ´IDEN ˇ ´I 5.3. VNEJ
87
oznaˇc´ıme jako bˇeh kaˇzdou podposloupnost ar , . . . , as , pro kterou plat´ı nerovnosti ar ≤ ar−1 , as ≤ as+1 , ai ≤ ai+1 pro r ≤ i < s.
(5.12)
Napˇr´ıklad posloupnost (5, 12, 7, 1, 9, 100) se skl´ ad´ a z bˇeh˚ u (5, 12), (7) a (1, 9, 100). Pˇri tˇr´ıdˇen´ı pˇr´ım´ ym sluˇcov´ an´ım jsme vych´ azeli z pˇredpokladu, ˇze v´ ychoz´ı soubor se skl´ ad´ a z n bˇeh˚ u d´elky 1, a ty jsme postupnˇe sluˇcovali. Pˇri tˇr´ıdˇen´ı pˇrirozen´ ym sluˇcov´ an´ım budeme pˇredpokl´ adat, ˇze se v´ ychoz´ı soubor skl´ ad´ a z bl´ıˇze neurˇcen´eho poˇctu bˇeh˚ u r˚ uzn´ ych d´elek, a pˇri kop´ırov´ an´ı a sluˇcov´ an´ı budeme bˇehy urˇcovat za chodu programu podle (5.12). Dynamick´e“ urˇcov´ an´ı bˇeh˚ u pˇrin´ aˇs´ı jist´e komplikace. Pˇredevˇs´ım se mohou i podstatn´ ym zp˚ usobem liˇsit poˇcty ” bˇeh˚ u ve sluˇcovan´ ych souborech. Vzhledem k tomu, ˇze rozdˇelovac´ı f´ aze kop´ıruje bˇehy stˇr´ıdavˇe do souboru B a C, by se mohlo zd´ at, ˇze se poˇcty bˇeh˚ u v tˇechto souborech budou liˇsit nejv´ yˇse o 1. M˚ uˇze se ale st´ at, ˇze posledn´ı prvek i-t´eho bˇehu je menˇs´ı neˇz prvn´ı prvek i + 2-t´eho bˇehu (a rozdˇelovac´ı f´ aze um´ıst´ı tyto dva bˇehy bezprostˇrednˇe za sebe). Sluˇcovac´ı f´ aze je pak bude ch´ apat jako jedin´ y bˇeh. Pod´ıvejme se na posloupnost (1, 5, 2, 9, 7, 11, 6, 22). Rozdˇelovac´ı f´ aze zde najde 4 bˇehy po dvou prvc´ıch a tuto posloupnost rozdˇel´ı do soubor˚ u B a C takto: B = (1, 5, 7, 11) , C = (2, 9, 6, 22) . V souboru B se ovˇsem dva bˇehy samovolnˇe slouˇcily“, takˇze sluˇcovac´ı f´ aze najde v B pouze jedin´ y bˇeh. ” Procedura pro pˇ rirozen´ e sluˇ cov´ an´ı V tomto odstavci budeme pˇredpokl´ adat, ˇze se tˇr´ıdˇen´ y soubor skl´ ad´ a ze z´ aznam˚ u tvaru type prvek = record kl´ ıc ˇ: integer; d: data end; Z´ aznamy tˇr´ıd´ıme podle vzr˚ ustaj´ıc´ı hodnoty kl´ıˇce. Objektov´ y typ Soubor Pˇri tˇr´ıdˇen´ı sluˇcov´ an´ım je obˇcas vhodn´e zn´ at nejen hodnotu pr´ avˇe ˇcten´eho z´ aznamu v souboru, ale i hodnotu z´ aznamu n´ asleduj´ıc´ıho. V klasick´em Pascalu k tomu slouˇz´ı tzv. pˇr´ıstupov´ a promˇenn´ a souboru. Turbo Pascal tuto moˇznost nenab´ız´ı, m˚ uˇzeme se ji ale snadno naprogramovat. Jedno z moˇzn´ ych ˇreˇsen´ı pˇredstavuje n´ asleduj´ıc´ı objektov´ y typ soubor . soubor = object f:file of prvek; pp: prvek; {hraje roli pˇ rı ´stupov´ e promˇ enn´ e souboru} def: boolean; procedure read(var x: prvek); procedure assign(s: string); procedure write(x: prvek); procedure reset; procedure rewrite; procedure close; function eof: boolean; end; Atribut f pˇredstavuje promˇennou typu soubor tak, jak ji zn´ ame z Turbo Pascalu; pp bude hr´ at roli pˇr´ıstupov´e promˇenn´e a def pouˇzijeme pˇri nov´e definici funkce eof. Pˇri z´ apisu do souboru v tomto algoritmu pˇr´ıstupovou promˇennou nepotˇrebujeme. Metody assign, rewrite, write a close budou prostˇe volat stejnojmenn´e standardn´ı procedury, napˇr.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
88 procedure soubor.assign(s: string); begin system.assign(f,s); end;
Metody pro ˇcten´ı ovˇsem pˇr´ıstupovou promˇennou budou muset vyuˇz´ıvat. Pˇri otevˇren´ı souboru pro ˇcten´ı proto ihned pˇreˇcteme prvn´ı z´ aznam (pokud soubor nˇejak´ y obsahuje) a uloˇz´ıme jej do pp. Pokud je soubor pr´ azdn´ y, vloˇz´ıme do promˇenn´e def hodnotu false. procedure soubor.reset; begin system.reset(f); if not system.eof(f) then begin system.read(f, pp); def := true; end else def := false; end; Procedura soubor.read vr´ at´ı hodnotu pp, pˇreˇcte dalˇs´ı prvek (pokud existuje) a pˇriprav´ı ho do pp. Pokud naraz´ı na konec souboru, uloˇz´ı do def hodnotu false. procedure soubor.read(var x: prvek); begin if def then x := pp; if not system.eof(f) then begin system.read(f, pp); def := true; end else def := false; end; Funkce eof prostˇe vrac´ı hodnotu atributu def, kter´ y signalizuje, zda je nˇeco v pˇr´ıstupov´e promˇenn´e. function soubor.eof; begin eof := not def; end; Procedura pro pˇ rirozen´ e sluˇ cov´ an´ı soubor˚ u Zde uvedeme proceduru Pˇrirozen´eSluˇcov´ an´ı v jednoduˇsˇs´ı podobˇe, s oddˇelenou rozdˇelovac´ı a sluˇcovac´ı f´ az´ı. Tˇr´ıd´ıme soubor C, kter´ y pokl´ ad´ ame za glob´ aln´ı instanci typu soubor. Pomocn´e soubory jsou A a B. Tˇelo procedury Pˇrirozen´eSluˇcov´ an´ı se skl´ ad´ a z opakov´ an´ı rozdˇelov´ an´ı a sluˇcov´ an´ı (procedury, kter´e to prov´ adˇej´ı, se jmenuj´ı v´ ystiˇznˇe Rozdˇel a Sluˇc ). Pˇritom se v promˇenn´e l poˇc´ıtaj´ı bˇehy, vytvoˇren´e pˇri sluˇcov´ an´ı; tˇr´ıdˇen´ı skonˇc´ı, jestliˇze v´ ysledn´ y soubor obsahuje pouze jeden bˇeh. Procedura Rozdˇel v cyklu vol´ a proceduru pro zkop´ırov´ an´ı jednoho bˇehu, Zkop´ırujBˇeh, a stˇr´ıdavˇe kop´ıruje bˇehy do A a B. Procedura Sluˇc v cyklu sluˇcuje bˇehy ze soubor˚ u A a B a zapisuje je do C ; k tomu pouˇzije proceduru SluˇcBˇeh. Pokud obsahuje jeden ze soubor˚ u v´ıce bˇeh˚ u neˇz druh´ y, pˇrekop´ıruje nakonec zbyl´e bˇehy do C. Bˇehy pˇritom poˇc´ıt´ a v promˇenn´e l. Procedura Zkop´ırujBˇeh kop´ıruje jednotliv´e prvky (vol´ a proceduru Zkop´ırujPrvek ), dokud nenaraz´ı na konec bˇehu. Procedura Zkop´ırujPrvek kop´ıruje jednotliv´e prvky z jednoho souboru do druh´eho a pˇritom zjiˇst’uje, zda nenastal konec bˇehu nebo konec souboru (coˇz je tak´e konec bˇehu). Sluˇcovac´ı f´ aze je ponˇekud sloˇzitˇejˇs´ı. Procedura Sluˇc se odvol´ av´ a na proceduru SluˇcBˇeh, kter´ a slouˇc´ı aktu´ aln´ı bˇeh ze souboru A s aktu´ aln´ım bˇehem ze souboru B. Pˇritom v cyklu porovn´ av´ a prvky, uloˇzen´e v pˇr´ıstupov´ ych promˇenn´ ych A a B a menˇs´ı z nich zap´ıˇse do C. Jestliˇze v jednom ze soubor˚ u A, B bˇeh skonˇc´ı, pˇrekop´ıruje zbytek bˇehu ze zb´ yvaj´ıc´ıho souboru do C. K tomu lze pouˇz´ıt proceduru Zkop´ırujBˇeh.
ˇ Sˇ´I TR ˇ ´IDEN ˇ ´I 5.3. VNEJ {****** Tˇ rı ´dˇ en´ ı souboru metodou pˇ rirozen´ eho sluˇ cov´ an´ ı *****} procedure pˇ rirozen´ eSlucovani; var l: integer; kb: boolean; {Indikuje konec bˇ ehu} A, B: soubor; procedure Zkop´ ırujPrvek (var X,Y: soubor); {Kop´ ıruje jednotliv´ y} var buf: prvek; {prvek z X do Y a pˇ ritom zjiˇ st’uje} begin {konec bˇ ehu nebo souboru} X.read(buf); Y.write(buf); if X.eof then kb := true {Test konce souboru} else kb := buf.kl´ ıc ˇ > X.pp.kl´ ıc ˇ; {Test konce bˇ ehu} end; procedure Zkop´ ırujBˇ eh(var X,Y: soubor); begin {zkop´ ırov´ an´ ı jednoho bˇ ehu ze souboru X do Y} repeat Zkop´ ırujPrvek(X,Y) {V cyklu kop´ ıruje prvky,} until kb; {dokud nedojde na konec bˇ ehu} end;{zkop´ ırujBˇ eh} procedure Rozdˇ el; {Rozdˇ elovac´ ı f´ aze} begin {Ze souboru c do soubor˚u a,b} repeat Zkop´ ırujBˇ eh(C,A); {Stˇ rı ´davˇ e kop´ ıruje bˇ ehy} if not C.eof then Zkop´ ırujBˇ eh(C,B) {z C do A nebo B} until C.eof; end; {Rozdˇ el} procedure Sluˇ cBˇ eh; begin {ze soubor˚u A,B do C} repeat {Porovn´ a prvky v pˇ rı ´stu-} if A.pp.kl´ ıc ˇ < B.pp.kl´ ıc ˇ then begin {pov´ ych promˇ enn´ ych} Zkop´ ırujPrvek(A,C); {menˇ sı ´ pˇ rekop´ ıruje} if kb then Zkop´ ırujBˇ eh(B,C) end else begin Zkop´ ırujPrvek(B,C); if kb then Zkop´ ırujBˇ eh(A,C) {Pˇ rekop´ ıruje zbytek} end; until kb; end; {Sluˇ cBˇ eh} procedure Sluˇ c; {Sluˇ covac´ ı f´ aze} begin {Ze soubor˚u A,B do souboru C} while not A.eof and not B.eof do begin Sluˇ cBˇ eh; {Sluˇ cuje bˇ ehy z A a B} inc(l); {a poˇ cı ´t´ a je} end{while 1}; while not A.eof do begin {Pokud m´ a A v´ ıce bˇ eh˚ u neˇ z B} Zkop´ ırujBˇ eh(A,C); {okop´ ıruj zbytek} inc(l); end{while 2}; while not B.eof do begin {Pokud m´ a B v´ ıce bˇ eh˚ u neˇ z A} Zkop´ ırujBˇ eh(B,C); {okop´ ıruj zbytek} inc(l); end{while 3}; end{sluˇ cov´ an´ ı}; begin {pˇ rirozen´ e sluˇ cov´ an´ ı} A.assign(’data.111’); B.assign(’data.222’); repeat {Rozdˇ elovac´ ı f´ aze} A.rewrite; B.rewrite;
89
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
90 C.reset; Rozdˇ el; A.reset; B.reset; C.close; C.rewrite; l:=0; Sluˇ c; until l = 1; end;{pˇ rirozen´ e sluˇ cov´ an´ ı}
{Sluˇ covac´ ı f´ aze}
{Jedin´ y bˇ eh, tj. soubor = bˇ eh: KONEC}
Je zˇrejm´e, ˇze u ´ˇcinnost tohoto algoritmu bude lepˇs´ı neˇz u ´ˇcinnost pˇr´ım´eho sluˇcov´ an´ı. Nejhorˇs´ı pˇr´ıpad nastane, jestliˇze vˇsechny bˇehy v p˚ uvodn´ım souboru budou m´ıt d´elku 1 a pˇri rozdˇelov´ an´ı nebo sluˇcov´ an´ı nikdy nedojde k samovoln´emu slouˇcen´ı“ dvou po sobˇe n´ asleduj´ıc´ıch bˇeh˚ u. V takov´em pˇr´ıpadˇe bude v proceduˇre Sluˇc tˇreba ” O (log2 n) pr˚ uchod˚ u cyklem repeat a tedy celkov´ y poˇcet pˇr´ıstup˚ u do soubor˚ u bude O (n log 2 n). Nejlepˇs´ı pˇr´ıpad nastane, jestliˇze bude vstupn´ı soubor jiˇz uspoˇra ´dan´ y. V takov´em pˇr´ıpadˇe najde sluˇcovac´ı procedura jedin´ y bˇeh a skonˇc´ı po prvn´ım pr˚ uchodu, takˇze budeme potˇrebovat 4n pˇr´ıstup˚ u do souboru. Dalˇ s´ı moˇ zn´ a vylepˇ sen´ı Metody, se kter´ ymi jsme se dosud sezn´ amili, lze oznaˇcit za z´ akladn´ı. Existuj´ı ovˇsem dalˇs´ı, propracovanˇejˇs´ı a ˇ tak´e u ´ˇcinnˇejˇs´ı metody. Rekneme si z´ akladn´ı myˇslenky nˇekter´ ych z nich. Podrobnˇejˇs´ı informace najdete napˇr. v [1] nebo [5]. V´ıcecestn´ e sluˇ cov´ an´ı M´ ame-li k dispozici dostatek voln´eho m´ısta ve vnˇejˇs´ı pamˇeti (dostatek stojan˚ u pro magnetick´e p´ asky), m˚ uˇzeme pouˇz´ıt v´ıce pomocn´ ych soubor˚ u. Jestliˇze v rozdˇelovac´ı f´ azi rozdˇel´ıme zdrojov´ y soubor do N pomocn´ ych soubor˚ u a z nich pak budeme jednotliv´e bˇehy sluˇcovat, dostaneme N-cestn´e sluˇcov´ an´ı. Podobnˇe, jako jsme v pˇr´ıpadˇe metody pˇr´ım´eho sluˇcov´ an´ı odvodili sloˇzitost O(n.log2 n), odvod´ıme pro N-cestn´e sluˇcov´ an´ı sloˇzitost O (n logN n). Poznamenejme, ˇze nejde o ˇra ´dov´e zlepˇsen´ı, nebot’ logN n =
log2 n log2 N .
Polyf´ azov´ e sluˇ cov´ an´ı Tuto metodu navrhl L. R. Gilstadt [28]. Vych´ az´ı ze snahy l´epe vyuˇz´ıt pomocn´ ych soubor˚ u (to je d˚ uleˇzit´e zejm´ena pˇri tˇr´ıdˇen´ı na magnetick´ ych p´ ask´ ach, kde uzavˇren´ı a znovuotevˇren´ı souboru znamen´ a zdlouhav´e pˇrev´ıjen´ı p´ asky). Pod´ıvejme se na sluˇcov´ an´ı ze soubor˚ u A a B do souboru C. Jakmile jsme pˇri pˇrirozen´em sluˇcov´ an´ı narazili na konec souboru A, okop´ırovali jsme zbytek souboru B do C a t´ım skonˇcil pr˚ uchod. Jestliˇze naraz´ıme na konec souboru A pˇri polyf´ azov´em tˇr´ıdˇen´ı, uzavˇreme ho, otevˇreme ho pro z´ apis. Proces sluˇcov´ an´ı bude pokraˇcovat: slouˇc´ıme zbytek souboru B se souborem C a v´ ysledek zapisujeme na A. Protoˇze se n bˇeh˚ u ve vstupn´ım souboru zmˇen´ı na n bˇeh˚ u ve v´ ystupn´ım souboru, staˇc´ı si v´est evidenci o poˇctu bˇeh˚ u na jednotliv´ ych p´ ask´ ach. Proces skonˇc´ı, aˇz dospˇejeme do situace, kdy slouˇc´ıme dva vstupn´ı soubory, obsahuj´ıc´ı po jednom bˇehu. Pouˇzijeme-li v´ıce soubor˚ u, bude postup podobn´ y. Pˇ r´ıklad 5.11 Ve vstupn´ım souboru A je 12 bˇeh˚ u, ve vstupn´ım souboru B ke 7 bˇeh˚ u (viz obr. 5.9). Pˇri prvn´ım pr˚ uchodu se 7 bˇeh˚ u ze souboru B slouˇc´ı s 7 bˇehy ze souboru A a vznikne 7 bˇeh˚ u v souboru C. To znamen´ a, ˇze soubor B je nyn´ı pr´ azdn´ y“ (vyˇcerpali jsme vˇsechny bˇehy), zat´ımco v souboru A zbylo 5 bˇeh˚ u. Budeme tedy pokraˇcovat ” sluˇcov´ an´ım 5 zbyl´ ych bˇeh˚ u ze souboru A se 7 bˇehy z C a v´ ysledek budeme ukl´ adat do B. Ve druh´em pr˚ uchodu se slouˇc´ı 5 bˇeh˚ u z A s 5 bˇehy z C. V´ ysledkem bude pr´ azdn´ y soubor A, v C zbudou 2 bˇehy a B bude obsahovat 5 bˇeh˚ u. Nov´ ym v´ ystupn´ım souborem bude A.
ˇ ´ DALSˇ´I METODY TR ˇ ´IDEN ˇ ´I 5.4. NEKTER E
91
A
B
C
12
7
0
5
0
7
0
5
2
2
3
0
0
1
2
1
0
1
0
1
0
Obr. 5.9: Polyf´ azov´e tˇr´ıdˇen´ı (k pˇr´ıkladu 5.11)
Tˇret´ı pr˚ uchod slouˇc´ı 2 bˇehy z C se dvˇema bˇehy z B, dostaneme 2 bˇehy v A a v B zbudou 3 bˇehy. Soubor C se vypr´ azdn´ı a v pˇr´ıˇst´ım pr˚ uchodu bude slouˇzit jako v´ ystupn´ı. Pˇri ˇctvrt´em pr˚ uchodu se slouˇc´ı 2 bˇehy ze souboru A se dvˇema bˇehy z B a vzniknou dva bˇehy v C. Vypr´ azdn´ı se soubor A, v B zbude jeden bˇeh. P´ at´ y pr˚ uchod slouˇc´ı jedin´ y bˇeh ze souboru B s jedn´ım bˇehem v C. B se vypr´ azdn´ı, v C zbude jeden bˇeh a v A je tak´e jeden bˇeh. Posledn´ı pr˚ uchod slouˇc´ı jedin´ y bˇeh v A s jedin´ ym bˇehem v C. V B je utˇr´ıdˇen´ y soubor. Dalˇs´ı vylepˇsen´ı se mohou t´ ykat rozdˇelen´ı bˇeh˚ u na p´ ask´ ach.
5.4
Nˇ ekter´ e dalˇ s´ı metody tˇ r´ıdˇ en´ı
V tomto odstavci se sezn´ am´ıme s dalˇs´ımi metodami pro tˇr´ıdˇen´ı.
5.4.1
Pˇ rihr´ adkov´ e tˇ r´ıdˇ en´ı
Pˇrihr´ adkov´e tˇr´ıdˇen´ı (bucketsort) lze pouˇz´ıt v pˇr´ıpadˇe, kdy kl´ıˇc, podle kter´eho tˇr´ıd´ıme, m˚ uˇze nab´ yvat pomˇernˇe mal´eho poˇctu hodnot. Bez u ´jmy na obecnosti m˚ uˇzeme pˇredpokl´ adat, ˇze pro vˇsechny prvky a i , kter´e se mohou ve tˇr´ıdˇen´e posloupnosti vyskytnout, plat´ı ˆ = {1, 2, K, M } . ai .kl´ıˇc ∈ M kde M je mal´e pevn´e ˇc´ıslo. Naˇs´ım u ´kolem je opˇet seˇradit danou posloupnost a i , i = 1, . . . , n, podle rostouc´ıho kl´ıˇce. Z´ akladn´ı myˇslenka pˇrihr´ adkov´eho tˇr´ıdˇen´ı je velmi jednoduch´ a. Definujeme N pˇrihr´ adek P 1 , . . . , PM . Nyn´ı budeme postupnˇe proch´ azet posloupnost ai a jednotliv´e prvky budeme rozˇrazovat do odpov´ıdaj´ıc´ıch pˇrihr´ adek.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
92
P1
...
ak P1
zdrojov´a posloupnost
Pm pˇrihr´adky
Pm
spojene pˇrihr´adky
Obr. 5.10: Zdrojov´ a posloupnost se roztˇr´ıd´ı do pˇrihr´ adek a ty se pak slouˇc´ı Prvky s kl´ıˇcem 1 do pˇrihr´ adky P1 , prvky s kl´ıˇcem 2 do pˇrihr´ adky P2 atd. Potom pˇrihr´ adky slouˇc´ıme v poˇrad´ı P1 + P2 + · · · + PM (viz obr. 5.10). Jako pˇrihr´ adky mohou poslouˇzit napˇr. fronty nebo soubory (z´ aleˇz´ı na rozsahu tˇr´ıdˇen´e posloupnosti). D˚ uleˇzit´e je, aby struktura, kterou pouˇzijeme v roli pˇrihr´ adky, zachov´ avala poˇrad´ı, ve kter´em do n´ı byly prvky vloˇzeny; jinak by tento algoritmus nebyl stabiln´ı, mˇenil by poˇrad´ı prvk˚ u, kter´e maj´ı stejnou hodnotu kl´ıˇce. Algoritmus pˇrihr´ adkov´eho tˇr´ıdˇen´ı popisuje procedura Pˇrihr´ adkov´eTˇr´ıdˇen´ı. V nˇem pˇredpokl´ ad´ ame, ˇze m´ ame k dispozici objektov´ y typ fronta, kter´ y implementuje tuto datovou strukturu. Konstruktor fronta.Vytvoˇr vytvoˇr´ı pr´ azdnou frontu, metoda fronta.Vloˇz vloˇz´ı prvek na konec fronty a metoda fronta.Vyjmi vyjme prvek z ˇcela fronty. Booleovsk´ a funkce fronta.Pr´ azdn´ a vr´ at´ı true, je-li fronta pr´ azdn´ a. Tˇr´ıdˇen´ a posloupnost je uloˇzena v poli a. procedure Pˇ rihr´ adkov´ eTˇ rı ´dˇ en´ ı; var P: array [1..M] of fronta; i: 0..n; j: 1..M; begin for j := 1 to M do P[j].Vytvoˇ r; {Vytvoˇ rı ´ pr´ azdn´ e fronty} for i := 1 to n do P[a[i].kl´ ıc ˇ].Vloˇ z(a); {Vloˇ zı ´ a[i] do spr´ avn´ e fronty} i := 0; for j := 1 to M do {Pˇ rep´ ıs ˇe frontu zpˇ et do pole a} while not P[j].Pr´ azdn´ a do begin inc(i); P[j].Vyjmi(a[i]) {Vyjme prvek z P[j] a uloˇ zı ´ do a[i]} end; end; Anal´ yza pˇ rihr´ adkov´ eho tˇ r´ıdˇ en´ı Nejprve se pod´ıvejme na spotˇrebu pamˇeti. Pokud bychom implementovali frontu jako pole, museli bychom poˇc´ıtat s moˇznost´ı, ˇze vˇsechny prvky tˇr´ıdˇen´e posloupnosti mohou m´ıt stejn´ y kl´ıˇc. To znamen´ a, ˇze vˇsechny fronty mus´ı m´ıt d´elku M , takˇze potˇrebujeme pamˇet’ pro celkem M n prvk˚ u nav´ıc. To je zpravidla naprosto nepˇrijateln´e. Mus´ıme tedy frontu implementovat jako seznam. V takov´em pˇr´ıpadˇe budeme potˇrebovat nav´ıc n prvk˚ u (ve skuteˇcnosti vzhledem k administrativˇe“ seznamu o nˇeco m´ alo v´ıce). M˚ uˇzeme ovˇsem oˇcek´ avat, ˇze program ” bude o nˇeco pomalejˇs´ı, neˇz kdybychom pouˇzili pole - opˇet d´ıky administrativˇe“ pˇri pr´ aci se seznamem. ” Nyn´ı se pod´ıv´ ame na poˇcet operac´ı. Kaˇzd´ y prvek jednou vyjmeme z pole a, vypoˇcteme jeho kl´ıˇc a vloˇz´ıme ho do fronty. Na konci jej z fronty vyjmeme a uloˇz´ıme do pole. To znamen´ a, ˇze na kaˇzd´ y prvek pˇripadaj´ı dva
ˇ ´ DALSˇ´I METODY TR ˇ ´IDEN ˇ ´I 5.4. NEKTER E
93
pˇresuny a jeden v´ ypoˇcet kl´ıˇce; celkem tedy 2n pˇresun˚ u a n v´ ypoˇct˚ u kl´ıˇce. Pˇrihr´ adkov´e tˇr´ıdˇen´ı tedy vyˇzaduje O (n) operac´ı.
5.4.2
Lexikografick´ e tˇ r´ıdˇ en´ı
Nejprve mus´ıme definovat lexikografick´e uspoˇra ´d´ an´ı. Je-li D line´ arnˇe uspoˇra ´dan´ a mnoˇzina a m pˇrirozen´e ˇc´ıslo, pak lexikografick´e uspoˇra ´d´ an´ı mnoˇziny D m (tedy mnoˇziny uspoˇra ´dan´ ych m-tic prvk˚ u z mnoˇziny D) je uspoˇra ´d´ an´ı definovan´e tak, ˇze nerovnost (a1 , a2 , . . . , am ) ≤ (b1 , b2 , . . . , bm )
(5.13)
plat´ı, pr´ avˇe kdyˇz se tyto dvˇe m-tice rovnaj´ı, (a1 , a2 , . . . , am ) = (b1 , b2 , . . . , bm ), nebo as ≤ bs pro nejmenˇs´ı index s takov´ y, ˇze as 6= bs . Pˇ r´ıklad 5.12 Pod´ıvejme se na dva pˇr´ıklady lexikografick´eho uspoˇra ´d´ an´ı. Je-li D anglick´ a abeceda, D = {a, b, c, . . . , z}, s uspoˇra ´d´ an´ım a ≤ b ≤ . . . ≤ z, pˇredstavuje D m mnoˇzinu vˇsech ˇretˇezc˚ u (slov) o m p´ısmenech a lexikografick´e uspoˇra ´d´ an´ı znamen´ a obvykl´e uspoˇra ´d´ an´ı podle abecedy. Je-li D mnoˇzina ˇc´ıslic, D = {0, 1, . . . , 9} s obvykl´ ym uspoˇra ´d´ an´ım, pˇredstavuje D m ˇretˇezec m ˇc´ıslic. Kaˇzd´ y z tˇechto ˇretˇezc˚ u m˚ uˇzeme pokl´ adat za z´ apis cel´eho ˇc´ısla v des´ıtkov´e soustavˇe (pˇripouˇst´ıme ˇc´ısla, zaˇc´ınaj´ıc´ı nev´ yznamn´ ymi nulami, napˇr. 00014). Snadno se pˇresvˇedˇc´ıme, ˇze v tomto pˇr´ıpadˇe se lexikografick´e uspoˇra ´d´ an´ı je vlastnˇe obvykl´e uspoˇra ´d´ an´ı cel´ ych ˇc´ısel. Uvaˇzujme nyn´ı posloupnost A1 , A2 , . . . , An , sloˇzenou z prvk˚ u D m . Tyto posloupnost m-tic chceme setˇr´ıdit na z´ akladˇe lexikografick´eho uspoˇra ´d´ an´ı (5.13). Algoritmus lexikografick´eho tˇr´ıdˇen´ı je velmi jednoduch´ y, i kdyˇz na prvn´ı pohled neˇcekan´ y. Posloupnost A1 , A2 , . . . , An budeme opakovanˇe tˇr´ıdit pomoc´ı algoritmu pˇrihr´ adkov´eho tˇr´ıdˇen´ı podle posledn´ı sloˇzky m-tice, pak podle pˇredposledn´ı sloˇzky, podle (m − 2)-t´e sloˇzky atd. Ze stability pˇrihr´ adkov´eho tˇr´ıdˇen´ı plyne, ˇze pˇri prvn´ım pr˚ uchodu budou ve v´ ysledn´e posloupnosti prvky A 1 v poˇrad´ı, urˇcen´em posledn´ım znakem. Ve druh´em pr˚ uchodu budou ve v´ ysledn´e posloupnosti prvky A 1 v poˇrad´ı, urˇcen´em pˇredposledn´ım znakem; pokud vˇsak bude u Ak a Al stejn´ y pˇredposledn´ı znak, budou ve stejn´em poˇrad´ı, jako byly po prvn´ım pr˚ uchodu - tedy v poˇrad´ı, urˇcen´em posledn´ım znakem. Tuto u ´vahu m˚ uˇzeme zopakovat i pro dalˇs´ı pr˚ uchody; z n´ı plyne, ˇze v´ ysledkem bude lexikograficky setˇr´ıdˇen´ a posloupnost. Algoritmus lexikografick´eho tˇr´ıdˇen´ı zap´ıˇseme opˇet v Pascalu. Pˇritom pˇredpokl´ ad´ ame, ˇze prvek tˇr´ıdˇen´eho pole a [i] obsahuje pole m kl´ıˇc˚ u. procedure Lexikografick´ eTˇ rı ´dˇ en´ ı; var P: array [1..M] of fronta; i: 0..n; j: 1..M; l: 1..k; begin for l := k downto 1 do begin {Opakovan´ a pˇ rihr´ adkov´ a tˇ rı ´dˇ en´ ı} for j := 1 to M do P[j].Vytvoˇ r; {Vytvoˇ rı ´ pr´ azdn´ e fronty} for i := 1 to n do P[a[i].kl´ ıc ˇ[l]].Vloˇ z(a); {Vloˇ zı ´ a[i] do spr´ avn´ e fronty} i := 0; for j := 1 to M do {Pˇ rep´ ıs ˇe frontu zpˇ et do pole a} while not P[j].Pr´ azdn´ a do begin inc(i); P[j].Vyjmi(a[i]) {Vyjme prvek z P[j] a uloˇ zı ´ do a[i]} end; end; end; Je-li poˇcet kl´ıˇc˚ u m a mnoˇzstv´ı moˇzn´ ych hodnot kl´ıˇc˚ u M pevn´e, potˇrebujeme m pr˚ uchod˚ u pˇrihr´ adkov´eho tˇr´ıdˇen´ı. Sloˇzitost lexikografick´eho tˇr´ıdˇen´ı tedy je mO (n) = O (n).
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
94 Pozn´ amka
Z definice lexikografick´eho tˇr´ıdˇen´ı by se mohlo zd´ at, ˇze je rozumn´e zaˇc´ıt porovn´ an´ım prvn´ıch kl´ıˇc˚ u, pokud se shoduj´ı, tak porovnat druh´e kl´ıˇce atd. Jestliˇze se takov´ y algoritmus pokus´ıte navrhnout, zjist´ıte, ˇze bude velmi komplikovan´ y. Pozn´ amka o abecedn´ım tˇ r´ıdˇ en´ı Jako nejdelˇs´ı ˇcesk´e slovo se obvykle uv´ ad´ı nejnedoobhospodaˇrov´ avatelnˇejˇs´ımi, kter´e m´ a 32 p´ısmen. Mohlo by se tedy zd´ at, ˇze na abecedn´ı tˇr´ıdˇen´ı abecedn´ı tˇr´ıdˇen´ı ˇcesk´ ych slov staˇc´ı vz´ıt ˇretˇezce dlouh´e ˇreknˇeme 60 znak˚ u (abychom pokryli i v´ yrazy sloˇzen´e z nˇekolika slov) a pouˇz´ıt algoritmu lexikografick´eho tˇr´ıdˇen´ı. Situace je bohuˇzel ponˇekud komplikovanˇejˇs´ı, a to nejen d´ıky spˇreˇzce ch, kter´ a se ch´ ape jako jedno p´ısmeno. ˇ Postup pˇri abecedn´ım ˇrazen´ı v ˇceˇstinˇe v ˇceˇstinˇe a slovenˇstinˇe je pˇredeps´ an st´ atn´ı normou [29], alespoˇ n v Cesku st´ ale platnou. V tomto odstavci si pov´ıme o nejd˚ uleˇzitˇejˇs´ıch z´ asad´ ach abecedn´ıho ˇrazen´ı, kter´e z t´eto normy plynou. Za prim´ arn´ı tˇr´ıdic´ı znaky se pokl´ adaj´ı p´ısmena tzv. ˇcesk´e a slovensk´e standardizovan´e unifikovan´e abecedy a b c ˇc d e f g h ch i j k l m n o p q r ˇr s ˇs t u v w x y z ˇz P´ısmena v t´eto abecedˇe neuveden´ a, a ´¨ a d’ ´e ˇe ´ı ´l l’ n ˇo ´o ˆ ´r t’ u ´˚ uy ´ maj´ı sekund´ arn´ı tˇr´ıdic´ı platnost. To znamen´ a, ˇze je nejprve ch´ apeme jako p´ısmena bez diakritick´ ych znam´enek (podobnˇe se zach´ az´ı i s p´ısmeny s diakritick´ ymi znam´enky jin´ ych n´ arodn´ıch abeced, vˇcetnˇe nerozepsan´ ych pˇrehl´ asek; to znamen´ a, ˇze t’uh´yk je v abecedˇe pˇred tygrem). K tomu, ˇze jde o odliˇsn´e znaky, pˇrihl´ıˇz´ıme pouze v pˇr´ıpadˇe jinak zcela totoˇzn´ ych hesel. Dalˇs´ı pravidla ˇr´ıkaj´ı, v jak´em poˇrad´ı se ˇrad´ı hesla, kter´ a se liˇs´ı diakritick´ ymi znam´enky na r˚ uzn´ ych m´ıstech, r˚ uzn´ ymi diakritick´ ymi znam´enky na t´emˇze m´ıstˇe, pouˇzit´ım mal´ ych a velk´ ych p´ısmen apod. Tato pravidla jsou sice komplikovan´ a, ale lze je bez probl´em˚ u zvl´ adnout. Co ale ˇcin´ı z ˇcesk´eho abecedn´ıho ˇrazen´ı z´ aleˇzitost v podstatˇe algoritmicky neˇreˇsitelnou, jsou napˇr. pravidla pro zach´ azen´ı s ˇc´ıslicemi. Napˇr. heslo 10 poh´ adek se bude ˇradit jako DESET poh´ adek, nebot’ jde o slovn´ı souˇca ´st ” n´ azvu“ knihy, zat´ımco Geometrie pro 8. roˇcn´ık se zaˇrad´ı pˇred heslo Geometrie pro 9. roˇcn´ık, nebot’ zde ˇc´ıslice ” v´ yraznˇe urˇcuj´ı poˇrad´ı“ (citov´ ano podle [29], str. 8). Jinou lah˚ udkou je zach´ azen´ı s ˇc´ınsk´ ymi jm´eny, kter´e se ch´ apou jako jedno slovo, i kdyˇz obsahuj´ı mezery. Z tohoto pov´ıd´ an´ı plynou dvˇe mravn´ı nauˇcen´ı: 1. Abecedn´ı tˇr´ıdˇen´ı tak, jak je pops´ ano v [29], je z´ avisl´e tak´e na v´ yznamu ˇrazen´ ych hesel. To znamen´ a, ˇze pokud m´ ame pouze soubor ˇretˇezc˚ u a budeme jej tˇr´ıdit poˇc´ıtaˇcem, z´ akonitˇe se dopust´ıme prohˇreˇsk˚ u proti t´eto normˇe. V bˇeˇzn´ ych souvislostech se ale prohˇreˇsky v detailech toleruj´ı a pˇri tˇr´ıdˇen´ı napˇr. hesel ve slovn´ıku lze vˇzdy k heslu pˇripojit kl´ıˇc, kter´ y zp˚ usob tˇr´ıdˇen´ı jednoznaˇcnˇe pˇredep´ıˇse (napˇr. k heslu 10 poh´ adek pˇripoj´ıme pomocn´e heslo Deset poh´ adek, podle kter´eho ho budeme tˇr´ıdit). 2. Procedura pro porovn´ an´ı dvou ˇretˇezc˚ u podle pravidel ˇcesk´eho abecedn´ıho tˇr´ıdˇen´ı bude natolik komplikovan´ a, ˇze pˇri tˇr´ıdˇen´ı m˚ uˇze zabrat v´ıce ˇcasu neˇz pˇresun z´ aznamu v pamˇeti.
5.4.3
Topologick´ e tˇ r´ıdˇ en´ı
Topologick´e tˇr´ıdˇen´ı se pouˇz´ıv´ a jako oznaˇcen´ı pro tˇr´ıdˇen´ı ˇca ´steˇcnˇe uspoˇra ´dan´ ych posloupnost´ı. Neˇz se pust´ıme do dalˇs´ıho v´ ykladu, zavedeme si potˇrebn´e pojmy. ˇ asteˇ C´ cn´ e uspoˇ r´ ad´ an´ı ˇ Rekneme, ˇze mnoˇzina S je ˇca ´steˇcnˇe uspoˇra ´dan´ a, je-li pro nˇekter´e dvojice (x, y) ∈ S × S definov´ ana relace “, ” kter´ a splˇ nuje n´ asleduj´ıc´ı podm´ınky:
ˇ ´ DALSˇ´I METODY TR ˇ ´IDEN ˇ ´I 5.4. NEKTER E
95
2 3
1 4
5
Obr. 5.11: Orientovan´ y graf, zn´ azorˇ nuj´ıc´ı ˇca ´steˇcn´e uspoˇra ´d´ an´ı (5.14) z pˇr´ıkladu 5.13
1
5
3
2
4
Obr. 5.12: Utˇr´ıdˇen´ a ˇca ´steˇcnˇe uspoˇra ´dan´ a mnoˇzina z pˇr´ıkladu 5.13 1. Relace
”
“ je tranzitivn´ı: plat´ı-li pro nˇejak´ a x, y, z ∈ S z´ aroveˇ n relace x
2. Relace “ je asymetrick´ a: jestliˇze pro nˇejak´ a x, y ∈ S plat´ı relace x ” y x. 3. Relace
”
“ je ireflexivn´ı: pro ˇza ´dn´e x ∈ S neplat´ı relace x
yay
z, plat´ı tak´e x
z.
y, nem˚ uˇze platit z´ aroveˇ n relace
x.
ˇ asteˇcn´e uspoˇra Relace se pochopitelnˇe naz´ yv´ a ˇca ´steˇcn´e uspoˇra ´d´ an´ı. C´ ´d´ an´ı koneˇcn´e mnoˇziny lze zn´ azornit orienˇ tovan´ ym grafem; pˇr´ıklad vid´ıme na obr. 5.11. Sipky, zn´ azorˇ nuj´ıc´ı orientaci hran, smˇeˇruj´ı k uzlu, zn´ azorˇ nuj´ıc´ımu vˇetˇs´ı“ prvek (prvek na prav´e stranˇe znaku “). Z podm´ınek, kter´e definuj´ı ˇca ´steˇcn´e uspoˇra ´d´ an´ı, plyne, ˇze ” ” takov´ yto graf nesm´ı obsahovat cykly. Pˇ r´ıklad 5.13 Vezmeme mnoˇzinu M = {1, 2, 3, 4, 5} a na n´ı definujeme ˇca ´steˇcn´e uspoˇra ´d´ an´ı tak, ˇze pˇr´ımo vyjmenujeme dvojice, kter´e ho tvoˇr´ı: 1 5, 1 3, 3 2, 3 4, 5 2, 2 4 (5.14) Toto ˇca ´steˇcn´e uspoˇra ´d´ an´ı lze zn´ azornit orientovan´ ym grafem na obr. 5.11. ˇ asteˇcn´e Jin´ y pˇr´ıklad ˇca ´steˇcn´eho uspoˇra ´d´ an´ı: Vezmˇeme mnoˇzinu vˇsech podmnoˇzin R nepr´ azdn´e mnoˇziny T . C´ uspoˇra ´d´ an´ı mnoˇziny R definuje relace ⊂“ (A ⊂ B zde znamen´ a A je vlastn´ı podmnoˇzinou B“). ” ” ˇ asteˇcn´e uspoˇra C´ ´d´ an´ı bychom tak´e mohli zav´est pro axiomy a matematick´e vˇety, kter´e z nich plynou. Relaci A B by pak odpov´ıdal vztah z tvrzen´ı A plyne tvrzen´ı B“. ” Topologick´ e tˇ r´ıdˇ en´ı Chceme-li tˇr´ıdit posloupnost ai , pro jej´ıˇz prvky je definov´ ano ˇca ´steˇcn´e uspoˇra ´d´ an´ı, mus´ıme toto ˇca ´steˇcn´e uspoˇra ´d´ an´ı vnoˇrit do nˇejak´eho line´ arn´ıho uspoˇra ´d´ an´ı. To znamen´ a uspoˇra ´dat prvky posloupnosti ai tak, aby pro kaˇzdou dvojici ai , aj ze vztahu i < j plynulo, ˇze bud’ plat´ı ai aj nebo mezi ai a aj nen´ı relace “ ” definov´ ana. Je jasn´e, ˇze v´ ysledek topologick´eho tˇr´ıdˇen´ı nemus´ı b´ yt jednoznaˇcnˇe definov´ an. Utˇr´ıdˇen´ı ˇca ´steˇcnˇe uspoˇra ´dan´e posloupnost´ı m˚ uˇzeme zn´ azornit orientovan´ ym grafem, ve kter´em budou vˇsechny uzly leˇzet v jedn´e pˇr´ımce a vˇsechny ˇsipky, vyjadˇruj´ıc´ı orientaci hran, budou smˇeˇrovat doprava (viz napˇr. obr. 5.12 ). Uk´ aˇzeme si, jak m˚ uˇze vypadat algoritmus pro topologick´e tˇr´ıdˇen´ı. Ve vstupn´ım souboru m´ ame seznam relac´ı ve tvaru uspoˇra ´dan´ ych dvojic, popisuj´ıc´ıch relace ˇca ´steˇcn´eho uspoˇra ´d´ an´ı. Soubor konˇc´ı ˇc´ıslem 0. Chceme tyto u ´daje setˇr´ıdit a vytisknout. Pro jednoduchost budeme pˇredpokl´ adat, ˇze jde o cel´ a ˇc´ısla. Napˇr. vstupn´ı soubor, obsahuj´ıc´ı ˇc´ısla 1, 5, 1, 3, 3, 2, 3, 4, 5, 2, 2, 4 popisuje mnoˇzinu {1, . . . , 5}, na kter´e je definov´ ano ˇca ´steˇcn´e uspoˇra ´d´ an´ı vztahy (5.14) z pˇr´ıkladu 5.13.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
96
Ze zad´ an´ı plyne, ˇze prvky tˇr´ıdˇen´e posloupnosti jsou poloˇzky, kter´e se ve vstupn´ım souboru vyskytuj´ı alespoˇ n jednou. Pˇri tˇr´ıdˇen´ı vyuˇzijeme dynamick´e datov´e struktury S, odvozen´e od jednosmˇern´eho seznamu. Algoritmus, kter´ y pouˇzijeme, m˚ uˇzeme shrnout do n´ asleduj´ıc´ıch tˇr´ı bod˚ u: 1. Prvky pˇri vstupu ze souboru ukl´ ad´ ame do seznamu S. O kaˇzd´em prvku pr˚ ubˇeˇznˇe zjiˇst’ujeme, kolik m´ a v dan´em ˇca ´steˇcn´em uspoˇra ´d´ an´ı pˇredch˚ udc˚ u a n´ asledovn´ık˚ u. 2. Potom v S vyhled´ ame prvky, kter´e nemaj´ı ˇza ´dn´eho pˇredch˚ udce ( nejmenˇs´ı prvky“). Takov´ y prvek mus´ı ” b´ yt ve vstupn´ım souboru - a tady v S - alespoˇ n jeden, nebot’ jinak by graf tohoto uspoˇra ´d´ an´ı obsahoval cyklus. 3. Tyto nejmenˇs´ı“ prvky vytiskneme a odstran´ıme z S. Zbude m´ am ˇca ´steˇcnˇe uspoˇra ´dan´ a mnoˇzina, takˇze v ” n´ı opˇet vyhled´ ame nejmenˇs´ı“ prvky, tj. prvky, kter´e nemaj´ı pˇredch˚ udce, a odstran´ıme je. Tento postup ” budeme opakovat aˇz do u ´pln´eho vypr´ azdnˇen´ı S. Seznam S bude obsahovat o kaˇzd´em z prvk˚ u tyto u ´daje: hodnotu prvku, mnoˇzinu jeho n´ asledovn´ık˚ u a poˇcet jeho pˇredch˚ udc˚ u v dan´em ˇca ´steˇcn´em uspoˇra ´d´ an´ı. Informace o n´ asledn´ıc´ıch jednotliv´ ych prvk˚ u tˇr´ıdˇen´e mnoˇziny se budeme dozv´ıdat postupnˇe pˇri ˇcten´ı vstupn´ıho souboru. Proto pˇripoj´ıme ke kaˇzd´emu prvku mnoˇziny pomocn´ y seznam jeho n´ asledovn´ık˚ u, nebo jeˇstˇe l´epe seznam odkaz˚ u na n´ asledovn´ıky. Strukturu, kter´ a bude prvek seznamu reprezentovat, nazveme hlavn´ı, nebot’ bude hlavn´ı sloˇzkou seznamu. Vedle toho budeme ale potˇrebovat strukturu pomocn´y pro reprezentaci prvk˚ u pomocn´ ych seznam˚ u n´ asledovn´ık˚ u. Jejich deklarace budou type uHlavn´ ı = ^Hlavn´ ı; uPomocn´ y = ^Pomocn´ y; Hlavn´ ı = record kl´ ıc ˇ, poˇ cet: integer; upom: uPomocn´ y; dalˇ sı ´: uHlavn´ ı; end; Pomocn´ y = record id: uHlavn´ ı; dalˇ sı ´: uPomocn´ y; end;
{Prvek hlavn´ ıho seznamu} {Kl´ ıc ˇ a poˇ cet pˇ redch˚udc˚u} {Odkaz na seznam n´ asledovn´ ık˚u} {Odkaz na dalˇ sı ´ prvek} {Prvek seznamu n´ asledovn´ ık˚u} {Ukazatel na n´ asledovn´ ıka} {Ukazatel na dalˇ sı ´ prvek seznamu}
Sloˇzka kl´ıˇc obsahuje prvek tˇr´ıdˇen´e posloupnosti. Na obr. 5.13 vid´ıme seznam, kter´ y se vytvoˇril po pˇreˇcten´ı prvn´ı dvojice 1, 5 ze vstupn´ıho souboru. Prvn´ı prvek seznamu obsahuje poloˇzku 1, ˇza ´dn´ a poloˇzka nen´ı menˇs´ı. Jedin´ y prvek pˇripojen´eho pomocn´eho seznamu obsahuje ukazatel na vˇetˇs´ı poloˇzku, v tomto pˇr´ıpadˇe na 5. Druh´ y prvek seznamu obsahuje poloˇzku 5 a informaci, ˇze m´ a jedin´eho pˇredch˚ udce. Protoˇze nem´ a (zat´ım) n´ asledovn´ıky, je pˇripojen´ y pomocn´ y seznam pr´ azdn´ y. Cel´ y program se bude skl´ adat ze ˇctyˇr etap; po pˇr´ıpravn´ ych operac´ıch pˇreˇcteme vstupn´ı soubor a na z´ akladˇe u ´daj˚ u, kter´e obsahuje, vytvoˇr´ıme seznam prvk˚ u a relac´ı mezi nimi. Ve tˇret´ı etapˇe sestav´ıme seznam prvk˚ u, kter´e nemaj´ı ˇza ´dn´e pˇredch˚ udce (jsou ve smyslu dan´eho ˇca ´steˇcn´eho uspoˇra ´d´ an´ı nejmenˇs´ı“). ” V posledn´ım kroku odstran´ıme ze seznamu nejmenˇs´ı prvky. Z´ aroveˇ n samozˇrejmˇe v n´ asledovn´ıc´ıch odstranˇen´ ych prvk˚ u zmenˇs´ıme odpov´ıdaj´ıc´ım zp˚ usobem u ´daje o poˇctu pˇredch˚ udc˚ u. V´ ysledkem bude opˇet ˇca ´steˇcnˇe uspoˇra ´dan´ a mnoˇzina, takˇze bude zase obsahovat alespoˇ n jeden nejmenˇs´ı prvek. Tento postup budeme opakovat aˇz do vypr´ azdnˇen´ı seznamu.
Hlavn´ ı program tedy bude begin Pˇ rı ´prava; Vytvoˇ rSeznam; NajdiNejmenˇ sı ´; Vypiˇ sSeznam; end.
ˇ ´ DALSˇ´I METODY TR ˇ ´IDEN ˇ ´I 5.4. NEKTER E
97
z´avˇer ˇcelo kl´ıˇc poˇcet pˇredch˚ udc˚ u spojka
1 0
5 1
ukazatel na seznam potomk˚ u
ukazatel na vˇetˇs´ı prvek spojka
zar´aˇzka
Obr. 5.13: po pˇreˇcten´ı prvn´ı dvojice ze vstupn´ıho souboru Tentokr´ at nepouˇzijeme objektov´ ych typ˚ u. To znamen´ a, ˇze mus´ıme definovat ukazatele na poˇca ´tek a konec seznamu jako glob´ aln´ı promˇenn´e (budou se jmenovat ˇcelo a z´ avˇer a budou typu uHlavn´ı). Vedle toho definujeme glob´ aln´ı promˇennou z typu integer, kter´ a bude slouˇzit jako poˇc´ıtadlo prvk˚ u v seznamu. Procedura Pˇr´ıprava smaˇze obrazovku a vytvoˇr´ı pr´ azdn´ y seznam se zar´ aˇzkou: procedure Pˇ rı ´prava; begin clrscr; new(celo); {vytvoˇ ren´ ı pr´ azdn´ eho seznamu hlavn´ ıch prvk˚ u} zaver := celo; z := 0; {poˇ cı ´tadlo prvk˚ u} end; V proceduˇre VytvoˇrSeznam pˇreˇcteme vˇzdy jednu dvojici x, y ze souboru f . Pod´ıv´ ame se, zda je x jiˇz v seznamu; pokud tam nen´ı, pˇrid´ ame ho tam. D´ ale se pod´ıv´ ame, zda je v seznamu y, a pokud nen´ı, zaˇrad´ıme ho. Z´ aroveˇ n k x pˇripoj´ıme informaci, ˇze jeho n´ asledovn´ıkem je y (tj. do pomocn´eho seznamu, pˇripojen´eho k x, vloˇz´ıme prvek s adresou y) a u prvku y zv´ yˇs´ıme poˇcet pˇredch˚ udc˚ u. Pro vyhled´ an´ı prvku v seznamu a jeho pˇr´ıpadn´e zaˇrazen´ı pouˇzijeme funkci JeVSeznamu, kter´ a z´ aroveˇ n vr´ at´ı adresu tohoto prvku: function JeVSeznamu(w: integer): uHlavn´ ı; {urˇ cen´ ı hlavn´ ıho prvku s kl´ ıc ˇem w a pˇ rı ´padnˇ e zaˇ razen´ ı do seznamu} var h: uHlavn´ ı; begin {JeVSeznamu} h := c ˇelo; z´ avˇ er^.kl´ ıc ˇ := w; {definice zar´ az ˇky} while h^.kl´ ıc ˇ <> w do h := h^.dalˇ sı ´; {hledani} if h = z´ avˇ er then begin {prvek nen´ ı v seznamun - pˇ ridej ho} new(z´ avˇ er); inc(z); h^.poˇ cet := 0; h^.upom := nil; h^.dalˇ sı ´ := z´ avˇ er; {zar´ az ˇka se stala posledn´ ım prvkem, vytvoˇ rı ´ se nov´ a} end; JeVSeznamu := h; end; {JeVSeznamu}
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
98
Procedura VytvoˇrSeznam ˇcte jednotliv´e dvojice, zaˇrazuje je do seznamu a pˇripojuje informace o pˇredch˚ udc´ıch a n´ asledovn´ıc´ıch. Pˇritom pˇredpokl´ ad´ ame, ˇze u ´daje jsou uloˇzeny v souboru topo.dta procedure Vytvoˇ rSeznam; var p, q: uHlavn´ ı; t: uPomocn´ y; x, y: integer; f: text; begin {Vstup} assign(f, ’topo.dta’); {otevˇ ren´ ı souboru topo.dta} reset(f); read(f,x); while x <> 0 do begin {Vstupn´ ı soubor konˇ cı ´ nulou} read(f,y); writeln(x,’ ’,y); p := JeVSeznamu(x); q := JeVSeznamu(y); new(t); {Odkaz na nov´ eho n´ asledovn´ ıka} t^.id := q; t^.dalˇ sı ´ := p^.upom; p^.upom := t; inc(q^.poˇ cet); {Informace o poˇ ctu pˇ redch˚udc˚u} read(f,x) end; close(f); end; V dalˇs´ım kroku vyhled´ ame nejmenˇs´ı prvky, a spoj´ıme je do nov´eho seznamu. K tomu vyuˇzijeme jejich spojek (odkaz˚ u na n´ asleduj´ıc´ı prvky), nebot’ p˚ uvodn´ı seznamovou strukturu uˇz nebudeme potˇrebovat. Ukazatelem na prvn´ı prvek nov´eho seznamu bude promˇenn´ a ˇcelo. (Nemus´ıme se b´ at, ˇze tak zniˇc´ıme strukturu seznamu a ztrat´ıme spojen´ı na nˇekter´e jeho ˇca ´sti; kaˇzd´ y z nejmenˇs´ıch prvk˚ u m´ a nˇejak´e n´ asledovn´ıky a odkazy na nˇe jsou v pˇripojen´em pomocn´em seznamu. Snadno zjist´ıme, ˇze se touto cestou m˚ uˇzeme dostat ke vˇsem prvk˚ um seznamu. Spojen´ı nejmenˇs´ıch prvk˚ u do nov´eho seznamu obstar´ a procedura NajdiNejmenˇs´ı. Prvky vkl´ ad´ a na zaˇca ´tek seznamu, takˇze nyn´ı budou v obr´ acen´em poˇrad´ı neˇz v p˚ uvodn´ı struktuˇre. procedure NajdiNejmenˇ sı ´; var p, q: uHlavn´ ı; begin {vyhled´ an´ ı hlavn´ ıch prvk˚ u s poˇ ctem = 0 pˇ redch˚udc˚u} p := c ˇelo; c ˇelo := nil; while p <> z´ avˇ er do begin {Prohled´ a aˇ z po zar´ az ˇku} q := p; p := p^.dalˇ sı ´; if q^.poˇ cet = 0 then begin q^.dalˇ sı ´ := c ˇelo; c ˇelo := q; end; end; end; Nyn´ı zb´ yv´ a vypsat prvky ve spr´ avn´em poˇrad´ı (nebo je zpracovat jin´ ym zp˚ usobem). Procedura VypiˇsSeznam vezme prvn´ı prvek, vyp´ıˇse ho a v n´ asledovn´ıc´ıch, uveden´ ych v pomocn´em seznamu, sn´ıˇz´ı poˇcet pˇredch˚ udc˚ u (sloˇzka poˇcet). Jestliˇze v nˇekter´em z n´ asledovn´ık˚ u klesne poˇcet pˇredch˚ udc˚ u na 0, pˇrid´ a ho do seznamu nejmenˇs´ıch prvk˚ u. procedure Vypiˇ sSeznam; var p, q: uHlavn´ ı; t: uPomocn´ y; begin q := celo;{v´ ystupn´ ı f´ aze}
ˇ ´ DALSˇ´I METODY TR ˇ ´IDEN ˇ ´I 5.4. NEKTER E
99
z´avˇer ˇcelo 1 0
5 1
2 2
4 2
3 1
Obr. 5.14: Vytvoˇren´ y seznam while q <> nil do begin writeln(q^.kl´ ıc ˇ); dec(z); {Odpoˇ cti zpracovan´ y prvek} t := q^.upom; q := q^.dalˇ sı ´; while t <> nil do begin {Prob´ ır´ ame n´ asledovn´ ıky} p := t^.id; dec(p^.poˇ cet); if p^.poˇ cet = 0 then begin {Uˇ z nem´ a pˇ redch˚udce - pˇ rid´ ame ho} p^.dalˇ sı ´ := q; {do seznamu prvk˚ u bez pˇ redch˚udc˚u} q := p; end; t := t^.dalˇ sı ´; end; end; if z <> 0 then writeln(’Tato mnoˇ zina nen´ ı c ˇa ´steˇ cnˇ e uspoˇ ra ´dan´ a’); end; Jestliˇze v seznamu nenajdeme prvek bez pˇredch˚ udc˚ u z pˇritom seznam nen´ı pr´ azdn´ y, nebyla mnoˇzina ˇca ´steˇcnˇe uspoˇra ´dan´ a; graf relac´ı obsahoval cyklus. Poznamenejme, ˇze v tomto jednoduch´em programu jsme se nestarali o zruˇsen´ı seznamu po skonˇcen´ı programu. ˇ aˇr se m˚ Cten´ uˇze pokusit upravit jej tak, abychom mohli prvky po zpracov´ an´ı uvolnit pomoc´ı procedury dispose, a to i v pˇr´ıpadˇe, ˇze vstupn´ı data nedefinovala ˇca ´steˇcn´e uspoˇra ´d´ an´ı. Pˇ r´ıklad 5.14 Pˇredpokl´ adejme, ˇze ve vstupn´ım souboru jsou dvojice, pro pˇrehlednost dopln´ıme znak “, vyznaˇcuj´ıc´ı uspo” ˇra ´d´ an´ı: 1
5, 5
2, 1
3, 2
4, 3
2, 3
4.
Jde vlastnˇe o uspoˇra ´d´ an´ı (14), dvojice jsou ale v jin´em poˇrad´ı. Procedura VytvoˇrSeznam vytvoˇr´ı seznam, kter´ y vid´ıte na obr. 5.14 Procedura NajdiNejmenˇs´ı tento seznam nezmˇen´ı, nebot’ ˇcelo ukazuje shodou okolnost´ı na jedin´ y nejmenˇs´ı prvek. Procedura VypiˇsSeznam vyp´ıˇse hodnotu nejmenˇs´ıho prvku, 1, a v jeho n´ asledovn´ıc´ıch podle uspoˇra ´d´ an´ı sn´ıˇz´ı u ´daj o poˇctu pˇredch˚ udc˚ u. Pˇritom zjist´ı, ˇze prvky s kl´ıˇci 5 a 3 uˇz nemaj´ı ˇza ´dn´eho pˇredch˚ udce a zaˇrad´ı je do seznamu. Strukturu seznamu po t´eto u ´pravˇe vid´ıte na obr. 5.15. Vˇsimnˇete si, ˇze prvek 2 je nyn´ı dostupn´ y pouze jako n´ asledovn´ık prvku 5 nebo 3.
ˇ ´IDEN ˇ ´I KAPITOLA 5. TR
100
q
z´avˇer ˇcelo 1 0
5 0
2 2
3 0
Obr. 5.15: Seznam po zpracov´ an´ı prvku 1
4 2
Kapitola 6
Pouˇ zit´ı bin´ arn´ıho stromu S bin´ arn´ım stromem a z´ akladn´ımi algoritmy pro pr´ aci s n´ım jsme se setkali jiˇz v kapitole 2.2.2. Zde si uk´ aˇzeme nˇekter´e dalˇs´ı vlastnosti a pouˇzit´ı t´eto datov´e struktury.
6.1
Vyv´ aˇ zen´ e stromy
Bin´ arn´ı strom oznaˇc´ıme za dokonale vyv´ aˇzen´ y, jestliˇze pro jeho libovoln´ y vrchol v plat´ı, ˇze poˇcet vrchol˚ uv lev´em a prav´em podstromu vrcholu v se liˇs´ı nejv´ yˇse o 1. Nen´ı obt´ıˇzn´e sestrojit statick´ y dokonale vyv´ aˇzen´ y strom, tedy strom, u nˇehoˇz pˇredem zn´ ame poˇcet vrchol˚ u a ten se nebude mˇenit. (Jde o u ´lohu podobnou konstrukci optim´ aln´ıho vyhled´ avac´ıho stromu, se kterou se setk´ ame v 6.2.2.) Znaˇcn´e probl´emy ovˇsem nastanou, jestliˇze se bude strom v pr˚ ubˇehu sv´e existence mˇenit, tj. jestliˇze do nˇej budeme cht´ıt pˇrid´ avat vrcholy nebo je ruˇsit. Proto se obvykle pouˇz´ıvaj´ı slabˇs´ı definice vyv´ aˇzenosti. Jednu z nich navrhli Adelson-Velskij a Landis [32] a stromy, vyv´ aˇzen´e podle jejich definice, se (podle nich) oznaˇcuj´ı jako AVL-stromy nebo jako AVL-vyv´ aˇzen´e stromy. Protoˇze ˇza ´dnou jinou definici vyv´ aˇzenosti nebudeme pouˇz´ıvat, budeme o nich hovoˇrit prostˇe jako o vyv´ aˇzen´ych stromech. Tedy: Strom je vyv´ aˇzen´y, pr´ avˇe kdyˇz se v´yˇsky obou podstrom˚ u, pˇripojen´ych k jeho libovoln´emu vrcholu, liˇs´ı nejv´yˇse o 1. Autoˇri AVL-strom˚ u dok´ azali, ˇze v´ yˇska AVL-stromu nikdy nepˇres´ ahne 1,45-n´ asobek v´ yˇsky dokonale vyv´ aˇzen´eho stromu, sloˇzen´eho ze stejn´ ych vrchol˚ u. To znamen´ a, ˇze obvykl´e operace, jako je pˇrid´ an´ı vrcholu do stromu, vyhled´ an´ı nebo zruˇsen´ı vrcholu lze prov´est v ˇcase O (log2 n), kde n je poˇcet vrchol˚ u. Vyhled´ av´ an´ı ve vyv´ aˇzen´em stromu se neliˇs´ı od vyhled´ av´ an´ı v obyˇcejn´em“ bin´ arn´ım stromu. Pˇri pˇrid´ av´ an´ı ” nebo ruˇsen´ı vrchol˚ u se ovˇsem z vyv´ aˇzen´eho stromu m˚ uˇze st´ at strom nevyv´ aˇzen´ y. Pod´ıv´ ame se, jak se napravuje rovnov´ aha, poruˇsen´ a pˇri pˇrid´ av´ an´ı vrchol˚ u; postup pˇri obnoven´ı rovnov´ ahy po zruˇsen´ı vrcholu bude podobn´ y.
6.1.1
Pˇ rid´ av´ an´ı vrchol˚ u do vyv´ aˇ zen´ eho stromu
M´ ame tedy strom, kter´ y byl vyv´ aˇzen´ y, a do kter´eho jsme pˇridali jeden vrchol. Pro urˇcitost pˇredpokl´ adejme, ˇze jsme jej pˇridali lev´eho podstromu. Jestliˇze si oznaˇc´ıme K koˇren, L resp. P lev´ y resp. prav´ y podstrom a v L resp. vP jejich v´ yˇsky, bude pˇred pˇrid´ an´ım vrcholu platit jedna z n´ asleduj´ıc´ıch tˇr´ı moˇznost´ı: 1. vL = vP . Po pˇrid´ an´ı bude vL o jedniˇcku vˇetˇs´ı neˇz vP , nicm´enˇe strom z˚ ustane vyv´ aˇzen´ y. 2. vL < vP . Po pˇrid´ an´ı bude vL = vP , strom z˚ ustal vyv´ aˇzen´ y. 3. vL > vP . Po pˇrid´ an´ı bude vL = vP + 2, takˇze strom jiˇz nen´ı vyv´ aˇren´ y - je tˇreba zjednat n´ apravu. Pˇri vyvaˇzov´ an´ı mus´ıme zachovat relativn´ı poˇrad´ı vrchol˚ u ve stromu, nebot’ to odpov´ıd´ a poˇrad´ı kl´ıˇc˚ u. To znamen´ a, ˇze jedin´e pˇr´ıpustn´e z´ asahy budou rotace vrchol˚ u, pˇri kter´ ych napˇr. vrchol L nahrad´ı K, koˇren K pˇrejde do prav´eho podstromu apod. 101
ˇ ´I BINARN ´ ´IHO STROMU KAPITOLA 6. POUZIT
102
4 5 5 4
7
7 Obr. 6.1: Jednoduch´ a rotace P P
5 4
5 2
7 1
2
7 4
1 Obr. 6.2: Jednoduch´ a rotace LL Pˇ r´ıklad 6.1 V tomto pˇr´ıkladu si uk´ aˇzeme vˇsechny situace, kter´e mohou nastat (podle [1]). Uvaˇzujme pr´ azdn´ y AVL-strom, do kter´eho budeme postupnˇe pˇrid´ avat vrcholy s hodnotami 4, 5, 7, 2, 1, 3, 6. Po pˇrid´ an´ı vrcholu 7 vznikne poprv´e nevyv´ aˇzen´ y strom, kter´ y vid´ıte na obr. 6.4 vlevo. N´ apravy dos´ ahneme jednoduchou rotac´ı vpravo, kterou oznaˇc´ıme P P (prodlouˇzil se prav´ y podstrom prav´eho podstromu). Po pˇrid´ an´ı vrcholu 2 z˚ ustane strom vyv´ aˇzen´ y. Po pˇrid´ an´ı vrcholu 1 se rovnov´ aha opˇet poruˇs´ı (obr. 6.4), takˇze mus´ıme pouˇz´ıt jednoduchou rotaci doleva (LL, nebot’ se prodlouˇzil se lev´ y podstrom lev´eho podstromu). Po pˇrid´ an´ı vrcholu 3 poruˇs´ı vyv´ aˇzenost koˇrene, tj. vrcholu 5. K n´ apravˇe nyn´ı pouˇzijeme dvojitou rotaci, kterou oznaˇc´ıme LP (vyrostl lev´ y podstrom prav´eho podstromu, obr. 6.4). Nejprve uprav´ıme lev´ y podstrom s koˇrenem 2 tak, aby se jeho koˇrenem stal vrchol 4. Pak posuneme koˇren cel´eho stromu - t´ım se stane 4. Posledn´ı pˇridan´ a hodnota je 6. Tentokr´ at se objev´ı nerovnov´ aha v lev´em podstromu prav´eho podstromu, takˇze pouˇzijeme rotaci P L. Postup je zrcadlov´ ym obrazem rotace LP (obr. 6.4). Pˇri pr´ aci s vyv´ aˇzen´ ym stromem budeme potˇrebovat u ´daje o tom, o kolik se liˇs´ı v´ yˇska lev´eho a prav´eho podstromu. Proto pˇrid´ ame do kaˇzd´eho z´ aznamu poloˇzku vyv´ aˇzenost, kter´ a bude obsahovat rozd´ıl v´ yˇsky prav´eho a lev´eho podstromu (v tomto poˇrad´ı). Ve sloˇzce poˇcet budeme ukl´ adat poˇcet v´ yskyt˚ u prvku s dan´ ym kl´ıˇcem. type uVrchol = ^vrchol; vrchol = record kl´ ıc ˇ: integer; poˇ cet:integer;
2 1
4
7
1
2
7 1
2
4 3
4
5
5
3
Obr. 6.3: Dvojit´ a rotace LP
5 3
7
´ ZEN ˇ E ´ STROMY 6.1. VYVA
103
4 2 1
4 5
2 7
3
1
6 3
5
7
6 Obr. 6.4: Dvojit´ a rotace P L lev´ y, prav´ y: uVrchol; vyv´ az ˇenost: -1..1; end; Pˇri vkl´ ad´ an´ı budeme potˇrebovat informaci o tom, zda se v´ yˇska podstromu zmˇenila. K tomu pouˇzijeme pomocn´ y parametr h typu boolean, kter´ y budeme pˇred´ avat hodnotou. Jestliˇze pˇrid´ ame prvek do lev´eho podstromu koˇrene K a v´ yˇska tohoto podstromu se zvˇetˇs´ı, bude potˇrebn´e strom znovu vyv´ aˇzit, jestliˇze bude vyvenost = −1. Jestliˇze jsme pˇrid´ avali do prav´eho podstromu a jeho v´ yˇska se zvˇetˇsila, bude potˇrebn´e strom znovu vyv´ aˇzit, bude-li vyvenost = 1. Podle hodnoty vyv´ aˇzenost v koˇreni podstromu pak urˇc´ıme, kterou z rotac´ı pouˇzijeme. V n´ asleduj´ıc´ı proceduˇre vloˇz jsou rotace vyj´ adˇreny jako vnoˇren´e procedury LL, LR atd. procedure vloˇ z(x: integer; var p: uVrchol; var v: boolean); var p1, p2: uVrchol; {na poˇ ca ´tku mus´ ı b´ yt v = false} procedure LL; {lev´ y podstrom lev´ eho podstromu} begin p^.lev´ y := p1^.prav´ y; p1^.prav´ y := p; p^.vyv´ az ˇenost := 0; p := p1; end; {LL} procedure RR; {prav´ y podstrom prav´ eho podstromu} begin p^.prav´ y := p1^.lev´ y; p1^.lev´ y := p; p^.vyv´ az ˇenost := 0; p := p1; end; {RR} procedure LR; {lev´ y podstrom prav´ eho podstromu} begin p2 := p1^.prav´ y; p1^.prav´ y := p2^.lev´ y; p2^.lev´ y := p1; p^.lev´ y := p2^.prav´ y; p2^.prav´ y := p; if p2^.vyv´ az ˇenost = -1 then p^.vyv´ az ˇenost := 1 else p^.vyv´ az ˇenost := 0; if p2^.vyv´ az ˇenost = +1 then p1^.vyv´ az ˇenost := -1 else p1^.vyv´ az ˇenost := 0; p := p2; end; {LRLLR} procedure RL; {prav´ y podstrom lev´ eho podstromu} begin p2 := p1^.lev´ y; p1^.lev´ y := p2^.prav´ y;
104
ˇ ´I BINARN ´ ´IHO STROMU KAPITOLA 6. POUZIT p2^.prav´ y := p1; p^.prav´ y := p2^.lev´ y; p2^.lev´ y := p; if p2^.vyv´ az ˇenost = +1 then p^.vyv´ az ˇenost := -1 else p^.vyv´ az ˇenost := 0; if p2^.vyv´ az ˇenost = -1 then p1^.vyv´ az ˇenost := +1 else p1^.vyv´ az ˇenost := 0; p := p2; end; {RL} begin {vloˇ z} if p=nil then begin {kl´ ıc ˇ nen´ ı ve stromu, pˇ rid´ a se} new(p); v := true; with p^ do begin kl´ ıc ˇ := x; poˇ cet := 1; lev´ y := nil; prav´ y := nil; vyv´ az ˇenost := 0; end; end; else if x < p^.kl´ ıc ˇ then begin vloˇ z(x, p^.lev´ y,v); if v then {zvˇ etˇ sil se lev´ y podstrom} case p^.vyv´ az ˇenost of 1: begin dec(p^.vyv´ az ˇenost); v := false; end; 0: dec(p^.vyv´ az ˇenost); -1: begin {vyvaˇ zov´ an´ ı} p1 := p^.lev´ y; if p1^.vyv´ az ˇenost = -1 then LL else LR; p^.vyv´ az ˇenost := 0; v := false; end; end; {case} end else if x > p^.kl´ ıc ˇ then begin vloˇ z(x, p^.prav´ y,v); if v then {zvˇ etˇ sil se prav´ y podstrom} case p^.vyv´ az ˇenost of -1: begin inc(p^.vyv´ az ˇenost); v := false; end; 0: inc(p^.vyv´ az ˇenost); 1: begin {vyvaˇ zov´ an´ ı} p1 := p^.prav´ y; if p1^.vyv´ az ˇenost = +1 then RR else RL; p^.vyv´ az ˇenost := 0; v := false; end; end; {case} end else begin inc(p^.poˇ cet); v := false; end;
´ AN ´ ´I V BINARN ´ ´IM STROMU 6.2. VYHLEDAV
105
i
i−1
n−i
Obr. 6.5: Bin´ arn´ı strom s n´ ahodnˇe zvolen´ ym koˇrenem i end;{vloˇ z} Je zˇrejm´e, ˇze operace s vyv´ aˇzen´ ym stromem jsou podstatnˇe sloˇzitˇejˇs´ı neˇz operace s obyˇcejn´ ym“ stromem. ” Proto se tyto stromy vyplat´ı budovat zejm´ena tehdy, kdyˇz vyhled´ av´ an´ı pˇrevaˇzuje nad pˇrid´ av´ an´ım nebo ruˇsen´ım vrchol˚ u. Tak´e v pˇr´ıpadˇe, ˇze se pouˇz´ıv´ a zhuˇstˇen´eho ukl´ ad´ an´ı z´ aznam˚ u, pˇrest´ avaj´ı b´ yt vyv´ aˇzen´e stromy v´ yhodn´e - operace pˇr´ıstupu ke zhuˇstˇen´ ym z´ aznam˚ um mohou b´ yt velmi neefektivn´ı.
6.2 6.2.1
Vyhled´ av´ an´ı v bin´ arn´ım stromu Anal´ yza vyhled´ av´ an´ı v bin´ arn´ım stromu
V pˇredchoz´ım odd´ılu jsme se setkali s vyv´ aˇzen´ ymi stromy. Protoˇze pr´ ace s nimi neb´ yv´ a efektivn´ı, pod´ıv´ ame se na obyˇcejn´e“ stromy. Nejprve ale definujeme dokonal´ y strom. ” Bin´ arn´ı strom s k u ´rovnˇemi oznaˇc´ıme jako dokonal´ y, jestliˇze vˇsechny vrcholy na u ´rovn´ıch 1, . . . , k − 1 maj´ı dva n´ asledovn´ıky. Vrcholu na k-t´e u ´rovni jsou pochopitelnˇe listy. Dokonal´ y strom m´ a n = 2 k − 1 vrchol˚ u - nejvˇetˇs´ı moˇzn´ y poˇcet vrchol˚ u ze vˇsech bin´ arn´ıch strom˚ u s k u ´rovnˇemi. Je to zvl´ aˇstn´ı pˇr´ıpad dokonale vyv´ aˇzen´eho stromu. Nejdelˇs´ı cesta, kterou mus´ıme pˇri vyhled´ av´ an´ı v dokonal´em bin´ arn´ım stromˇe proj´ıt, m´ a d´elku k ≈ log2 n. Pr˚ umˇern´ a d´elka cesty v dokonal´em stromˇe bude podle vztahu (2.1) z kap. 2.2.2. k
sn =
k2k+1 − (k + 1) 2k + 1 (k − 1) 2k + 1 1 X i−1 i2 = = ≈ k − 1 ≈ log2 n − 1 k n i=1 2 −1 2k − 1
(6.1)
P Pk k d i−1 i v Souˇcet v (6.1) snadno vypoˇcteme, jestliˇze si uvˇedom´ıme, ˇze 2 je hodnota funkce x i=1 i=1 dx bodˇe x = 2. Pr˚ umˇern´ a d´elka cesty je tedy pˇribliˇznˇe rovna dvojkov´emu logaritmu poˇctu uzl˚ u. Na druh´e stranˇe dokonal´e stromy se vyskytuj´ı opravdu zˇr´ıdka. Strom roste dynamicky podle toho, jak v nˇem ukl´ ad´ ame u ´daje -au ´daje mohou pˇrich´ azet nesm´ırnˇe chaoticky. V nejhorˇs´ım pˇr´ıpadˇe m˚ uˇze m´ıt kaˇzd´ y vrchol pouze jedin´eho n´ asledovn´ıka, takˇze m´ısto stromu vznikne line´ arn´ı seznam. To se stane napˇr. v pˇr´ıpadˇe, ˇze budeme ukl´ adat data seˇrazen´ a podle velikosti. Potom bude k vyhled´ an´ı u ´daje potˇreba v nejhorˇs´ım pˇr´ıpadˇe n, v pr˚ umˇern´em pˇr´ıpadˇe n/2 porovn´ an´ı (pˇredpokl´ ad´ ame, ˇze vˇsechny poloˇzky se hledaj´ı stejnˇe ˇcasto). Nejhorˇs´ı pˇr´ıpad, kdy vznikne m´ısto stromu seznam, je ovˇsem podobnˇe m´ alo pravdˇepodobn´ y jako nejlepˇs´ı pˇr´ıpad, kdy vznikne dokonal´ y strom. Pod´ıv´ ame se proto, jak vypad´ a d´elka cesty, tj. poˇcet operac´ı, v pr˚ umˇern´em pˇr´ıpadˇe. Vezmeme libovolnou permutaci mnoˇziny n ˆ = {1, . . . , n} a vytvoˇr´ıme z n´ı bin´ arn´ı strom; protoˇze permutac´ı mnoˇziny n ˆ je n!, mus´ıme uvaˇzovat n! strom˚ u, kter´e budou vˇsechny stejnˇe pravdˇepodobn´e. Pravdˇepodobnost, ˇze se koˇrenem stane ˇc´ıslo i, je rovna 1/n. Potom bude m´ıt lev´ y podstrom i − 1 vrchol˚ ua prav´ y podstrom n − i vrchol˚ u (obr. 6.4). Oznaˇc´ıme an hledanou pr˚ umˇernou d´elku cesty ve stromu s n vrcholy. Pravdˇepodobnosti pˇr´ıstupu k jednotliv´ ym vrchol˚ um pokl´ ad´ ame za stejn´e, rovn´e 1/n. Pr˚ umˇernou d´elku cesty pro i-t´ y vrchol m˚ uˇzeme poˇc´ıtat jako souˇcet souˇcin˚ uu ´rovnˇe vrcholu (tedy d´elky cesty) a pravdˇepodobnosti pˇr´ıstupu k nˇemu, tj.
ˇ ´I BINARN ´ ´IHO STROMU KAPITOLA 6. POUZIT
106
n
an = kde pi je d´elka cesty pro i-t´ y vrchol.
1X pi n i=1
(6.2)
Vrcholy stromu, kter´ y vid´ıte na obr. 6.5, m˚ uˇzeme rozdˇelit na tˇri skupiny: 1. Vrcholy lev´eho podstromu. Jejich pr˚ umˇern´ a d´elka cesty je ai−1 + 1 (jedniˇcka nav´ıc je za cestu do koˇrene cel´eho stromu). V tomto stromu je i − 1 vrchol˚ u, to znamen´ a, ˇze pravdˇepodobnost pˇr´ıstupu k nˇekter´emu z vrchol˚ u v tomto podstromu je (i − 1) /n. 2. Koˇren stromu. D´elka cesty je 1, pravdˇepodobnost pˇr´ıstupu 1/n. 3. Vrcholy prav´eho podstromu. Pr˚ umˇern´ a d´elka cesty pro n − i vrchol˚ u v tomto podstromu je pravdˇepodobnost pˇr´ıstupu k nˇekter´emu z vrchol˚ u v tomto podstromu je (n − i) /n.
a n−i + 1,
To znamen´ a, ˇze pr˚ umˇernou d´elku cesty (pˇri pevnˇe zvolen´em i) m˚ uˇzeme vyj´ adˇrit jako souˇcet tˇr´ı ˇclen˚ u: 1 n − i i − 1 + 1 + (an−i + 1) an(i) = (ai−1 + 1) n n n
(6.3)
(i)
Celkovou pr˚ umˇernou d´elku an potom m˚ uˇzeme vyj´ adˇrit jako pr˚ umˇer z hodnot an pˇres vˇsechna i, i = 1, . . . , n, to znamen´ a pˇres vˇsechny stromy s hodnotami 1, 2, . . . , n v koˇreni. Dostaneme n n 1X i−1 1 X (i) 1 n−i a = (ai−1 + 1) (6.4) an = + + (an−i + 1) n i=1 n n i=1 n n n V hranat´ ych z´ avork´ ach m˚ uˇzeme vytknout 1/n a seˇc´ıst v´ yrazy, kter´e nez´ avis´ı na a k . Tak dostaneme an = 1 +
n n n−1 1 X 2 X 2 X [(i − 1) a + (n − i) a ] = 1 + (i − 1) a = 1 + iai . i−1 n−i i−1 n2 i=1 n2 i=1 n2 i=0
(6.5)
V (6.5) jsme poˇc´ıtali dva stejn´e souˇcty v opaˇcn´em poˇrad´ı, proto jsme je nahradili jedin´ ym souˇctem. Tak jsme dospˇeli k vyj´ adˇren´ı tvaru an = f (an−1 , an−2 , . . . , a1 ), kter´e se ale pro v´ ypoˇcet an pˇr´ıliˇs nehod´ı. Pokus´ıme se z nˇej z´ıskat vztah tvaru an = g (an−1 ). Z (6.5) plyne an = 1 +
n−1 n−2 2 2 X 2 X ia = 1 + iai , (n − 1) a + i n−1 n2 i=0 n2 n2 i=0
an−1 = 1 + Vyj´ adˇr´ıme-li ze vztahu (6.7) souˇcet
Pn−2
2 (n − 1)
2
n−2 X
iai .
(6.6)
(6.7)
i=0
iai a dosad´ıme-li za nˇej do (6.6), dostaneme 1 an = 2 n2 − 1 an−1 + 2n − 1 (6.8) n Vztah (6.8) pˇredstavuje line´ arn´ı diferenˇcn´ı rovnici. Pouˇzijeme obvykl´eho postupu ˇreˇsen´ı. Nejprve vyˇreˇs´ıme rovnici bez prav´e strany, n2 − 1 bn = bn−1 (6.9) n2 Poloˇz´ıme-li b1 = β, odvod´ıme snadno matematickou indukc´ı, ˇze 22 − 1 32 − 1 2n − 1 22 − 1 , b3 = β , . . . , bn = β b2 = β 2 2 22 32 2n D´ ale potˇrebujeme naj´ıt jak´ekoli ˇreˇsen´ı rovnice s pravou stranou (6.8). Pˇritom pouˇzijeme analogii metody variace konstant, zn´ am´e z teorie diferenci´ aln´ıch rovnic. Pˇredpokl´ ad´ ame, ˇze ˇreˇsen´ı m´ a tvar a n = Kn bn = Kn (n + 1) /2n a po dosazen´ı do (6.8) a u ´pravˇe dostaneme pro posloupnost Kn rekurentn´ı vztah (diferenˇcn´ı rovnici) 3 1 Kn − Kn−1 = − n+1 n Snadno se pˇresvˇedˇc´ıme, ˇze ˇreˇsen´ı t´eto rovnice lze m´ a tvar 3H n+1 − Hn , kde Hn je posloupnost n X 1 Hn = i i=1 i=0
´ AN ´ ´I V BINARN ´ ´IM STROMU 6.2. VYHLEDAV
107
Z toho pak plyne, ˇze ˇreˇsen´ı rovnice (6.8) m´ a tvar an = βbn + Kn bn = β
n+1 n+1 + (3Hn+1 − Hn ) n n
(6.10)
Protoˇze posloupnost Hn se pro velk´ a n chov´ a pˇribliˇznˇe jako pˇrirozen´ y logaritmus n, bude pro pomˇer cesty dokonal´eho stromu sn a stˇredn´ı hodnoty cesty n´ ahodn´eho stromu an platit an 2 ln n lim = lim = 2 ln 2 ≈ 1, 39. n→∞ sn n→∞ log2 n Pr˚ umˇern´e zhorˇsen´ı tedy nen´ı pˇr´ıliˇs v´ yrazn´e, pˇredstavuje pouh´ ych 39%. Pˇresto v pˇr´ıpadˇe algoritm˚ u, u kter´ ych je ˇcas kritick´ y (napˇr. pˇri ˇr´ızen´ı proces˚ u v re´ aln´em ˇcase) m˚ uˇze b´ yt nepˇr´ıjemn´e.
6.2.2
Bin´ arn´ı vyhled´ avac´ı stromy
´ Uvodn´ ıu ´vahy V pˇredchoz´ım odstavci jsme vych´ azeli z pˇredpokladu, ˇze pˇr´ıstup ke vˇsem vrchol˚ um je stejnˇe pravdˇepodobn´ y. To ale nemus´ı b´ yt vˇzdy pravda. Nˇekdy se setk´ ame se stromy, kter´e se v pr˚ ubˇehu programu nemˇen´ı a kter´e slouˇz´ı k rychl´emu vyhled´ av´ an´ı informac´ı. Napˇr´ıklad pˇri pˇrekladu programu nar´ aˇz´ıme ve zdrojov´em textu identifik´ atory a potˇrebuje zjiˇst’ovat, zda to jsou kl´ıˇcov´ a slova. Uspoˇra ´d´ ame proto vˇsechna kl´ıˇcov´ a slova do bin´ arn´ıho stromu a kaˇzd´ y nalezen´ y identifik´ ator budeme nejprve hledat v tomto stromu. Pokud v nˇem nen´ı, nejde o kl´ıˇcov´e slovo. Takov´eto stromy budeme oznaˇcovat jako vyhled´ avac´ı. Pˇresn´ a definice vyhled´ avac´ıho stromu vypad´ a takto: Bin´ arn´ı vyhled´ avac´ı strom (BVS ) je bin´ arn´ı strom, pro kter´ y plat´ı: 1. Vˇsechny poloˇzky, uloˇzen´e v lev´em podstromu, jsou menˇs´ı neˇz poloˇzka v koˇreni. 2. Vˇsechny poloˇzky, uloˇzen´e v prav´em podstromu, jsou vˇetˇs´ı neˇz poloˇzka v koˇreni. 3. Lev´ y a prav´ y podstrom jsou opˇet bin´ arn´ı vyhled´ avac´ı stromy. Vzhledem k tomu, ˇze se BVS pouˇz´ıvaj´ı nejˇcastˇeji v kompil´ atorech, budeme o poloˇzk´ ach, uloˇzen´ ych ve vyhled´ avac´ıch stromech, d´ ale hovoˇrit jako o identifik´ atorech“. Uk´ aˇzeme si, jak sestrojit optim´ aln´ı BVS za pˇredpok” ladu, ˇze zn´ ame pravdˇepodobnosti pouˇzit´ı identifik´ ator˚ u (kl´ıˇcov´ ych slov), kter´ a v nˇem budou uloˇzena. Pˇritom ale mus´ıme zn´ at i pravdˇepodobnosti ne´ uspˇeˇsn´eho vyhled´ av´ an´ı, tj. pravdˇepodobnosti vyhled´ av´ an´ı identifik´ atoru, kter´ y ve stromˇe nen´ı. V BVS budou uloˇzeny identifik´ atory a1 , a2 , . . . , an pro kter´e plat´ı uspoˇra ´d´ an´ı a1 < a2 < . . . < an . Pro u ´plnost jeˇstˇe definujeme a0 = −∞, an+1 = +∞. Pˇredpokl´ ad´ ame, ˇze pravdˇepodobnosti, ˇze identifik´ ator x je roven jedn´e z hodnot uloˇzen´ ych v BVS, jsou Pi = P (x = ai ) Identifik´ atory, kter´e ve stromˇe nejsou, se rozdˇel´ı na tˇr´ıdy ekvivalence E i , Ei = {x |ai < x < ai+1 } , i = 0, 1, . . . , n V dalˇs´ıch u ´vah´ ach budeme muset tak´e vz´ıt v potaz pravdˇepodobnosti Q i ne´ uspˇeˇsn´eho vyhled´ av´ an´ı, tj. pravdˇepodobnosti, ˇze pro identifik´ ator x bude platit ai < x < ai+1 : Qi = P (x ∈ Ei ) , i = 0, 1, . . . n Identifik´ ator x v BVS bud’ najdeme (pak je x = ai pro nˇejak´e i) nebo nenajdeme, a pak pro nˇejak´e i plat´ı ai < x < ai+1 . Jin´ a moˇznost nen´ı, a proto mus´ı pro souˇcet pravdˇepodobnost´ı Pi a Qi platit n X (Pi + Qi ) + Q0 = 1. i=1
Poznamenejme, ˇze BVS m˚ uˇzeme ch´ apat jako bin´ arn´ı strom, ve kter´em ne´ uspˇeˇsn´ a vyhled´ av´ an´ı konˇc´ı ve zvl´ aˇstn´ıch vrcholech, jak jsme je zavedli v odst. 2.2.2. (obr. 6.6).
ˇ ´I BINARN ´ ´IHO STROMU KAPITOLA 6. POUZIT
108
a4 a2
ai a3
a1
ai E4
E0
E1
E3
E2
E5
E6
Obr. 6.6: Bin´ arn´ı vyhled´ avac´ı strom Vyhled´ avac´ı procedura a cena stromu Je-li BVS sloˇzen z vrchol˚ u typu vrchol, a data, uloˇzen´ a ve vrcholech BVS, jsou typu data, m˚ uˇzeme pro vyhled´ av´ an´ı pouˇz´ıt funkci (nap´ıˇseme ji pro zmˇenu v jazyku C) typedef vrchol *uVrchol; uVrchol zjisti(uVrchol T, data X) { uVrchol i; i = T; //d je sloˇ zka vrcholu obsahuj´ ıc´ ı data while(i != NULL) if (X < i->d) i = i->Levy; else if (X > i->d) i = i->Pravy; else return i; return i; } Tato funkce vr´ at´ı bud’ adresu vrcholu, ve kter´em je uloˇzena hodnota X, nebo NULL, jestliˇze X ve stromˇe nen´ı. Je-li vyhled´ av´ an´ı u ´spˇeˇsn´e, tj. jestliˇze se hodnota X najde, skonˇc´ıme v norm´ aln´ım vrcholu stromu na l-t´e u ´rovni. To znamen´ a, ˇze probˇehlo l iterac´ı cyklu while v proceduˇre zjisti. Je-li vyhled´ av´ an´ı ne´ uspˇeˇsn´e, tj. skonˇc´ıme-li ve zvl´ aˇstn´ım uzlu stromu na u ´rovni l, probˇehlo pouze l − 1 iterac´ı cyklu while. To n´ am umoˇzn ˇuje definovat cenovou funkci pro dan´ y strom S: n n X X C (S) = Pi u (ai ) + Qi (u (Ei ) − 1) , i=1
(6.11)
i=0
kde u (ai ) je u ´roveˇ n vrcholu ai ; pˇripomeˇ nme si, ˇze koˇren je na u ´rovni 1. Pˇ r´ıklad 6.2 Uvaˇzujme jednoduch´ y programovac´ı jazyk, kter´ y obsahuje pouze tˇri kl´ıˇcov´ a slova if, do a while. Tato kl´ıˇcov´ a slova m˚ uˇzeme uspoˇra ´dat do pˇeti vyhled´ avac´ıch strom˚ u, kter´e vid´ıte na obr´ azc´ıch 6.7 Jestliˇze bude Pi = Qi = 1/7, bude C (b) = 13/7, ceny ostatn´ıch strom˚ u budou 15/7. Jak jsme mohli oˇcek´ avat, nejv´ yhodnˇejˇs´ı je v tomto pˇr´ıpadˇe pravideln´ y strom (b). Bude-li vˇsak P 1 = 0, 5, P2 = 0, 1, P3 = 0, 05, Q0 = 0, 15, Q1 = 0, 1, Q2 = Q3 = 0, 05, bude C (a) = 2, 65, C (b) = 1, 9, C (c) = 1, 5, C (d) = 2, 05, C (e) = 1, 6. V tomto pˇr´ıpadˇe je nejv´ yhodnˇejˇs´ı strom, kter´ y m´ a tvar seznamu! Optim´ aln´ı vyhled´ avac´ı strom Na konstrukci optim´ aln´ıho vyhled´ avac´ıho stromu se m˚ uˇzeme d´ıvat jako na probl´em dynamick´eho programov´ an´ı: prvn´ım rozhodnut´ım urˇc´ıme, kter´ y vrchol bude koˇrenem (pˇresnˇeji kter´ yu ´daj uloˇz´ıme do koˇrene). Necht’ je to ak .
´ AN ´ ´I V BINARN ´ ´IM STROMU 6.2. VYHLEDAV
while
109
do
if
if
while
do
if
(b) do
(a)
(c) while do
while
do (d)
(e)
if
while if
Obr. 6.7: Moˇzn´e bin´ arn´ı vyhled´ avac´ı stromy pro dan´ a kl´ıˇcov´ a slova
ak
L
P
Obr. 6.8: Volba koˇrene T´ım, ˇze jsme zvolili koˇren, se strom rozpadl na tˇri ˇca ´sti: koˇren a dva podstromy. Cena lev´eho resp. prav´eho podstromu bude k−1 k−1 n n X X X X C (L) = Pi u (ai ) + Qi (u (Ei ) − 1) , C (P ) = Pi u (ai ) + Qi (u (Ei ) − 1) . (6.12) i=1
i=0
i=k+1
i=k
Tvar tohoto stromu zn´ azorˇ nuje obr. 6.8. Nyn´ı urˇc´ıme cenu cel´eho stromu. S pravdˇepodobnost´ı Pk z˚ ustaneme v koˇreni ak . Jinak m˚ uˇzeme j´ıt do lev´eho podstromu - pak bude cena souˇctem ceny lev´eho podstromu a ceny za prˇrchod z koˇrene do nˇej, nebo m˚ uˇzeme j´ıt do prav´eho podstromu - a cena bude souˇctem ceny prav´eho podstromu a ceny za pr˚ uchod do nˇej. Cena za pr˚ uchod do lev´eho resp. prav´eho podstromu je rovna souˇcinu d´elky cesty (1 krok) a pravdˇepodobnosti, ˇze c´ılov´ y vrchol leˇz´ı v tomto podstromu, tedy k−1 X Q0 + (Pi + Qi ) = W (0, k − 1) , i=1
resp.
Qk +
n X
(Pi + Qi ) = W (k, n) .
i=k+1
Obecnˇe definujeme funkci W (i, j) vztahem W (i, j) = Qi +
j X
(Pl + Ql ) .
l=i+1
Cenu cel´eho stromu S tedy m˚ uˇzeme vyj´ adˇrit rovnic´ı C (S) = Pk + W (0, k − 1) + W (k, n) + C (L) + C (P ) .
(6.13)
Naˇs´ım c´ılem je zkonstruovat BVS, pro kter´y bude C (S) minim´ aln´ı. Protoˇze volbou koˇrene a k je pevnˇe d´ ano rozdˇelen´ı vrchol˚ u do lev´eho a prav´eho podstromu, je jasn´e, ˇze i C (L) a C (P ) mus´ı b´ yt minim´ aln´ı. (Plat´ı tedy princip optimality.)
ˇ ´I BINARN ´ ´IHO STROMU KAPITOLA 6. POUZIT
110
Jestliˇze se n´ am podaˇr´ı zvolit vhodnˇe ak , stoj´ıme pˇred podobn´ ym u ´kolem: urˇcit koˇreny lev´eho a prav´eho podstromu. Opakov´ an´ım tohoto postupu dospˇejeme nakonec k podstrom˚ um sloˇzen´ ym z jedin´eho uzlu. Abychom mohli tuto minimalizaˇcn´ı u ´lohu vyˇreˇsit, definujeme C (i, j) jako cenu optim´ aln´ıho podstromu S ij , sloˇzen´eho z uzl˚ u ai+1 , . . . , aj a Ei , . . . , Ej . Odtud pak plyne pro cenu cel´eho stromu S = S0n C (0, n) = min = {C (0, k − 1) + C (k, n) + Pk + W (0, k − 1) + W (k, n)} . (6.14) l
Obecnˇe pro podstrom Sij vzhledem k definici W (i, j) plat´ı C (i, j) = =
min {C (i, k − 1) + C (k, j) + Pk + W (i, k − 1) + W (k, j)} =
(6.15)
min {C (i, k − 1) + C (k, j)} + W (i, j)
(6.16)
i
Rovnici (6.16) lze ˇreˇsit pro C (0, n) tak, ˇze nejprve spoˇcteme vˇsechna C (i, j) pro i − j = 1, pak pro i − j = 2 atd. To znamen´ a, ˇze nejprve sestroj´ıme vˇsechny optim´ aln´ı stromy z jednoho vrcholu, pak optim´ aln´ı stromy ze dvou vrchol˚ u atd. Pˇ r´ıklad 6.3 Sestroj´ıme optim´ aln´ı bin´ arn´ı vyhled´ avac´ı strom pro mnoˇzinu kl´ıˇcov´ ych slov a i ={do, if, real, while} (je tedy n = 4). Pˇritom v´ıme, ˇze pravdˇepodobnosti Pi jsou 3, 3, 1, 1 a pravdˇepodobnosti Qi jsou 2, 3, 1, 1, 1 (pro pohodl´ı jsme vˇsechny pravdˇepodobnosti vyn´ asobili 16). Oznaˇc´ıme R (i, j) vrchol S ij . Z definice W (i, j) plyne, ˇze W (i, i) = Qi pro libovoln´e i = 0, . . . , n. D´ ale pak C (i, i) = 0 pro libovoln´e i, nebot’ strom Sii neobsahuje ˇza ´dn´ y vrchol. D´ ale se budeme ˇr´ıdit vztahem (6.16) a vezmeme v u ´vahu, ˇze W (i, j) = Pj + Qj + W (i, j − 1) . Pro stromy sloˇzen´e z jednoho vrcholu rovnici (6.16) vlastnˇe ani nepouˇzijeme. Dostaneme postupnˇe W (0, 1) = P1 + Q1 + W (0, 0) = 8, C (0, 1) = W (0, 1) + min {C (0, 0) + C (1, 1)} = 8 R (0, 1) = 1,
W (1, 2) = P2 + Q2 + W (1, 1) = 7, C (1, 2) = W (1, 2) + min {C (1, 1) + C (2, 2)} = 7 R (1, 2) = 2,
W (2, 3) = P3 + Q3 + W (2, 2) = 3, C (2, 3) = W (2, 3) + min {C (2, 2) + C (3, 3)} = 3 R (2, 3) = 3,
W (3, 4) = P4 + Q4 + W (3, 3) = 3, C (3, 4) = W (3, 4) + min {C (3, 3) + C (4, 4)} = 3 R (3, 4) = 4.
Jestliˇze nyn´ı zn´ ame W (i, i + 1) a C (i, i + 1) pro i = 0, . . . , 3, m˚ uˇzeme pouˇz´ıt rovnici (6.16) a vypoˇc´ıtat W (i, i + 2), C (i, i + 2) a R (i, i + 2) pro i = m, . . . , 2. Pro stromy sloˇzen´e ze dvou vrchol˚ u dostaneme W (0, 2) = P2 + Q2 + W (0, 1) = 12, C (0, 2) = W (0, 2) + min {C (0, k − 1) + C (k, 2)} = 19 R (0, 2) = 1,
(pro cenovˇ e min. vrchol k) .
(minimum pˇres k = 1, 2)
´ ´I ARITMETICKEHO ´ ´ 6.3. ZPRACOVAN VYRAZU
111
W (1, 3) = P3 + Q3 + W (1, 3) = 9, C (1, 3) = W (1, 3) + min {C (1, k − 1) + C (k, 3)} = 12
(minimum pˇres k = 2, 3)
R (1, 3) = 2,
W (2, 4) = P4 + Q4 + W (0, 1) = 5, C (2, 4) = W (0, 2) + min {C (2, k − 1) + C (k, 4)} = 8 R (2, 4) = 3.
(minimum pˇres k = 3, 4)
Podobnˇe pro stromy, sloˇzen´e ze tˇr´ı vrchol˚ u, dostaneme W (0, 3) = P3 + Q3 + W (0, 2) = 14, C (0, 3) = W (0, 3) + min {C (0, k − 1) + C (k, 3)} = 25
(minimum pˇres k = 1, 2, 3)
R (0, 3) = 2,
W (1, 4) = P4 + Q4 + W (1, 3) = 11, C (1, 4) = W (1, 4) + min {C (1, k − 1) + C (k, 4)} = 19 R (1, 4) = 2.
(minimum pˇres k = 2, 3, 4)
Nyn´ı jiˇz zb´ yv´ a urˇcit pouze optim´ aln´ı vrchol cel´eho stromu: W (0, 4) = P4 + Q4 + W (0, 3) = 16, C (0, 4) = W (0, 4) + min {C (0, k − 1) + C (k, 4)} = 32
(minimum pˇres k = 1, 2, 3, 4)
R (0, 4) = 2.
Odtud m˚ uˇzeme vyˇc´ıst, ˇze vrcholem stromu bude poloˇzka a2 ; v lev´em podstromu je jedinˇe vrchol a1 , takˇze nen´ı o ˇcem pˇrem´ yˇslet. V prav´em podstromu zbyly vrcholy a3 a a4 . Strom S24 m´ a optim´ aln´ı koˇren R24 a to je - jak jsme spoˇc´ıtali - a3 . T´ım je optim´ aln´ı strom plnˇe urˇcen.
6.3
Zpracov´ an´ı aritmetick´ eho v´ yrazu
V tomto odstavci se budeme pro jednoduchost zab´ yvat pouze v´ yrazy s bin´ arn´ımi oper´ atory.
6.3.1
Z´ apis v´ yrazu pomoc´ı stromu
Bin´ arn´ı strom umoˇzn ˇuje popsat strukturu aritmetick´eho v´ yrazu, aniˇz bychom potˇrebovali zn´ at priority oper´ ator˚ u nebo pouˇz´ıvat z´ avorky. Pod´ıvejme se na v´ yraz A + B ∗ C + (D + E) ∗ F. (6.17) Chceme-li ho vyhodnotit na z´ akladˇe tohoto z´ apisu, mus´ıme vˇedˇet, ˇze n´ asoben´ı m´ a pˇrednost pˇred sˇc´ıt´ an´ım a ˇze v´ yrazy v z´ avork´ ach se vyhodnocuj´ı pˇrednostnˇe. V´ yraz (6.17) m˚ uˇzeme ovˇsem tak´e zn´ azornit bin´ arn´ım stromem, ve kter´em budou vnitˇrn´ı uzly pˇredstavovat oper´ atory, listy budou znamenat operandy (promˇenn´e nebo konstanty). Pˇr´ıklad vid´ıme na obr. 6.9. Postup pro vyˇc´ıslen´ı hodnoty, kterou tento strom reprezentuje, definujeme v´ yrazem L op P, kde L je hodnota lev´eho podstromu, P je hodnota prav´eho podstromu a op je oper´ ator v koˇreni stromu. Obsahuje-li nˇekter´ y podstrom pouze jedin´ y list, je jeho hodnota rovna hodnotˇe konstanty (promˇenn´e), kterou tento list urˇcuje. Pokud je tento podstrom sloˇzitˇejˇs´ı, pouˇzijeme k v´ ypoˇctu jeho hodnoty rekurzivnˇe t´ yˇz postup. Pˇri uveden´em postupu je strom na obr. 6.9 ekvivalentn´ı v´ yrazu (6.17). Popsan´ y algoritmus m˚ uˇzeme zapsat ve tvaru funkce
ˇ ´I BINARN ´ ´IHO STROMU KAPITOLA 6. POUZIT
112
+ +
A
* +
*
B
C
D
F
E
Obr. 6.9: V´ yraz reprezentovan´ y pomoc´ı bin´ arn´ıho stromu function hodnota(t: ^strom): c ˇı ´slo; begin if list(t) then hodnota := t^.data else hodnota := vyˇ cı ´sli(hodnota(t^.lev´ y), t^.oper´ ator, hodnota(t^prav´ y)); end; Pˇritom se odvol´ av´ ame na funkci vyˇc´ısli, kter´ a provede s prvn´ım a tˇret´ım parametrem operaci, urˇcenou druh´ ym parametrem (oper´ atorem).
6.3.2
Obr´ acen´ y polsk´ y z´ apis
Bin´ arn´ı strom lze uloˇzit do pole. K tomu lze pouˇz´ıt jak pˇr´ım´e tak zpˇetn´e zpracov´ an´ı. Budeme-li pˇredpokl´ adat, ˇze vˇsechny u ´daje ve stromˇe (oper´ atory, promˇenn´e) jsou stejn´eho typu, mohli bychom k z´ apisu pouˇz´ıt n´ asleduj´ıc´ı proceduru: procedure uloˇ z(t: uStrom); begin if t<> nil then begin if list(t) then zapiˇ s(t^.data) else zapiˇ s(t^.operator); uloˇ z(t^.lev´ y); uloˇ z(t^.prav´ y); end; end; Ze stromu z obr. 6.9 vznikne pomoc´ı t´eto procedury + + A ∗ BC ∗ +DEF
(6.18)
Toto vyj´ adˇren´ı oznaˇcujeme jako polsk´ y z´ apis1 . Jsou-li vˇsechny oper´ atory bin´ arn´ı, je tvar (6.18) ekvivalentn´ı funkcion´ aln´ımu z´ apisu + (+ (A, ∗ (BC)) , ∗ (+ (D, E) , F )) (6.19) ve kter´em oper´ atory vystupuj´ı jako funkce se dvˇema argumenty. Pro poˇc´ıtaˇcov´e zpracov´ an´ı se obˇcas pouˇz´ıv´ a obr´ acen´y polsk´y z´ apis (RPZ ), kter´ y vznikne, budeme-li zapisovat oper´ ator aˇz za operandy (zpˇetn´e zpracov´ an´ı). V´ yraz (6.17), pˇrepsan´ y do RPZ, m´ a tvar ABC ∗ DE + F ∗ + (6.20) Uk´ aˇzeme si jednoduch´ y algoritmus, kter´ y umoˇzn ˇuje pˇrev´est v´ yraz do RPZ. Tento algoritmus pˇredpokl´ ad´ a, ˇze v´ yraz m´ a tvar ˇretˇezce poloˇzek, ˇze vˇsechny oper´ atory jsou bin´ arn´ı a ˇze kaˇzd´ y m´ a oper´ ator pˇriˇrazenu kladnou prioritu. V´ ystupem je opˇet ˇretˇezec poloˇzek ve tvaru RPZ. Pˇri pˇrevodu vyuˇz´ıv´ a z´ asobn´ık. 1 Podle
polsk´eho filozofa Lukasiewicze, kter´ y si zˇrejmˇe jako prvn´ı uvˇedomil, ˇze oper´ ator pˇredstavuje funkci sv´ ych operand˚ u.
´ ´I ARITMETICKEHO ´ ´ 6.3. ZPRACOVAN VYRAZU
113
Algoritmus 1. Vezmi dalˇs´ı poloˇzku ve vstupn´ım ˇretˇezci. 2. Je-li to operand, vloˇz jej do v´ ystupn´ıho ˇretˇezce. 3. Je-li to lev´ a z´ avorka (“, vloˇz ji do z´ asobn´ıku s prioritou 0. ” 4. Je-li to oper´ ator, porovnej jeho prioritu s prioritou oper´ atoru na vrcholu z´ asobn´ıku: (a) Je-li jeho priorita vˇetˇs´ı, vloˇz ho do z´ asobn´ıku; (b) Jinak vezmi oper´ ator z vrcholu z´ asobn´ıku a dej ho do v´ ystupn´ıho ˇretˇezce; porovn´ an´ı opakuj s oper´ atorem, kter´ y je nyn´ı na vrcholu z´ asobn´ıku, dokud nebude priorita nov´eho oper´ atoru vˇetˇs´ı nebo dokud nebude z´ asobn´ık pr´ azdn´ y. Pak vloˇz nov´ y oper´ ator do z´ asobn´ıku. 5. Je-li to prav´ a z´ avorka )“, vyber ze z´ asobn´ıku vˇsechny oper´ atory aˇz po odpov´ıdaj´ıc´ı levou z´ avorku a dej ” je do v´ ystupn´ıho ˇretˇezce; obˇe z´ avorky zahod’ (nepatˇr´ı do v´ ystupn´ıho ˇretˇezce). 6. Naraz´ıˇs-li na konec v´ yrazu, vyber ze z´ asobn´ıku vˇsechny zb´ yvaj´ıc´ı oper´ atory a dej je do v´ ystupn´ıho ˇretˇezce, jinak se vrat’ na krok 1. Tento algoritmus m´ a jednu drobnou nev´ yhodu: prohod´ı poˇrad´ı operand˚ u. To mus´ıme vz´ıt v u ´vahu u nesymetrick´ ych oper´ ator˚ u ( -“, mocnina) . ” Pˇ r´ıklad 6.4 Pod´ıvejme se, jak by vypadal pˇrevod v´ yrazu (6.17) do RPZ pomoc´ı popsan´eho algoritmu. Vstup A+B*C+(D+E)*F +B*C+(D+E)*F B*C+(D+E)*F *C+(D+E)*F C+(D+E)*F +(D+E)*F (D+E)*F D+E)*F +E)*F E)*F )*F *F F ∅
V´ ystup ∅ A A AB AB ABC ABC*+ ABC*+ ABC*+D ABC*+D ABC*+DE ABC*+DE+ ABC*+DE+ ABC*+DE+F ABC*+DE+F*+
Z´ asobn´ık ∅ ∅ + + +* +* + +( (+ +(+ +(+ + +* +* ∅
114
ˇ ´I BINARN ´ ´IHO STROMU KAPITOLA 6. POUZIT
Kapitola 7
Seminumerick´ e algoritmy V t´eto kapitole se pod´ıv´ ame na algoritmy pro z´ akladn´ı poˇcetn´ı operace, jako je sˇc´ıt´ an´ı nebo n´ asoben´ı cel´ ych nebo re´ aln´ ych ˇc´ısel. Zab´ yvat se tˇemito algoritmy se m˚ uˇze zd´ at zbyteˇcn´e: v aritmetick´e jednotce procesoru jsou jiˇz implementov´ any a uˇzivatel je nem˚ uˇze mˇenit. Mohou se ovˇsem vyskytnout situace, kdy znalost tˇechto z´ akladn´ıch algoritm˚ u ocen´ıme. M˚ uˇze se napˇr. st´ at, ˇze n´ am nebude staˇcit pˇresnost, kterou n´ am nab´ız´ı procesor naˇseho poˇc´ıtaˇce (poˇc´ıt´ an´ı s velmi dlouh´ ymi ˇc´ısly je bˇeˇzn´e napˇr. pˇri ˇsifrov´ an´ı). Potom nezb´ yv´ a, neˇz si z´ akladn´ı operace naprogramovat sami. Vedle toho znalost z´ akladn´ıch algoritm˚ u umoˇzn´ı l´epe porozumˇet moˇznostem a omezen´ım poˇc´ıtaˇce.
7.1
Poziˇ cn´ı ˇ c´ıseln´ e soustavy
Algoritmy pro aritmetick´e operace, tedy vlastnˇe pro veˇsker´e zach´ azen´ı s ˇc´ısly, jsou bezprostˇrednˇe z´ avisl´e na zp˚ usobu, jak´ ym jsou ˇc´ıseln´e hodnoty v poˇc´ıtaˇci zobrazov´ any. Zp˚ usob zobrazen´ı ˇc´ısel se naz´ yv´ a ˇc´ıseln´ a soustava; naˇse pov´ıd´ an´ı tedy zaˇcne v´ ykladem o ˇc´ıseln´ ych soustav´ ach. V poziˇcn´ı ˇc´ıseln´e soustavˇe o z´ akladu z pˇredstavuje z´ apis (. . . a 3 a2 a1 a0 , a−1 a−2 a−3 . . .)z re´ aln´e ˇc´ıslo s hodnotou 3 2 1 0 −1 −2 (. . . a3 a2 a1 a0 , a−1 a−2 a−3 . . .)z = · · · + a3 z + a2 z + a1 z + a0 z + a−1 z + a−2 z + a−3 z −3 + · · · (7.1) kde ai jsou ˇc´ıslice, ai ≤ z. Poziˇcn´ı1 ˇc´ıseln´ a soustava se z´ akladem z pouˇz´ıv´ a k vyj´ adˇren´ı z r˚ uzn´ ych znak˚ u, oznaˇcovan´ ych jako ˇc´ıslice. V ˇc´ıseln´ ych soustav´ ach se z´ akladem z ≤ 10 se pouˇz´ıvaj´ı ˇc´ıslice 0, 1, . . . , 9. Je-li z´ aklad z ≤ 36, pouˇz´ıvaj´ı se jako dalˇs´ı ˇc´ıslice p´ısmena latinsk´e (pˇresnˇeji anglick´e) abecedy. P´ısmeno a bude hr´ at roli ˇc´ıslice s v´ yznamem 10, b bude pˇredstavovat ˇc´ıslici 11 atd. Osmiˇckov´ a soustava pouˇz´ıv´ a pouze ˇc´ıslic 0, . . . , 7. Z´ apis (120, 3)8 pˇredstavuje ˇc´ıslo 3 (120, 3)8 = 1.82 + 2.8 + 0.1 + 3.8−1 = 80 + = (80, 375)10 . 8 ˇ Sestn´ actkov´ a soustava se skl´ ad´ a z ˇc´ıslic 0, 1, . . . , 9, a, b, c, d, e, f. Z´ apis (1f ) 16 pˇredstavuje ˇc´ıslo (1f )16 = 1.16 + 15 = 31. ˇ ıslici ak oznaˇcujeme jako v´yznamnˇejˇs´ı neˇz ˇc´ıslici al , je-li k > l. C´
7.2
Cel´ aˇ c´ısla
Pod´ıv´ ame se nyn´ı na probl´emy, kter´e souvis´ı s reprezentac´ı cel´ ych ˇc´ısel v soustavˇe se z´ akladem z a s algoritmy pro z´ akladn´ı aritmetick´e operace v poˇc´ıtaˇci, kter´ y pracuje s n pozicemi. 1 Nyn´ ı je jiˇz zˇrejm´e, proˇc se tyto soustavy oznaˇcuj´ı jako poziˇ cn´ı: V´ aha jednotliv´ ych ˇc´ıslic je urˇcena jejich pozic´ı vzhledem k desetinn´e ˇca ´rce (nebo teˇcce). Existuj´ı i nepoziˇcn´ı ˇc´ıseln´e soustavy - napˇr. ˇr´ımsk´ a. Pro poziˇcn´ı ˇc´ıselnou soustavu se z´ akladem z se pouˇz´ıv´ a oznaˇcen´ı z-´ arn´ı soustava.
115
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
116
7.2.1
Reprezentace cel´ ych ˇ c´ısel se znam´ enkem
Jak zobraz´ıme z´ aporn´ a ˇc´ısla, jestliˇze m´ ame k dispozici n pozic? Jedno z moˇzn´ ych ˇreˇsen´ı je pˇridat dalˇs´ı pozici, kter´ a bude obsahovat znam´enko. Uvaˇzujme poˇc´ıtaˇc, kter´ y pracuje v des´ıtkov´e soustavˇe a m´ a 10 pozic na ˇc´ıslice a jednu pozici na znam´enko. To znamen´ a, ˇze ˇc´ıslo 123 se bude zobrazovat +0000000123, ˇc´ıslo −123 se zobraz´ı jako −0000000123. Tato znam´enkov´ a reprezentace (oznaˇcovan´ a tak´e jako pˇr´ım´y k´ od ) v podstatˇe odpov´ıd´ a bˇeˇzn´ ym konvenc´ım z´ apisu cel´ ych ˇc´ısel. Jej´ı moˇznou nev´ yhodou je, ˇze ˇc´ısla +0 a −0 pˇredstavuj´ı tut´eˇz hodnotu 0, coˇz m˚ uˇze v´est k urˇcit´ ym pot´ıˇz´ım. Proto se zpravidla pouˇz´ıv´ a doplˇ nkov´ a reprezentace (doplˇ nkov´ y k´ od). Je-li d´ ana konstanta k, definujeme v soustavˇe se z´ akladem z obraz ˇc´ısla x, |x| < z k , takto: • Obrazem cel´eho nez´ aporn´eho ˇc´ısla x < z k je samo ˇc´ıslo x, zapsan´e v z-´ arn´ı soustavˇe (a pˇr´ıpadnˇe doplnˇen´e zleva nulami). • Obrazem z´ aporn´eho cel´eho ˇc´ısla x > −z k je ˇc´ıslo z k + x. Doplˇ nkov´ a reprezentace nepouˇz´ıv´ a znam´enka. Proto se zde nesetk´ ame s probl´emem kladn´e a z´ aporn´e nuly; na druh´e stranˇe vˇsechny ostatn´ı obrazy mohou m´ıt dva v´ yznamy, mohou pˇredstavovat bud’ kladn´e nebo z´ aporn´e ˇc´ıslo. Proto se obvykle uˇz´ıv´ a ostˇrejˇs´ı definice doplˇ nkov´eho k´ odu: • Obrazem cel´eho nez´ aporn´eho ˇc´ısla x < z k /2 je samo ˇc´ıslo x, zapsan´e v z-´ arn´ı soustavˇe (a pˇr´ıpadnˇe doplnˇen´e zleva nulami). • Obrazem z´ aporn´eho cel´eho ˇc´ısla x ≥ z k /2 je ˇc´ıslo z k + x. Pouˇzijeme-li t´eto definice, m˚ uˇzeme podle hodnoty prvn´ı ˇc´ıslice obrazu rozhodnout, zda jde o obraz kladn´eho nebo z´ aporn´eho ˇc´ısla. Pˇ r´ıklad 7.1 Klasick´e mechanick´e kalkulaˇcky pracovaly v doplˇ nkov´em k´ odu v des´ıtkov´e soustavˇe. Pˇredstavme si, ˇze m´ ame kalkulaˇcku s 10 pozicemi. Jestliˇze na n´ı od ˇc´ısla 0 odeˇcteme 1, dostaneme 9999999999, nebot’ aritmetick´e operace se prov´ adˇej´ı modulo 1010 . Stejn´ y v´ ysledek ovˇsem m˚ uˇzeme dostat i seˇcten´ım obraz˚ u kladn´ ych ˇc´ısel, napˇr. 4444444444 + 4444444444 + 1111111111 = 9999999999. Obvykle se ovˇsem ˇc´ısla (obrazy), kter´ a maj´ı na nejvyˇsˇs´ı pozici 5, . . . , 9 interpretuj´ı jako z´ aporn´ a. Dneˇsn´ı poˇc´ıtaˇce pouˇz´ıvaj´ı obvykle dvojkovou (bin´ arn´ı) soustavu. Zpravidla si m˚ uˇzeme pˇredepsat, zda budeme obsah dan´eho ˇc´ısla ch´ apat jako cel´e ˇc´ıslo bez znam´enka, tedy nez´ aporn´e ˇc´ıslo v pˇr´ım´em k´ odu, nebo jako cel´e ˇc´ıslo se znam´enkem v doplˇ nkov´em k´ odu. V Turbo Pascalu na PC m´ ame napˇr. typ byte, coˇz je jeden byte, ch´ apan´ y jako cel´e ˇc´ıslo bez znam´enka. M˚ uˇze tedy obsahovat cel´ a ˇc´ısla v rozmez´ı 0..255. Vedle toho m´ ame typ shortint, coˇz je opˇet jeden byte, tentokr´ at ale ch´ apan´ y jako cel´e ˇc´ıslo v doplˇ nkov´em k´ odu. To znamen´ a, ˇze m˚ uˇze obsahovat cel´ a ˇc´ısla v rozmez´ı -128..127. Podobn´e dvojice tvoˇr´ı dvoubytov´e typy word a integer. V jazyku C na PC najdeme jeˇstˇe ˇctyˇrbytov´e typy long a unsigned long; Turbo Pascal nab´ız´ı typ ˇctyˇrbytov´ y typ se znam´enkem longint, z nˇejak´ ych z´ ahadn´ ych d˚ uvod˚ u n´ am vˇsak odep´ır´ a analogick´ y typ bez znam´enka. Pod´ıvejme se nyn´ı na vztah ˇc´ıseln´e soustavy ze z´ akladem z a soustavy se z´ akladem Z = z n . Oznaˇc´ıme-li ai ˇc´ıslice v soustavˇe se z´ akladem z a Ai ˇc´ıslice v soustavˇe se z´ akladem Z, snadno se pˇresvˇedˇc´ıme, ˇze plat´ı vztah (. . . a3 a2 a1 a0 , a−1 a−2 a−3 . . .)z = (. . . A3 A2 A1 A0 , A−1 A−2 A−3 . . .)Z kde ˇc´ıslice Aj vznikne ze skupiny k ˇc´ıslic ai Aj = (anj+n−1 . . . anj+1 anj )z
´ C ˇ ´ISLA 7.2. CELA
117
Protoˇze ai jsou ˇc´ıslice z-´ arn´ı soustavy, je 0 ai < z, takˇze pro libovoln´e n-m´ıstn´e ˇc´ıslo (a0 . . . an−2 an−1 )z plat´ı 0 ≤ (a0 . . . an−2 an−1 )z =
n−1 X i=0
ai z i ≤
n−1 X i=0
(z − 1) z i = (z − 1)
zn − 1 = z n − 1. z−1
To znamen´ a, ˇze skupina (a0 . . . an−2 an−1 )z opravdu m˚ uˇze pˇredstavovat ˇc´ıslici v soustavˇe se z´ akladem Z = z n . Pˇrevedeme ˇc´ıslo, vyj´ adˇren´e ve dvojkov´e soustavˇe x = (111101011011001)2 do ˇsestn´ actkov´e soustavy. Nejprve rozdˇel´ıme x na skupiny po ˇctyˇrech, (1111) (0101) (1011) (0001) | {z } | {z } | {z } | {z } 15 5 11 1 F 5 B 1 ˇ ıslo x m´ C´ a tedy v ˇsestn´ actkov´e soustavˇe tvar F 6B1. Pozn´ amka: Jako z´ aklad ˇc´ıseln´e soustavy se obvykle vol´ı cel´e ˇc´ıslo z > 1. Volby z = 1 a z = 0 zjevnˇe nemaj´ı smysl. Mohli bychom vˇsak za z´ aklad vz´ıt cel´e ˇc´ıslo z < −12 . V takov´e soustavˇe lze vyj´ adˇrit jak´ekoli cel´e ˇc´ıslo, kladn´e i z´ aporn´e, aniˇz bychom potˇrebovali znam´enko. Napˇr. v soustavˇe se z´ akladem −10 se ˇc´ıslo (−1) 10 vyj´ adˇr´ı jako (19)−10 , nebot’ 1. (−10) + 9.1 = −1. Z´ akladem ˇc´ıseln´e soustavy mohou b´ yt i komplexn´ı ˇc´ısla. Napˇr. soustava se z´ akladem z = 2i umoˇzn ˇuje vyj´ adˇrit libovoln´e komplexn´ı ˇc´ıslo s cel´ ymi sloˇzkami jako posloupnost ˇc´ıslic 0, 1, 2 a 3. Podrobnˇejˇs´ı informace o ˇc´ıseln´ ych soustav´ ach najdete napˇr. v Knuthovˇe knize [3]. V tomto i v n´ asleduj´ıc´ıch algoritmech budeme oznaˇcen´ı ˇc´ıseln´e soustavy vynech´ avat.
7.2.2
Sˇ c´ıt´ an´ı cel´ ych ˇ c´ısel
Postup pˇri sˇc´ıt´ an´ı bude vych´ azet ze zn´ am´ ych algoritm˚ u ze z´ akladn´ı ˇskoly. Sˇc´ıt´ ame dvˇe k-m´ıstn´ a ˇc´ısla, u a v, kde u = u1 u2 . . . uk−1 uk a v = v1 v2 . . . vk−1 vk (pozor, nyn´ı indexujeme ˇc´ıslice v opaˇcn´em poˇrad´ı neˇz v (7.1)). V´ ysledek bude u + v = w = w1 w2 . . . wk−1 wk ; ˇ ısla u, v a w mohou zaˇc´ınat nulami. Pˇri sˇc´ıt´ (ui , vi a wi jsou ˇc´ıslice, kter´e tato ˇc´ısla tvoˇr´ı). C´ an´ı pouˇzijeme pomocnou promˇennou, kterou budeme oznaˇcovat p a naz´ yvat pˇrenos. Vlastn´ı algoritmus sˇc´ıt´ an´ı dvou cel´ ych ˇc´ısel bez znam´enka bude vypadat takto: 1. Poloˇz p := 0 a pro i = k, k − 1, . . . 1 opakuj n´ asleduj´ıc´ı kroky: 2. Vypoˇcti b := ui + vi + p. 3. Je-li b < z, poloˇz wi := b a p := 0; jinak poloˇz p := 1 a wi := b − z. Protoˇze souˇcet dvou ˇc´ıslic nepˇres´ ahne hodnotu 2z − 2, nem˚ uˇze b´ yt pˇrenos po seˇcten´ı prvn´ıch dvou ˇc´ıslic vˇetˇs´ı neˇz 1. Pˇri seˇcten´ı druh´e - a kaˇzd´e n´ asleduj´ıc´ı - dvojice nem˚ uˇze souˇcet u i + vi + p pˇres´ ahnou 2z − 1, takˇze pˇrenos pˇri sˇc´ıt´ an´ı bude vˇzdy bud’ 0 nebo 1. Proto mu v popsan´em algoritmu pˇriˇrazujeme pouze 0 nebo 1. Je-li po skonˇcen´ı tohoto algoritmu p = 1, doˇslo k pˇreteˇcen´ı“, v´ ysledek m´ a k + 1 m´ıst (a v´ ysledek, kter´ y ” jsme z´ıskali, je nespr´ avn´ y - chyb´ı v nˇem nejvyˇsˇs´ı pozice). Uveden´ y algoritmus tedy vlastnˇe pˇredstavuje sˇc´ıt´ an´ı modulo z k . Popsan´ y algoritmus m˚ uˇzeme pouˇz´ıvat i pˇri sˇc´ıt´ an´ı v doplˇ nkov´em k´ odu. Zde se ovˇsem m˚ uˇze st´ at, ˇze v´ ysledkem sˇc´ıt´ an´ı dvou kladn´ ych ˇc´ısel bude hodnota vˇetˇs´ı neˇz nejvˇetˇs´ı zobraziteln´e kladn´e ˇc´ıslo, avˇsak menˇs´ı, neˇz nejvˇetˇs´ı zobraziteln´e kladn´e ˇc´ıslo v pˇr´ım´em k´ odu. V´ ysledek se pak bude jevit jako z´ aporn´e ˇc´ıslo. 2 Mus´ ıme ovˇsem pravidlo, kter´e pˇrikazuje, ˇze ˇc´ıslice je menˇs´ı neˇz z´ aklad, nahradit pravidlem, ˇze ˇc´ıslice je menˇs´ı neˇz absolutn´ı hodnota z´ akladu, 0 a < |z|.
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
118
Uvaˇzujme osmibitovou bin´ arn´ı aritmetiku se znam´enkem (napˇr. typ shortint z Turbo Pascalu). Seˇcteme-li 127 + 2, dospˇejeme popsan´ ym algoritmem k hodnotˇe 129, kter´ a se ovˇsem interpretuje uˇz jako z´ aporn´e ˇc´ıslo v doplˇ nkov´em k´ odu. Z definice doplˇ nkov´eho k´ odu pˇri 8 bitech plyne, ˇze ˇc´ıslo 129 je obrazem z´ aporn´eho ˇc´ısla −127. Dostaneme tedy ponˇekud pˇrekvapuj´ıc´ı v´ ysledek, ˇze 127 + 2 = −127. Uk´ aˇzeme si, ˇze pokud nenastane pˇreteˇcen´ı, m˚ uˇzeme uveden´ y postup pouˇz´ıt i pro sˇc´ıt´ an´ı z´ aporn´ ych ˇc´ısel. Je-li x < 0, y < 0, sˇc´ıt´ ame jejich obrazy, z k + x + z k + y = 2z k + x + y = z k + x + y mod z k
nebot’ poˇc´ıt´ ame v aritmetice modulo z k . Posledn´ı v´ yraz ale pˇredstavuje obraz ˇc´ısla x + y. Podobnˇe uk´ aˇzeme, ˇze uveden´ y algoritmus lze pouˇz´ıt i pro pˇr´ıpad x > 0, y < 0 nebo x < 0, y > 0, kde ovˇsem nem˚ uˇze doj´ıt k pˇreteˇcen´ı.
7.2.3
Odeˇ c´ıt´ an´ı cel´ ych ˇ c´ısel
Algoritmus pro odeˇc´ıt´ an´ı bude podobn´ y algoritmu pro sˇc´ıt´ an´ı; opˇet vyuˇz´ıv´ a pomocnou promˇennou pˇrenos. Odeˇc´ıt´ ame dvˇe k-m´ıstn´ a ˇc´ısla, u a v, kde u = u1 u2 . . . uk−1 uk a v = v1 v2 . . . vk−1 vk ; v´ ysledek bude u − w = w = w1 w2 . . . wk−1 wk . Algoritmus pro odeˇcten´ı dvou cel´ ych ˇc´ısel bez znam´enka m´ a n´ asleduj´ıc´ı tvar. 1. Poloˇz p := 0 a pro i := k, k − 1, . . . , 1 opakuj n´ asleduj´ıc´ı kroky: 2. Vypoˇcti b := ui − vi − p. 3. Je-li b < 0, poloˇz´ıme wi := z − b a p := 1; jinak poloˇz´ıme wi := b a p := 0. Opˇet jde o operaci v aritmetice modulo z k . Je-li u < v, dostaneme v´ ysledek u − v + z k . Snadno se pˇresvˇedˇc´ıme, ˇze tento postup lze pouˇz´ıt i pro odeˇc´ıt´ an´ı z´ aporn´ ych ˇc´ısel v doplˇ nkov´em k´ odu. Jin´ y moˇzn´ y postup: K ˇc´ıslu v sestroj´ıme ˇc´ıslo opaˇcn´e, −v, a seˇcteme u + −v pomoc´ı algoritmu z pˇredchoz´ıho odd´ılu.
7.2.4
Opaˇ cn´ eˇ c´ıslo
Jako ˇc´ıslo opaˇcn´e k v se oznaˇcuje ˇc´ıslo −v. Uk´ aˇzeme si, jak je v doplˇ nkov´em k´ odu sestroj´ıme. Budeme jako obvykle pˇredpokl´ adat, ˇze v = v1 v2 . . . vk−1 vk a z je z´ aklad ˇc´ıseln´e soustavy. Nejprve sestroj´ıme negovan´e“ ˇc´ıslo v 0 , pro jehoˇz ˇc´ıslice plat´ı ” vi0 = z − vi − 1. Snadno se pˇresvˇedˇc´ıme, ˇze souˇcet v + v 0 , sestrojen´ y podle v´ yˇse uveden´eho algoritmu, se skl´ ad´ a z k stejn´ ych ˇc´ıslic s hodnotou z − 1. To znamen´ a, ˇze souˇcet v + v 0 + 1 bude (v aritmetice modulo zk) roven 0. Algoritmus pro sestrojen´ı opaˇcn´eho ˇc´ısla je tedy jednoduch´ y: 1. Pro i = 1, . . . , k nahrad’ vi ˇc´ıslic´ı s hodnotou z − vi − 1 (sestroj negovan´e“ ˇc´ıslo). ” 2. K v´ ysledku pˇredchoz´ıho kroku pˇriˇcti 1. Ve dvojkov´e soustavˇe z´ısk´ ame negovan´e“ ˇc´ıslo pomoc´ı negace v po bitech (odtud n´ azev). Za pˇredpokladu, ” ˇze pouˇz´ıv´ ame dvojkovou soustavu s doplˇ nkov´ ym k´ odem, m˚ uˇzeme pˇredchoz´ı algoritmus vyj´ adˇrit pascalsk´ ym pˇr´ıkazem y := (not v) + 1; Poznamenejme, ˇze nˇekter´e poˇc´ıtaˇce maj´ı pro v´ ypoˇcet opaˇcn´eho ˇc´ısla zvl´ aˇstn´ı instrukci (na PC je to instrukce NEG).
´ C ˇ ´ISLA 7.2. CELA
7.2.5
119
N´ asoben´ı cel´ ych ˇ c´ısel
Zde n´ am postaˇc´ı umˇet vyn´ asobit nez´ aporn´ a cel´ a ˇc´ısla. V soustavˇe se z´ akladem z hled´ ame souˇcin dvou ˇc´ısel u = u1 u2 . . . un a v = v1 v2 . . . vm . V´ ysledek bude u × v = w = w1 w2 . . . wm+n . Vˇsimnˇete si, ˇze nyn´ı nepˇredpokl´ ad´ ame stejn´ y poˇcet cifer v obou ˇcinitel´ıch.
Vyjdeme opˇet od tradiˇcn´ıho n´ asoben´ı na pap´ıˇre. Pˇri tomto vˇseobecnˇe zn´ am´em postupu jsme napoˇc´ıt´ avali parci´ aln´ı souˇciny a celkov´ y v´ ysledek jsme z´ıskali jako jejich souˇcet. Pˇri strojov´em v´ ypoˇctu bude v´ yhodnˇejˇs´ı ihned pˇriˇc´ıtat jednotliv´e ˇc´ıslice parci´ aln´ıch souˇcin˚ u k celkov´emu v´ ysledku. Algoritmus bude opˇet vyuˇz´ıvat pomocnou promˇennou p pro pˇrenos; i a j slouˇz´ı jako parametry cykl˚ u. 1. Inicializace: Poloˇz´ıme wm+1 := 0, . . . , wm+n := 0, j := m. 2. Test nuly: Je-li vj = 0, nastav´ıme wj := 0 a jdeme na bod 6. (Tento krok lze vynechat.) 3. Inicializace i: i := n, p := 0. 4. N´ asoben´ı a sˇc´ıt´ an´ı: Poloˇz´ıme t := ui × vi + wi+j + p. D´ ale poloˇz´ıme wi+j := t mod z, p := [t/z]. Symbol [x] zde znamen´ a celou ˇca ´st ˇc´ısla x. (Pˇrenos bude vˇzdy v rozmez´ı 0 ≤ p < z, tedy jednocifern´ y.) 5. Konec cyklu podle i: Zmenˇs´ıme i o jedniˇcku; je-li i > 0, vr´ at´ıme se na bod 4, jinak poloˇz´ıme w j := p. 6. Konec cyklu podle j: Zmenˇs´ıme j o jedniˇcku; je-li nyn´ı j > 0, vr´ at´ıme se na bod 2, jinak konec. Tento algoritmus pˇredpokl´ ad´ a, ˇze meziv´ ysledek t splˇ nuje nerovnost 0 ≤ t < z 2 a pˇrenos p ˇze leˇz´ı v rozmez´ı 0 ≤ p < z. Nerovnost pro pˇrenos dok´ aˇzeme matematickou indukc´ı podle jednotliv´ ych krok˚ u algoritmu. V prvn´ım kroku zˇrejmˇe plat´ı, nebot’ na poˇca ´tku je p = 0. Necht’ tedy plat´ı pro nˇejak´e i. Podle 4. bodu algoritmu dostaneme ui × vi + wi+j + p ≤ (z − 1) × (z − 1) + (z − 1) + (z − 1) = z 2 − 1 < z 2
(to je druh´ a z dokazovan´ ych nerovnost´ı). Odtud plyne pro pˇrenos p = [t/z] ≤ z − 1.
7.2.6
Dˇ elen´ı cel´ ych ˇ c´ısel
Tak´e algoritmus dˇelen´ı cel´ ych ˇc´ısel v soustavˇe o z´ akladu z vych´ az´ı z tradiˇcn´ıho postupu dˇelen´ı. Budeme se opˇet zab´ yvat pouze dˇelen´ım nez´ aporn´ ych ˇc´ısel a budeme pˇredpokl´ adat, ˇze dˇelencem je ˇc´ıslo u = u 1 u2 . . . um+n , dˇelitelem ˇc´ıslo v = v1 v2 . . . vn . Pod´ıl pak m´ıt hodnotu q = [u/v] a bude to m-cifern´e ˇc´ıslo q = q0 q1 . . . qm . Zbytek po dˇelen´ı r bude m´ıt nejv´ yˇse n cifer, r = r1 . . . rn . Prvn´ım krokem je normalizace, kter´ a zajist´ı, ˇze prvn´ı cifra dˇelitele bude vˇetˇs´ı neˇz [z/k]. Toto opatˇren´ı zajist´ı lepˇs´ı vlastnosti odhadu ˇc´ıslic pod´ılu. 1. Normalizace: Poloˇz´ıme d := [z/ (v1 + 1)]. D´ ale poloˇz´ıme u := u × d, v := v × d. 2. Inicializace j: Poloˇz j := 0. (Bude n´ asledovat cyklus podle j. Kroky 2 - 7 pˇredstavuj´ı v podstatˇe dˇelen´ı uj . . . uj+m ˇc´ıslem v1 . . . vn tak, abychom dostali jednoˇc´ıseln´ y pod´ıl qj .) 3. V´ypoˇcet odhadu q 0 : Je-li uj = vj , poloˇz´ıme q 0 := z − 1, jinak poloˇz q 0 := [(uj z + uj+1 ) /v1 ]. Nyn´ı zkus´ıme, zda je v2 q 0 > (uj z + uj+1 − q 0 v1 ) z + uj+2 . Pokud ano, zmenˇs´ıme q 0 o 1 a test zopakujeme.
4. N´ asoben´ı a odeˇcten´ı: nahrad´ıme uj uj+1 . . . uj+n ˇc´ıslem uj uj+1 . . . uj+n −(q 0 × v1 v2 . . . vn ). Jde o n´ asoben´ı ˇ ıslo uj uj+1 . . . uj+n mus´ı z˚ jednom´ıstn´ ym ˇc´ıslem a odeˇcten´ı. C´ ustat kladn´e. Je-li v´ ysledek tohoto kroku z´ aporn´ y, zvˇetˇs´ıme jeho hodnotu o z n , tj. vezmeme doplnˇek skuteˇcn´e hodnoty, a tuto v´ yp˚ ujˇcku“ si ” zapamatujeme.
5. Test zbytku: Poloˇz´ıme q := q 0 . Je-li v´ ysledek kroku 4 z´ aporn´ y, jdeme na 6, jinak jdeme na 7. 6. Zpˇetn´e pˇriˇcten´ı: Zmenˇs´ıme q o 1. K uj uj+1 . . . uj+n pˇriˇcti 0v1 v2 . . . vn . (Objev´ı se pˇrenos; ten se ignoruje, nebot’ pouze ruˇs´ı v´ yp˚ ujˇcku“ ze 4. kroku.) ” 7. Konec cyklu podle j:j := j + 1. Je-li nyn´ı j ≤ m, vr´ at´ıme se na krok 3. 8. Zruˇsen´ı normalizace: Nyn´ı je q0 q1 . . . qm pod´ıl. Zbytek z´ısk´ ame dˇelen´ım um+1 . . . um+n ˇc´ıslem d (faktorem, kter´ y jsme pouˇzili k normalizaci). Jde o dˇelen´ı jednocifern´ ym ˇc´ıslem.
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
120 Pˇ r´ıklad 7.2 Cel´ aˇ c´ısla na PC
Procesory Intel 80x86 umoˇzn ˇuj´ı pouˇz´ıvat bud’ cel´ a ˇc´ısla bez znam´enek nebo cel´ a ˇc´ısla se znam´enky v doplˇ nkov´em k´ odu. M´ ame na vybranou tyto moˇznosti: velikost znam´enko rozsah hodnot typ v Pascalu typ v C 1B se znam´enkem -128..127 shortint signed char 1B bez znam´enka 0..255 byte unsigned char 2B se znam´enkem -32768..32767 integer int, short 2B bez znam´enka 0..65535 word unsigned, unsigned short 4B se znam´enkem -2147483648..2147483647 longint long 4B bez znam´enka 0..4294967295 unsigned long 8B se znam´enkem −263 ..263 − 1 comp 3
Tabulka 7.1 Cel´ a ˇc´ısla na PC a odpov´ıdaj´ıc´ı typy v Turbo Pascalu a v Borland C++ Poznamenejme, ˇze typ comp vyˇzaduje pouˇzit´ı matematick´eho koprocesoru.
7.3
Re´ aln´ aˇ c´ısla
Pod oznaˇcen´ım re´ aln´ a ˇc´ısla se v poˇc´ıtaˇcov´e terminologii ukr´ yv´ a koneˇcn´ a podmnoˇzina mnoˇziny racion´ aln´ıch ˇc´ısel. Vedle toho se obˇcas hovoˇr´ı o ˇc´ıslech s pohyblivou ˇra ´dovou ˇca ´rkou (teˇckou), coˇz je kalk anglick´eho term´ınu floationg point numbers.
7.3.1
Zobrazen´ı re´ aln´ ych ˇ c´ısel
Zobrazen´ı re´ aln´ ych ˇc´ısel se op´ır´ a o semilogaritmick´ y4 z´ apis, bˇeˇznˇe pouˇz´ıvan´ y k z´ apisu extr´emnˇe velk´ ych nebo mal´ ych hodnot. Napˇr. Planckova konstanta, zn´ am´ a z kvantov´e mechaniky, m´ a hodnotu h = 6, 6256.10 −34Js. V tomto z´ apisu oznaˇcujeme 6,6256 jako mantisu a −34 jako exponent. V soustavˇe se z´ akladem z bychom k vyj´ adˇren´ı re´ aln´eho ˇc´ısla mohli pouˇz´ıt dvojici (e, f ), kter´ a by pˇredstavovala ˇc´ıslo (e, f ) = f z e . Z praktick´ ych d˚ uvod˚ u se ale pouˇz´ıv´ a k´ odov´ an´ı (e, f ) = f z e−q , kde f je p-cifern´e ˇc´ıslo, vyjadˇruj´ıc´ı zlomkovou ˇca ´st (mantisu), e je exponent a q je posun (exces); e a q jsou cel´ a ˇc´ısla. Poˇzadujeme, aby pro mantisu platilo |x| < 1. Z toho plyne, ˇze −z p < z p f < z p . Zvol´ıme-li posun e rovn´ y 50 a poˇcet cifer p = 5, m˚ uˇzeme Planckovu konstantu vyj´ adˇrit z´ apisem h = (27, +0, 66256) .
Pro mantisu se pouˇz´ıv´ a prakticky v´ yluˇcnˇe pˇr´ım´ y k´ od, tj. k´ odov´ an´ı se znam´enkem. Obvykle se tak´e poˇzaduje, aby zobrazovan´e ˇc´ıslo bylo normalizovan´e , tj. aby pro mantisu platilo z −1 ≤ |f | ≤ 1 nebo f = 0. (7.2)
Setk´ ame se ale i s jin´ ymi definicemi normalizovan´eho ˇc´ısla. Za normalizovan´e m˚ uˇzeme napˇr. pokl´ adat ˇc´ıslo, jehoˇz mantisa splˇ nuje podm´ınky 1 ≤ |f | ≤ z nebo f = 0. V pˇr´ıpadˇe, ˇze je mantisa nulov´ a, f = 0, mus´ı m´ıt exponent nejmenˇs´ı moˇznou hodnotu.
Z tˇechto poˇzadavk˚ u vych´ az´ı uloˇzen´ı re´ aln´ ych ˇc´ısel v pamˇeti poˇc´ıtaˇce. Je-li r promˇenn´ a, do kter´e ukl´ ad´ ame re´ aln´ a ˇc´ısla, bude ˇca ´st z n´ı obsahovat exponent, ˇca ´st mantisu a jeden bit bude obsahovat znam´enko mantisy. 3 Uveden´ e hodnoty plat´ı pro pˇrekladaˇce pro ”ˇsestn´ actibitov´e” programy (tj. napˇr. programy pro DOS nebo pro Windows 3.1). Ve ”dvaatˇricetibitov´ ych” programech (napˇr. v programech pro Windows NT) se v C´eˇcku pouˇz´ıvaj´ı typy int a unsigned int o velikosti 4 B a velikost 2 B maj´ı pouze typy short a unsigned short. 4 Z n´ avod˚ u ke kalkulaˇck´ am zn´ ame tak´e ponˇekud nesmysln´e oznaˇcen´ı vˇ edeck´ y z´ apis (scientific notation). Dosud se mi nepodaˇrilo zjistit, co je na tomto z´ apisu vˇedeck´eho - pokud v´ım, uˇc´ı se v 8. tˇr´ıdˇe z´ akladn´ıch ˇskol.
´ A ´ C ˇ ´ISLA 7.3. REALN
121
hb s
db
hb
e(8)
db m(23) byte 1
byte 4
Obr. 7.1: Uloˇzen´ı ˇctyˇrbytov´eho re´ aln´eho ˇc´ısla v pamˇeti PC; s je 1 bit, urˇcuj´ıc´ı znam´enko mantisy, m je mantisa, e je exponent; ˇc´ısla v z´ avork´ ach ud´ avaj´ı poˇcet bit˚ u; db resp. hb je doln´ı, nejm´enˇe v´ yznamn´ y resp. horn´ı, nejv´ yznamnˇejˇs´ı bit sloˇzky.
Pˇ r´ıklad 7.3 Re´ aln´ aˇ c´ısla na PC Zobrazen´ı re´ aln´ ych ˇc´ısel na PC, tedy na poˇc´ıtaˇc´ıch vybaven´ ych procesory Intel 80x86, vych´ az´ı z vlastnost´ı matematick´eho koprocesoru Intel 80x875. M´ ame tedy k dispozici 3 typy re´ aln´ ych ˇc´ısel, kter´e se v Turbo Pascalu oznaˇcuj´ı single, double a extended ; v C´eˇcku se pro tyto typy pouˇz´ıv´ a oznaˇcen´ı float, double a long double. Uloˇzen´ı hodnot tˇechto typ˚ u v pamˇeti PC vych´ az´ı v podstatˇe z popsan´eho zp˚ usobu zobrazen´ı; nav´ıc umoˇzn ˇuj´ı pracovat se speci´ aln´ımi hodnotami ±INF a ±NaN. Hodnoty ±INF pˇredstavuj´ı strojov´e nekoneˇcno“, to zna” men´ a, ˇze se ve v´ yrazech chovaj´ı podobnˇe jako v limit´ ach v matematick´e anal´ yze. INF lze - pˇri urˇcit´em nastaven´ı stavov´eho slova matematick´eho koprocesoru - z´ıskat napˇr. jako v´ ysledek operace 1.0/0.0. (Tak je tomu napˇr. pˇri standardn´ım nastaven´ı v pˇrekladaˇci Borland C++ 3.1; pˇrekladaˇc Turbo Pascalu pouˇz´ıv´ a jin´e nastaven´ı, kter´e zp˚ usob´ı, ˇze dˇelen´ı nulou vyvol´ a chybu.) NaN je zkratka ze slov Not a Number“, tedy u ´daj, kter´ y nelze ch´ apat jako ˇc´ıslo. Vznikne zpravidla jako ” v´ ysledek chyby v definiˇcn´ım oboru parametr˚ u matematick´ ych funkc´ı, napˇr. pˇri pokusu o odmocninu ze z´ aporn´eho ˇc´ısla. (Pˇripomeˇ nme si, ˇze v´ ypoˇcty hodnot bˇeˇzn´ ych matematick´ ych funkc´ı, jako je odmocnina, logaritmus, sinus apod., pˇredstavuj´ı jednu instrukci matematick´eho koprocesoru 80x87.) Pod´ıvejme se nyn´ı, jak jsou tyto datov´e typy zobrazeny v pamˇeti PC. Typ single je v pamˇeti uloˇzen n´ asleduj´ıc´ım zp˚ usobem (viz obr. 7.1): pro mantisu ve vyhrazeno 23 bit˚ u, pro exponent 8 bit˚ u a pro znam´enko mantisy 1 bit. Nejv´ yznamnˇejˇs´ı bit jednotliv´ ych sloˇzek je vˇzdy vlevo, nejm´enˇe v´ yznamn´ y vlevo. Vˇsimnˇete si, ˇze hranice jednotliv´ ych ˇca ´st´ı se nekryj´ı s hranicemi byt˚ u. Exponent leˇz´ı v rozmez´ı 0 ≤ e ≤ 255, posun q je 127. Pro bˇeˇzn´ a“ ˇc´ısla se pouˇz´ıvaj´ı hodnoty exponentu ” v rozmez´ı 0 < e < 255; hodnota 0 je vyhrazena pro nenormalizovan´ a ˇc´ısla, hodnota 255 pro INF a NaN. Za normalizovan´ y pokl´ ad´ ame takov´ y tvar, kdy je 1 ≤ m < 2 nebo m = 0. Pokud je m ≥ 1, zaˇc´ın´ a mantisa jedniˇckou a tu nemus´ıme zobrazovat. Obraz mantisy se proto skl´ ad´ a pouze z ˇc´ıslic za ˇra ´dovou teˇckou. Skuteˇcnou hodnotu mantisy budeme v tomto pˇr´ıpadˇe vyjadˇrovat z´ apisem 1.m. Pouze pro nejmenˇs´ı moˇznou hodnotu exponentu se pˇredpokl´ ad´ a, ˇze mantisa zaˇc´ın´ a nulou, ˇze jde o tzv. denorm´ aln´ı ˇc´ıslo; jej´ı hodnota je pak interpretov´ ana jako 0.m. Hodnotu v, uloˇzenou v promˇenn´e typu single, vypoˇcteme z n´ asleduj´ıc´ıch vztah˚ u: s Je − li 0 < e < 255, je v = (−1) × 2e−q × (1.m) ; Je − li e = 0, m 6= 0, je
(7.3)
s
v = (−1) × 21−q × (0.m) ;
Je − li e = 0, m = 0, je Je − li e = 255, m = 0, je
s
v = (−1) × 0; v = (−1)s × INF;
5 Typy re´ aln´ ych ˇc´ısel v podstatˇe odpov´ıdaj´ı standardu svˇetov´e organizace elektrick´ ych a elektrotechnick´ ych inˇzen´ yr˚ u IEEE [30]. Z´ akladn´ı informace o tom, jak pracuje matematick´ y koprocesor, najdeme napˇr. v [34].
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
122
s
e(11)
m(52) byte 1
byte 8
Obr. 7.2: Uloˇzen´ı osmibytov´eho re´ aln´eho ˇc´ısla v pamˇeti PC; ˇc´ısla v z´ avork´ ach ud´ avaj´ı poˇcet bit˚ u.
s
e(15)
l
m(63) byte 1
byte 10
Obr. 7.3: Uloˇzen´ı desetibytov´eho re´ aln´eho ˇc´ısla v pamˇeti PC; l je bit pˇred ˇra ´dovou teˇckou mantisy, ˇc´ısla v z´ avork´ ach ud´ avaj´ı poˇcet bit˚ u. Je − li e = 255, m 6= 0, je
v = NaN.
Typ double je uloˇzen v 8 bytech, jak to ukazuje obr. 7.2. Exponent leˇz´ı v rozmez´ı 0 ≤ e ≤ 2048, posun q je 1023. Pravidla pro v´ ypoˇcet hodnoty jsou podobn´ a jako u ˇctyˇrbytov´ ych re´ aln´ ych ˇc´ısel, pouze mus´ıme ve vztaz´ıch (7.3) nahradit horn´ı mez exponentu 255 hodnotou 2047. Typ extended je uloˇzen v 10 bytech, jak ukazuje obr. 7.3. Exponent e leˇz´ı v rozmez´ı 0 ≤ e ≤ 32767, posun q je 16383. U tohoto typu se vˇzdy zobrazuje i jedniˇcka nebo nula pˇred ˇra ´dovou teˇckou v mantise (jednobitov´e pole l na obr. 7.4). Hodnotu v, uloˇzenou v promˇenn´e typu extended, vypoˇcteme takto: s Je − li 0 < e < 32767, je v = (−1) × 2e−q × (l.m) . Je − li e = 32767, m = 0, je
e
v = (−1) × INF;
Je − li e = 32767, m 6= 0, je
v = NaN.
V Turbo Pascalu najdeme nav´ıc typ real , se kter´ ym ovˇsem matematick´ y koprocesor neum´ı pracovat. Je uloˇzen v 6 bytech (viz obr. 7.4), posun q je 129; tento typ neumoˇzn ˇuje zobrazit hodnoty ±INF a ±NaN a denorm´ aln´ı ˇc´ısla. Hodnotu v, uloˇzenou v ˇsestibytov´em r´ aln´em ˇc´ısle, vypoˇcteme takto: Je − li 0 < e < 255, je
s
v = (−1) × 2e−q × (1.m) ,
Je − li e = 0, je
v = 0.
V n´ asleduj´ıc´ıch odd´ılech se budeme zab´ yvat algoritmy pro z´ akladn´ı aritmetick´e operace s re´ aln´ ymi ˇc´ısly. Jak uvid´ıme, nejsou pˇresn´e - nalezen´ a dvojice w se bude zpravidla liˇsit od obrazu matematick´eho souˇctu u + v, rozd´ılu u − v, pod´ılu u/v, nebo souˇcinu uv. Rozd´ıl mezi nalezenou hodnotou w a skuteˇcn´ ym v´ ysledkem matematick´e operace oznaˇcujeme jako zaokrouhlovac´ı chybu. Vzhledem k zaokrouhlovac´ım chyb´ am budeme pro operace pomoc´ı uveden´ ych algoritm˚ u pouˇz´ıvat m´ısto +“ ” znaˇcku +◦“, m´ısto /“ znaˇcku /◦“ apod. ” ” ”
s byte 6
e(39)
m(8) byte 1
Obr. 7.4: Uloˇzen´ı ˇc´ısla typu real v pamˇeti PC; ˇc´ısla v z´ avork´ ach opˇet ud´ avaj´ı poˇcet bit˚ u.
´ A ´ C ˇ ´ISLA 7.3. REALN
7.3.2
123
Sˇ c´ıt´ an´ı a odeˇ c´ıt´ an´ı re´ aln´ ych ˇ c´ısel
Jak uvid´ıme, je algoritmus pro sˇc´ıt´ an´ı re´ aln´ ych ˇc´ısel nejkomplikovanˇejˇs´ı z algoritm˚ u pro pr´ aci s re´ aln´ ymi ˇc´ısly. Pˇredpokl´ ad´ ame, ˇze je opˇet d´ an z´ aklad z, posun q, poˇcet cifer p a dvˇe normalizovan´ a re´ aln´ a ˇc´ısla u = (e u , fu ) a v = (ev , fv ). Hled´ ame souˇcet w = u + v, tj. hled´ ame dvojici (ew , fw ), kter´ a bude obrazem souˇctu u + v (nebo pˇresnˇeji u +◦ v). 1. Rozbalen´ı: V reprezentaci ˇc´ısel u a v od sebe oddˇel´ıme exponent a mantisu. To znamen´ a, ˇze budeme u obou ˇc´ısel u a v pracovat zvl´ aˇst’ s mantisou f a zvl´ aˇst’ s exponentem e. 2. Pˇredpokl´ ad´ ame eu ≥ ev : Je-li eu < ev , zamˇen´ıme u a v. 3. Urˇcen´ı exponentu v´ysledku ew : Poloˇz´ıme ew = eu . 4. Test eu − ev : Je-li eu − ev ≥ p + 2 (velk´ y rozd´ıl exponent˚ u), poloˇz´ıme fw := fu a p˚ ujdeme na krok 7. (Protoˇze jsme pˇredpokl´ adali, ˇze jsou obˇe ˇc´ısla normalizovan´ a, mohli bychom skonˇcit.) 5. Posun doprava: Posuneme fv o eu − ev m´ıst doprava (tzn. vydˇel´ıme fv ˇc´ıslem z eu−ev ). Jde o posun o maxim´ alnˇe p + 1 m´ıst doprava, to znamen´ a, ˇze potˇrebujeme registr, kter´ y bude m´ıt alespoˇ n 2p + 1 m´ıst. D´ a se ale uk´ azat, ˇze za jist´ ych okolnost´ı lze tento poˇzadavek zredukovat na p + 2 m´ıst. 6. Seˇcten´ı: Poloˇz´ıme fw := fu + fv . Zde pouˇzijeme algoritmus pro sˇc´ıt´ an´ı cel´ ych ˇc´ısel, se kter´ ym jsme se sezn´ amili v 7.2.2. 7. Normalizace: V tomto bodˇe algoritmu jiˇz (ew , fw ) pˇredstavuje u+◦ v, avˇsak w nemus´ı splˇ novat podm´ınky normalizace, tj. fw m˚ uˇze m´ıt v´ıce neˇz p ˇc´ıslic, m˚ uˇze b´ yt vˇetˇs´ı neˇz 1 nebo menˇs´ı neˇz 1/z (pˇr´ıpadnˇe vˇetˇs´ı neˇz z nebo menˇs´ı neˇz 1 - z´ aleˇz´ı na tom, jak definujeme normalizovan´e ˇc´ıslo). Normalizaci pop´ıˇseme jako samostatn´ y algoritmus v n´ asleduj´ıc´ım odd´ılu. Jestliˇze v tomto algoritmu zamˇen´ıme v za −v, dostaneme algoritmus pro odeˇc´ıt´ an´ı re´ aln´ ych ˇc´ısel, tedy pro v´ ypoˇcet u −◦ v.
7.3.3
Normalizace re´ aln´ eho ˇ c´ısla
Pˇri normalizaci pˇrevedeme hrub´ y“ exponent e a hrubou“ mantisu f do normalizovan´eho tvaru a mantisu v pˇr´ı” ” padˇe potˇreby zaokrouhl´ıme na p ˇc´ıslic. Uvedeme algoritmus pro normalizaˇcn´ı podm´ınku (7.2); pˇredpokl´ ad´ ame, ˇze |f | < z a z ˇze je sud´e ˇc´ıslo. 1. Test f : Je-li |f | ≥ 1 (nastalo pˇreteˇcen´ı mantisy“), jdeme na 4. Je-li f = 0, nastav´ıme e na nejmenˇs´ı ” moˇznou hodnotu a jdeme na 7. 2. Je f normalizovan´e ? Je-li |f | ≥ 1/z, jdeme na 7. 3. Posun doleva: Na tento krok pˇrijdeme pouze v pˇr´ıpadˇe, ˇze f < 1/z. Posuneme f o jednu pozici doleva (tj. vyn´ asob´ıme f z´ akladem z) a zmenˇs´ıme e o 1. Vr´ at´ıme se na 2. 4. Posun doprava: Na tento krok pˇrijdeme pouze v pˇr´ıpadˇe, ˇze |f | ≥ 1. Posuneme f o jednu pozici doprava (tj. vydˇel´ıme f z´ akladem z) a zvˇetˇs´ıme e o 1. 5. Zaokrouhlen´ı: Zaokrouhl´ıme f na p m´ıst. Zpravidla pouˇz´ıv´ ame vztah˚ u f := z −p [z p f + 0, 5] pro f > 0,
f := z −p z p f − 0, 5 pro f < 0.
Zde [x] oznaˇcuje celou ˇca ´st ˇc´ısla x, tj. nejbliˇzˇs´ı menˇs´ı cel´e ˇc´ıslo, x oznaˇcuje horn´ı celou ˇca ´st“, nejbliˇzˇs´ı ” vˇetˇs´ı cel´e ˇc´ıslo. Lze pouˇz´ıt i jin´ ych vztah˚ u; ty, kter´e jsme uvedli, vˇsak vedou k nejpˇr´ıznivˇejˇs´ımu rozloˇzen´ı zaokrouhlovac´ıch chyb. V tomto m´ıstˇe m˚ uˇze doj´ıt k zaokrouhlovac´ımu pˇreteˇcen´ı“: z mantisy |f | < 1 ” vznikne zaokrouhlen´ım mantisa |f | = 1. V takov´em pˇr´ıpadˇe se vr´ at´ıme na 4. 6. Kontrola e: Je-li e pˇr´ıliˇs velk´e (pˇresahuje-li povolen´ y rozsah), nastalo pˇreteˇcen´ı exponentu“. Je-li e pˇr´ıliˇs ” mal´e, nastalo podteˇcen´ı exponentu“. Tyto situace se obvykle hodnot´ı jako chyby, nebot’ v´ ysledek nelze ” vyj´ adˇrit jako normalizovan´e re´ aln´e ˇc´ıslo.) Nastav´ıme pˇr´ıznak pˇreteˇcen´ı nebo podteˇcen´ı. 7. Sbalen´ı: Sloˇz´ıme e a f dohromady do pˇredepsan´eho tvaru reprezentace re´ aln´eho ˇc´ısla. Algoritmus normalizace lze zn´ azornit v´ yvojov´ ym diagramem, kter´ y vid´ıte na obr. 7.5. Kv˚ uli pohodl´ı v nˇem rozhodov´ an´ı m´ısto pˇredepsan´e znaˇcky vyznaˇc´ıme ov´ alem.
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
124
Test f |f | ≥ 1 f =0
Posun doprava
Je f normalizovan´e? ne
ano
Posun doleva
Zaokrouhlen´ı
ano
|f | = 1? ne Kontrola e
Pˇreteˇcen´ı nebo podteˇcen´ı
Sbalen´ı norm´aln´ı konec Obr. 7.5: V´ yvojov´ y diagram algoritmu normalizace re´ aln´eho ˇc´ısla
7.3.4
N´ asoben´ı a dˇ elen´ı re´ aln´ ych ˇ c´ısel
Algoritmy pro n´ asoben´ı a dˇelen´ı re´ aln´ ych ˇc´ısel se liˇs´ı pouze v jednom kroku, takˇze je zap´ıˇseme spoleˇcnˇe. Odvol´ avaj´ı se na algoritmy pro n´ asoben´ı resp. dˇelen´ı cel´ ych ˇc´ısel. Opˇet pˇredpokl´ ad´ ame, ˇze je d´ an z´ aklad z, posun q, poˇcet ˇc´ıslic p a normalizovan´e re´ aln´ a ˇc´ısla u = (e u , fu ) a v = (ev , fv ). Hled´ ame jejich souˇcin w = u ×◦ v resp. pod´ıl w = u/◦ v. Podobnˇe jako u pˇredchoz´ıho algoritmu pˇredpokl´ ad´ ame, ˇze z´ aklad z je sud´ y. 1. Rozbalen´ı: V reprezentaci ˇc´ısel u a v od sebe oddˇel´ıme exponent a mantisu. To znamen´ a, ˇze budeme u obou ˇc´ısel u a v pracovat zvl´ aˇst’ s mantisou f a zvl´ aˇst’ s exponentem e. 2. Vlastn´ı n´ asoben´ı nebo dˇelen´ı: V pˇr´ıpadˇe n´ asoben´ı poloˇz´ıme ew = eu + ev − q,
f w = fu + fv
(7.4)
v pˇr´ıpadˇe dˇelen´ı pak ew = eu − ev + q + 1,
fw =
1 z fu
fv
(7.5)
Ve vztaz´ıch (7.4 a 7.5) pouˇz´ıv´ ame operace n´ asoben´ı resp. dˇelen´ı pro cel´ a ˇc´ısla. Protoˇze jsou obˇe ˇc´ısla podle pˇredpokladu normalizovan´ a, bude bud’ fw = 0, nebo 1/z 2 ≤ |fw | > 1, nebo nastalo dˇelen´ı nulou. V pˇr´ıpadˇe potˇreby m˚ uˇzeme tak´e zkr´ atit v tomto m´ıstˇe f w na p + 2 ˇc´ıslic (odseknout pˇreb´ yvaj´ıc´ı ˇc´ıslice). 3. Normalizace: Podobnˇe jako v pˇr´ıpadˇe sˇc´ıt´ an´ı a odeˇc´ıt´ an´ı zb´ yv´ a normalizovat v´ ysledek (a sbalit exponent s mantisou do jedn´e promˇenn´e).
7.3.5
Pˇ revod cel´ eho ˇ c´ısla na re´ aln´ e a naopak
Je d´ ano cel´e ˇc´ıslo i v doplˇ nkov´em k´ odu. Hled´ ame re´ aln´e ˇc´ıslo u = (e, f ) tak, aby se hodnota i rovnala hodnotˇe u. Pˇredpokl´ ad´ ame, ˇze dan´e cel´e ˇc´ıslo m´ a j m´ıst, hledan´e re´ aln´e ˇc´ıslo m´ a m´ıt p m´ıst mantisy. K tomu m˚ uˇzeme pouˇz´ıt n´ asleduj´ıc´ı algoritmus: 1. Poloˇz´ıme e = j.
ˇ ´ YCH ´ ˇ ´ISEL 7.4. PRESNOST ARITMETIKY REALN C
125
2. Zjist´ıme znam´enko v´ ysledku a zapamatujeme si je. 3. Poloˇz´ıme f := |i| z p−j . 4. Normalizujeme u = (e, f ) a uprav´ıme znam´enko mantisy v´ ysledku. Pˇ r´ıklad 7.4 Budeme pracovat v dekadick´e aritmetice. Poˇcet m´ıst re´ aln´eho ˇc´ısla j = 5, poˇcet m´ıst mantisy je p = 10. Chceme pˇrev´est pˇrev´est ˇc´ıslo i = −123 na re´ aln´e ˇc´ıslo u se stejnou hodnotou. Poloˇz´ıme tedy e := 5 a zapamatujeme si, ˇze i < 0. D´ ale budeme pouˇz´ıvat |i| = 123. V dalˇs´ım kroku poloˇz´ıme f := 123 × 1010−5 = 123 × 105 . Protoˇze mantisa obsahuje pouze ˇc´ıslice za desetinnou teˇckou, dospˇeli jsme tak k vyj´ adˇren´ı u = 0, 0012300000 × 105 . Normalizac´ı dospˇejeme k vyj´ adˇren´ı u = 0, 12300000 × 10 3 nebo 2 u = 1, 2300000 × 10 (podle toho, jak definujeme normalizovan´ y tvar). Nakonec uprav´ıme znam´enko mantisy − dostaneme u = −0, 12300000. Nyn´ı se pod´ıvejme na opaˇcnou u ´lohu. Je d´ ano re´ aln´e ˇc´ıslo u = (e, f ) a hled´ ame cel´e ˇc´ıslo i, jehoˇz hodnota vznikne odseknut´ım zlomkov´e ˇca ´sti u. Budeme pˇredpokl´ adat, ˇze mantisa f dan´eho ˇc´ısla u m´ a p m´ıst a hledan´e cel´e ˇc´ıslo ˇze m´ a j m´ıst. Pˇri tomto pˇrevodu m˚ uˇze doj´ıt k pˇreteˇcen´ı, bude-li cel´ a ˇca ´st re´ aln´eho ˇc´ısla vˇetˇs´ı neˇz nejvˇetˇs´ı zobraziteln´ a hodnota cel´eho ˇc´ısla. 1. Rozbalen´ı: Rozloˇz´ıme u na exponent e a mantisu f . 2. Kontrola exponentu: Je-li e ≤ 0, poloˇz´ıme i = 0 a skonˇc´ıme, nebot’ |u| < 1. Je-li e > E, kde E je nejvˇetˇs´ı moˇzn´ a hodnota exponentu cel´eho ˇc´ısla, nastalo pˇreteˇcen´ı, chyba, konec. 3. Zjist´ıme znam´enko u a zapamatujeme si je. 4. Je-li e = E, jdeme na 6. 5. Zvˇetˇs´ıme e o 1, posuneme f o jednu pozici doprava a vr´ at´ıme se na krok 4. 6. Poloˇz´ıme i := f /z p−j (tj. pˇresuneme prvn´ıch j m´ıst f do i). 7. Uprav´ıme znam´enko v´ ysledku: je-li u < 0, poloˇz´ıme i := −i.
Pˇ r´ıklad 7.5 Pˇredpokl´ ad´ ame opˇet dekadickou aritmetiku, deset m´ıst mantisy a pˇetim´ıstn´ a cel´ a ˇc´ısla. Je d´ ano ˇc´ıslo u = 32, 1 = 0, 321 × 102. Mantisa je tedy zobrazena jako desetim´ıstn´e ˇc´ıslo 3210000000, exponent e = 2. Maxim´ aln´ı moˇzn´ y exponent cel´eho ˇc´ısla je 5 (zap´ıˇseme-li nejvˇetˇs´ı cel´e ˇc´ıslo jako re´ aln´e, dostaneme 0, 99999 × 10 5 . Ve ˇctvrt´em a p´ at´em kroku algoritmu toto ˇc´ıslo uprav´ıme do tvaru 0, 000321 × 10 5 ., tj. mantisa bude nyn´ı reprezentov´ ana ˇc´ıslem 0003210000×102. Vydˇel´ıme-li mantisu (jako cel´e ˇc´ıslo) ˇc´ıslem 105, dostaneme 0000000032, coˇz je hledan´ a hodnota.
7.4
Pˇ resnost aritmetiky re´ aln´ ych ˇ c´ısel
V tomto odd´ılu si naznaˇc´ıme z´ akladn´ı probl´emy, na kter´e nar´ aˇz´ıme v souvislosti se zaokrouhlovac´ımi chybami pˇri v´ ypoˇctech s re´ aln´ ymi ˇc´ısly. Podrobnˇejˇs´ı informace lze naj´ıt napˇr. v Knuthovˇe knize [3].
7.4.1
Z´ akladn´ı u ´vahy
Pˇri u ´vah´ ach o pˇresnosti v´ ypoˇct˚ u se obvykle pouˇz´ıv´ a relativn´ı chyba. Jestliˇze reprezentujeme pˇresnou hodnotu x v poˇc´ıtaˇci pˇribliˇznˇe hodnotou ξ = x (1 + ε), oznaˇcujeme veliˇcinu ε = (ξ − x) /x jako relativn´ı chybu. V n´ asleduj´ıc´ıch pˇr´ıkladech uvid´ıme, ˇze zaokrouhlovac´ı chyby mohou do aritmetick´ ych operac´ı (zejm´ena jde o sˇc´ıt´ an´ı a odeˇc´ıt´ an´ı) vn´est velkou relativn´ı chybu. Jedn´ım z nepˇr´ıjemn´ ych d˚ usledk˚ u pak je, ˇze neplat´ı asociativn´ı z´ akon, (u +◦ v) +◦ w 6= u +◦ (v +◦ w) .
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
126 Pˇ r´ıklad 7.6 Asociativn´ı z´ akon
Uk´ aˇzeme si pˇr´ıklad, ve kter´em bude z´ aleˇzet na poˇrad´ı sˇc´ıt´ an´ı resp. odeˇc´ıt´ an´ı. Pˇredpokl´ ad´ ame dekadickou aritmetiku s osmim´ıstnou mantisou. Budeme zapisovat pouze ˇc´ıslice, kter´e jsou souˇca ´st´ı mantisy, takˇze n´ am nˇekter´ a ˇc´ısla budou konˇcit desetinnou ˇca ´rkou. (11111113, +◦ − 11111111, ) +◦ 7, 5111111 = 2, 0000000 +◦ 7, 5111111 = 9, 5111111; 11111113, +◦ (−11111111, +◦7, 5111111) = 11111113, +◦ − 11111103 = 10, 000000; Vˇsimnˇete si, ˇze relativn´ı chyba v´ ysledku je t´emˇeˇr 5%. Podobnˇe lze uk´ azat, ˇze asociativn´ı z´ akon neplat´ı pro n´ asoben´ı re´ aln´ ych ˇc´ısel podle algoritmu z odst. 7.3.4. Skuteˇcnost, ˇze asociativn´ı z´ akon neplat´ı, m˚ uˇzP e m´ıt obrovsk´e d˚ usledky. Uvˇedomme si, ˇze ˇrada bˇeˇzn´ ych maten matick´ ych z´ apis˚ u, jako napˇr. a + b + c nebo i=1 ai , je zaloˇzena pr´ avˇe na pˇredpokladu, ˇze plat´ı asociativn´ı z´ akon. Na druh´e stranˇe z tvaru algoritmu pro sˇc´ıt´ an´ı plyne, ˇze pro operace +◦“ plat´ı komutativn´ı z´ akon a pro +◦“ ” ” ◦ a − “ plat´ı jeˇstˇe nˇekter´e dalˇs´ı vztahy: ” u +◦ v = v +◦ u; (7.6) u −◦ v = u +◦ −v, − (u +◦ v) = −u +◦ −v, u +◦ 0 = u, u −◦ v = − (v −◦ (−u)) D´ ale odtud plyne, ˇze pokud plat´ı u +◦ v = 0, mus´ı b´ yt u = −v. Pro libovoln´e w, pro kter´e nenastane pˇreteˇcen´ı, plat´ı: je − li u < v, je u +◦ w < v +◦ w (7.7) Vztah (7.7) nen´ı zcela zˇrejm´ y, proto jej dok´ aˇzeme. Za zaokrouhlovat ˇc´ıslo x na p m´ıst: e−p p−e [z x + 0, 5] z 0 (x, p) = e−p p−e z [z x + 0, 5]
t´ım u ´ˇcelem definujeme funkci r (x, p), kter´ a bude pro z e−1 ≤ x < z e , pro x = 0, pro z e−1 ≤ −x < z e .
(7.8)
Odtud plyne r (−x, p) = − (x, p) a r (bx, p) = br (x, p) pro kaˇzd´e b. Operace, zaveden´e algoritmy z odd´ıl˚ u 7.3.2. a 7.3.4., splˇ nuj´ı vztahy u +◦ v = r (u + v, p) , u −◦ v = r (u − v, p) , u ×◦ v = r (uv, p) , u/◦ v = r (u/v, p) , za pˇredpokladu, ˇze nedojde k pˇreteˇcen´ı exponentu, tj. za pˇredpokladu, ˇze v´ ysledky operac´ı u + v, u − v, u × v, u/v leˇz´ı ve spr´ avn´em rozmez´ı hodnot. Odtud a ze skuteˇcnosti, ˇze funkce r je neklesaj´ıc´ı vzhledem k x, jiˇz plyne (7.7). Na z´ akladˇe pˇredchoz´ıch vztah˚ u snadno zjist´ıme, ˇze pokud nenastane pˇreteˇcen´ı, plat´ı ˇrada bˇeˇzn´ ych vztah˚ u, jako napˇr. v ×◦ u = u ×◦ v, (−u) ×◦ v = − (u ×◦ v) , 1 ×◦ u = u u ×◦ v = 0 pr´ avˇe kdyˇz u = 0 nebo v = 0, ◦ (−u) / v = u/ (−v) = − (u/ v) , 0/◦ v = 0, u/◦ 1 = u, ◦
◦
Bude-li platit 0 < u ≤ v a w > 0, z˚ ustanou v platnosti obvykl´e nerovnosti u ×◦ w ≤ v ×◦ w, u/◦ w ≤ v/◦ w, w/◦ u ≥ w/◦ v. Na druh´e stranˇe n´ asleduj´ıc´ı pˇr´ıklad ukazuje, ˇze neplat´ı distributivn´ı z´ akon.
u/◦ u = 1.
ˇ ´ YCH ´ ˇ ´ISEL 7.4. PRESNOST ARITMETIKY REALN C
127
Pˇ r´ıklad 7.7 Distributivn´ı z´ akon Uvaˇzujme opˇet dekadickou aritmetiku s osmi platn´ ymi ˇc´ıslicemi. Uk´ aˇzeme si pˇr´ıklad, kdy pro operace, zaveden´e algoritmy z 7.3.2. a 7.3.4., neplat´ı distributivn´ı z´ akon. Jako pˇr´ıklad vezmeme hodnoty tˇr´ı promˇenn´ ych u = 20000, 000, v = −6, 0000000, w = 6, 0000003. Pak (u ×◦ v) +◦ (u ×◦ w) = −120000, 00 +◦ 120000, 01 = 0, 0100000, zat´ımco u ×◦ (v +◦ w) = 20000, 00 ×◦ 0, 00000030 = 0, 00600000.
Podobnˇe vezmeme-li u = v = 0, 00005000, bude platit nerovnost 2 (u ×◦ u + v ×◦ v) < (u +◦ v) ×◦ (u +◦ v), tj. - pˇreps´ ano bˇeˇzn´ ym matematick´ ym zp˚ usobem 2 2 u2 + v 2 < (u + v) . To znamen´ a, ˇze pˇri v´ ypoˇctu standardn´ı smˇerodatn´ v e odchylky podle!vzorce u n 2 n X X 1u t 2 σ= n xk − xk n k=1
k=1
se n´ am m˚ uˇze st´ at, ˇze budeme poˇc´ıtat odmocninu ze z´ aporn´eho ˇc´ısla! Pˇ r´ıklad 7.8 Rovnost re´ aln´ ych ˇ c´ısel
Pˇri ˇreˇsen´ı soustav line´ arn´ıch algebraick´ ych rovnic, ale i pˇri dalˇs´ıch numerick´ ych v´ ypoˇctech, se setk´ ame s iteraˇcn´ımi procedurami, zaloˇzen´ ymi na rekurentn´ım vztahu tvaru xn+1 = f (xn ) (7.9) Z teorie v´ıme, ˇze posloupnost xn konverguje k limitˇe x. Nicm´enˇe bylo by chybou stanovit jako podm´ınku ukonˇcen´ı iteraˇcn´ıho cyklu rovnost xn = xn+1 , nebot’ vzhledem k zaokrouhlovac´ım chyb´ am pˇri v´ ypoˇctu f (xn ) m˚ uˇze b´ yt posloupnost xn periodick´ a. Proto je rozumnˇejˇs´ı ukonˇcit v´ ypoˇcet, bude-li napˇr. |xn − xn+1 | < δ pro δ vhodn´e . Takov´ ato podm´ınka ovˇsem pˇredpokl´ ad´ a, ˇze zn´ ame alespoˇ n ˇra ´d limity x. Pokud nem´ ame ˇza ´dn´e pˇredbˇeˇzn´e informace o v´ ysledku, je vhodnˇejˇs´ı pouˇz´ıt podm´ınku |xn+1 − xn | ≤ε (7.10) |xn | Veliˇcina ve vztahu (7.10) ud´ av´ a relativn´ı m´ıru nepˇresnosti, kterou jsme ochotni pˇrijmout jako v´ ysledek iteraˇcn´ıho procesu (7.9). Vztah (7.10) bychom tak´e mohli interpretovat jako tvrzen´ı, ˇze x n je pˇribliˇznˇe rovno“ ” xn+1 .
7.4.2
M´ıra nepˇ resnosti
V pˇredchoz´ım odstavci jsme zjistili, ˇze nˇekter´e bˇeˇzn´e vztahy pro z´ akladn´ı aritmetick´e operace d´ıky zaokrouhlovac´ım chyb´ am neplat´ı. Pod´ıvejme se tedy podrobnˇeji na vliv zaokrouhlovac´ıch chyb algoritm˚ u z odd´ıl˚ u 7.3.2. a 7.3.4. Z definice (7.8) funkce r plyne r (x, p) = x (1 + δp (x)) , To znamen´ a, ˇze m˚ uˇzeme vˇzdy ps´ at a +◦ b = (a + b) (1 + δp (a + b)) , a −◦ b = (a − b) (1 + δp (a − b)) ,
kde |δp (x)| ≤ 0, 5z 1−p . a ×◦ b = (a × b) (1 + δp (a × b)) , a/◦ b = (a/b) (1 + δp (a/b)) .
Tyto vztahy n´ am mohou poslouˇzit jako podklad pro odhad chyby. Pod´ıvejme se na asociativn´ı z´ akon pro n´ asoben´ı re´ aln´ ych ˇc´ısel. Dostaneme postupnˇe (u ×◦ v) ×◦ w = ((uv) (1 + δi )) ×◦ w = uvw (1 + δ1 ) (1 + δ2 ) (7.11) u ×◦ (v ×◦ w) = u ×◦ ((vw) (1 + δ − 3)) = uvw (1 + δ3 ) (1 + δ4 ) .
Tyto vztahy plat´ı (za pˇredpokladu, ˇze nedojde k pˇreteˇcen´ı exponentu) pro nˇejak´ a δ i , i = 1, . . . , 4. Pˇripomeˇ nme si, ˇze vˇsechna tato i vyhovuj´ı podm´ınce |δi | ≤ 0, 5z 1−p. To znamen´ a, ˇze (1 + δ1 ) (1 + δ2 ) (u ×◦ v) ×◦ w = =1+δ ◦ ◦ u × (v × w) (1 + δ3 ) (1 + δ4 )
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
128
2 kde |δ| =≤ 2z 1−p / 1 − 0, 5z 1−p . Uk´ azali jsme tedy, ˇze pokud nenastane pˇreteˇcen´ı exponentu, je (u × ◦ w)×◦ w ◦ nˇejak´em smyslu pˇribliˇznˇe rovno u × (v ×◦ w). Pokusme se d´ ale upˇresnit, co znamen´ a pˇribliˇznˇe rovno. Zavedeme nov´e operace pro porovn´ av´ an´ı re´ aln´ ych ˇc´ısel zobrazen´ ych v poˇc´ıtaˇci, kter´e budou br´ at v u ´vahu velikost porovn´ avan´ ych ˇc´ısel a moˇznou relativn´ı chybu. Je-li z z´ aklad ˇc´ıseln´e soustavy a q posun a plat´ı-li u = (eu , fu ) a v = (ev , fv ), definujeme u ≺ v (ε), pr´ avˇe kdyˇz v − u > ε. max (z eu −q , z ev −q ) ...
u je urˇcitˇe menˇs´ı neˇz v“; ”
u ∼ v (ε), pr´ avˇe kdyˇz |v − u| ≤ ε. max (z eu −q , z ev −q ) ...
u je pˇribliˇznˇe rovno v“; ”
u v (ε), pr´ avˇe kdyˇz u − v > ε. max (z eu −q , z ev −q ) ...
u je urˇcitˇe vˇetˇs´ı neˇz v“; ”
u ≈ v (ε), pr´ avˇe kdyˇz |v − u| ≤ ε. max (z eu −q , z ev −q ) ...
u je v podstatˇe rovno v“. ”
V tˇechto definic´ıch ud´ av´ a ε uvaˇzovanou relativn´ı m´ıru aproximace. Zaveden´e oper´ atory splˇ nuj´ı ˇradu jednoduch´ ych vztah˚ u, kter´e ukazuj´ı pravidla pro poˇc´ıt´ an´ı s pˇribliˇzn´ ymi hodnotami, napˇr. u ≺ v (ε) ⇒ v u (ε) , u ≈ v (ε) ⇒ u ∼ v (ε) , u ≈ u (ε) u v (ε1 ) ∧ ε1 < ε2 ⇒ v (ε2 ) .
(7.12)
Obdobn´e vztahy jako (11) plat´ı i pro operace ∼ a ≈. Pˇr´ımo z definice tˇechto operac´ı d´ ale plyne, ˇze je − li u v (ε1 ) ∧ v w (ε2 ) ⇒ u w (ε1 + ε2 ) , je − li u ≈ v (ε1 ) ∧ v ≈ w (ε2 ) ⇒ u ∼ w (ε1 + ε2 ) . Bez obt´ıˇz´ı lze tak´e dok´ azat, ˇze |u − v| ≤ ε |u| ∧ |u − v| ≤ ε |v| ⇒ u ≈ v (ε) , |u − v| ≤ ε |u| ∨ |u − v| ≤ ε |v| ⇒ u ∼ v (ε) , a naopak, pro normalizovan´ a ˇc´ısla u a v a ε < 1 plat´ı u ≈ v (ε) ⇒ |u − v| ≤ zε |u| ∧ |u − v| ≤ zε |v| , u ∼ v (ε) ⇒ |u − v| ≤ zε |u| ∨ |u − v| ≤ zε |v| . Pˇ r´ıklad 7.9 Opˇ et asociativn´ı z´ akon Pod´ıv´ ame se, jak je to s asociativn´ım z´ akonem pro n´ asoben´ı (viz t´eˇz (7.11) a vztahy n´ asleduj´ıc´ı). Dostaneme 1−p (1 + δ ) (1 + δ ) 2z 1 2 ≤ |u ×◦ (v ×◦ w)| . |(u ×◦ v) ×◦ w − u ×◦ (v ×◦ w)| = |u ×◦ (v ×◦ w)| (1 + δ3 ) (1 + δ4 ) 1 − 1 z 1−p 2
◦
◦
◦
◦
T´ yˇz v´ ysledek dostaneme i pro pˇr´ıpad |u × (v × w) − (u × v) × w|. Z toho plyne, ˇze pro 2 ε ≥ 2z 1−p / 1 − z 1−p plat´ı
u ×◦ (v ×◦ w) ≈ (u ×◦ v) ×◦ w. Poˇc´ıt´ ame-li v des´ıtkov´e soustavˇe, z = 10, s pˇresnost´ı na 8 platn´ ych cifer, p = 8, m˚ uˇzeme na z´ akladˇe tohoto v´ ysledku poloˇzit ε = 0, 00000021. Na z´ avˇer uvedeme vˇetu, kter´ a ukazuje, ˇze rozd´ıl mezi u +◦ v a u + v lze odhadnout pomoc´ı veliˇcin, kter´e m˚ uˇzeme vypoˇc´ıtat s pomoc´ı operac´ı v jednoduch´e pˇresnosti. Tato vˇeta ovˇsem plat´ı pouze za pˇredpokladu, ˇze v normalizaˇcn´ım algoritmu pouˇzijeme zaokrouhlov´ an´ı, nikoli odˇrez´ av´ an´ı. Vˇ eta: Bud’te u a v normalizovan´ a ˇc´ısla. Poloˇz´ıme u0 = (u+◦ ) −◦ v,
v 00 = (u +◦ v) −◦ u0 .
Pak plat´ı: u + v = (u +◦ v) + ((u −◦ u0 ) + (v −◦ v 00 )) . D˚ ukaz lze naj´ıt napˇr. v Knuthovˇe knize [3], str. 203.
ˇ ´ YCH ´ ˇ ´ISEL 7.4. PRESNOST ARITMETIKY REALN C
129
Pˇ r´ıklad 7.10: pouˇ zit´ı Taylorovy ˇ rady ˇ Casto se setk´ ame s u ´lohou vypoˇc´ıtat se zadanou pˇresnost´ı hodnoty nˇejak´e transcendentn´ı funkce. Jedn´ım z prvn´ıch n´ apad˚ u, jak postupovat, b´ yv´ a vyuˇzit´ı Taylorovy nebo jin´e mocninn´e ˇrady. Uk´ aˇzeme si, ˇze se v tom mohou skr´ yvat nepˇr´ıjemn´e probl´emy. Jak v´ıme, jsou funkce sinus a kosinus definov´ any pomoc´ı ˇrad +∞ +∞ X X (−1)k x2k (−1)k x2k+1 , cos x = sin x = (2k + 1)! (2k)!
(7.13)
k=0
k=0
Pod´ıvejme se, jak dopadne pokus poˇc´ıtat pomoc´ı ˇrady (7.13) funkci kosinus; z´ avˇery pro funkci sinus jsou podobn´e. Snadno dok´ aˇzeme napˇr. pomoc´ı D’Alembertova krit´eria, ˇze ˇrada pro tuto funkci konverguje pro libovoln´e komplexn´ı x. Nav´ıc jsou to ˇrady se stˇr´ıdav´ ymi znam´enky. Pod´ıl dvou po sobˇe n´ asleduj´ıc´ıch sˇc´ıtanc˚ u je an+1 x2 =− ; an (2n + 1) (2n + 2)
(7.14)
To znamen´ a, ˇze poˇc´ınaje jist´ ym n0 (x) se bude pro libovoln´e x absolutn´ı hodnota an monot´ onnˇe zmenˇsovat. Jestliˇze tedy ˇradu (7.13) nahrad´ıme koneˇcn´ ym souˇctem m k X (−1) x2k , cos x ≈ (2k)! k=0
dopust´ıme se chyby, kter´ a nepˇres´ ahne absolutn´ı hodnotu prvn´ıho zanedban´eho ˇclenu. Tolik teorie, zn´ am´ az matematick´e anal´ yzy ze 2. semestru.
Nejprve se pokus´ıme spoˇc´ıtat hodnotu cos 1 s chybou, kter´ a nepˇresahuje 10 −6 . Podle (7.14) kles´ a an monot´ onnˇe k nule poˇc´ınaje nult´ ym ˇclenem. To znamen´ a, ˇze staˇc´ı naj´ıt n, pro kter´e bude 1 |an | = ≤ 10−6 . (2n)! Snadno se pˇresvˇedˇc´ıme, ˇze tato podm´ınka je splnˇena jiˇz pro n = 5; to znamen´ a, ˇze staˇc´ı seˇc´ıst prvn´ıch 5 sˇc´ıtanc˚ u v ˇradˇe (7.13) (pro n = 0, . . . , 4). Pouˇzijeme-li kter´ ykoli z typ˚ u re´ aln´ ych ˇc´ısel, popsan´ ych v pˇr´ıkladu 7.3, budou zaokrouhlovac´ı chyby menˇs´ı neˇz poˇzadujeme, proto se jimi nemus´ıme zab´ yvat. Nyn´ı se pokusme spoˇc´ıtat cos 100. Podle (7.14) budou absolutn´ı hodnoty sˇc´ıtanc˚ u v ˇradˇe (7.13) monot´ onnˇe nar˚ ustat pro n = 0, . . . , 50 a teprve pak zaˇcnou klesat k nule. Nejvˇetˇs´ı ˇclen, a 50 , je 100200 a50 = 100! Snadno zjist´ıme, ˇze dekadick´ y logaritmus a50 je log a50 = 200 − 157, 97 = 42, 03. To znamen´ a, ˇze nejvˇetˇs´ı ze sˇc´ıtanc˚ u v ˇradˇe (7.13) m´ a 43 ˇc´ıslic pˇred desetinnou ˇca ´rkou, ale my jej potˇrebujeme zn´ at s pˇresnost´ı nejm´enˇe 10−6 . Celkem bychom tedy potˇrebovali m´ıt k dispozici reprezentaci re´ aln´ ych ˇc´ısel s alespoˇ n 49 m´ısty mantisy. Ovˇsem poˇc´ıtaˇce PC n´ am poskytuj´ı maxim´ alnˇe 19 cifer. Proto v´ ysledek, kter´ y bychom z´ıskali pˇr´ım´ ym v´ ypoˇctem z ˇrady (7.13), by neobsahoval ani jednu spolehlivou“ ˇc´ıslici. ” V tomto pˇr´ıpadˇe je ovˇsem ˇreˇsen´ı snadn´e. Vzhledem k periodicitˇe funkc´ı cos resp. sin staˇc´ı odeˇc´ıst vhodn´ y n´ asobek 2 a pak poˇc´ıtat hodnotu funkce pro argument z intervalu h−π, π). Z dalˇs´ıch vztah˚ u pro goniometrick´e funkce π π sin x ± = ± cos x, cos x ± = ∓ sin x 2 2 plyne, ˇze staˇc´ı poˇc´ıtat hodnoty z intervalu −/2, /2. Ze vztah˚ u π π sin − x = cos x, cos − x = sin x 2 2 nakonec m˚ uˇzeme uzavˇr´ıt, ˇze v pˇr´ıpadˇe funkc´ı sinus a kosinus m˚ uˇzeme u ´lohu v´ ypoˇctu hodnoty vˇzdy pˇrev´est na souˇcet ˇrady (7.13) s |x| < π/4 < 1. Nicm´enˇe i zde se m˚ uˇze projevit vliv zaokrouhlovac´ıch chyb. Vrat’me se k v´ ypoˇctu cos 100. Z toho, co jsme si ˇrekli, plyne, ˇze staˇc´ı spoˇc´ıtat cos (100 − 32π) = cos (−0, 5309849148762)
Zde jsme mˇeli ˇstˇest´ı, ˇze goniometrick´e funkce jsou periodick´e. Pro jin´e funkce se m˚ uˇze st´ at, ˇze nebudeme moci Taylor˚ uv rozvoj vyuˇz´ıt v˚ ubec.
130
´ ALGORITMY KAPITOLA 7. SEMINUMERICKE
Kapitola 8
Nˇ ekter´ e dalˇ s´ı algoritmy V t´eto kapitole se sezn´ am´ıme s nˇekolika dalˇs´ımi algoritmy, kter´e se nehodily do ˇza ´dn´e z pˇredchoz´ıch kapitol.
8.1
Rozklad grafu na komponenty
Obˇcas se setk´ ame s u ´lohou rozloˇzit graf na komponenty. Pˇ r´ıklad 8.1 Vezmˇeme orientovan´ y graf z obr´ azku 8.1. Jeho incidenˇcn´ı matice je 1 0 0 0 1 0 1 0 1 0 A= 0 0 1 0 1 . 0 1 0 1 0 1 0 1 0 1
(8.1)
Je zˇrejm´e, ˇze se tento graf skl´ ad´ a ze dvou nez´ avisl´ ych komponent, tvoˇren´ ych uzly 1, 3 5 a 2, 4. Pˇri konstrukci algoritmu, kter´ y bude hledat rozklad grafu na komponenty, vyjdeme z oˇcividn´eho faktu, ˇze pokud jak z uzlu i tak z uzlu j vede hrana do uzlu k, leˇz´ı vˇsechny tˇri ve stejn´e komponentˇe. Skuteˇcnost, ˇze v grafu existuje z´ aroveˇ n cesta i → k a j → k, znamen´ a, ˇze incidenˇcn´ı matice bude obsahovat nenulov´e prvky Aik a Ajk . To znamen´ a, ˇze do t´eˇze komponenty budou patˇrit ty ˇra ´dky matice A, kter´e oba obsahuj´ı nenulov´ y prvek v t´emˇze sloupci. Algoritmus, kter´ y urˇc´ı komponenty grafu, popsan´eho incidenˇcn´ı matic´ı A, bude pouˇz´ıvat pomocn´ ych mnoˇzin Si , sloˇzen´ ych z ˇra ´dkov´ ych index˚ u matice A. M˚ uˇzeme jej formulovat takto: 1. Pro i = 1, . . . , n (n je poˇcet uzl˚ u grafu, tedy poˇcet ˇra ´dk˚ u incidenˇcn´ı matice A) pˇriˇrad´ıme i-t´emu ˇra ´dku matice A mnoˇzinu Si = {i}. 2. Najdeme sloupec l s nejvˇetˇs´ım poˇctem nenulov´ ych prvk˚ u - necht’ je to q. Je-li q = 1, jdeme na krok 5. 3. Udˇel´ ame logick´ y souˇcet1 vˇsech ˇra ´dk˚ u, kter´e maj´ı v l-t´em sloupci 1. Tyto ˇra ´dky z matice A odstran´ıme a nahrad´ıme je vytvoˇren´ ym logick´ ym souˇctem. 4. Novˇe vytvoˇren´emu ˇra ´dku pˇriˇrad´ıme mnoˇzinu S = Si , tj. sjednocen´ı mnoˇzin S = ˇra ´dky, a vr´ at´ıme se na krok 2.
S
Si pro odstranˇen´e
5. Kaˇzd´ y ˇra ´dek nyn´ı pˇredstavuje jednu komponentu grafu, popsan´eho matic´ı A. Mnoˇzina S i , kter´ a takov´emu ˇra ´dku odpov´ıd´ a, obsahuje ˇc´ısla uzl˚ u, kter´e tvoˇr´ı jednu komponentu. 1 Logick´ y
souˇcet definujeme pro ˇc´ısla 0 a 1 takto: 0 + 0 = 0, 0 + 1 = 1 + 0 = 1 + 1 = 1.
131
ˇ ´ DALSˇ´I ALGORITMY KAPITOLA 8. NEKTER E
132
1
5
2
4
3
Obr. 8.1: Orientovan´ y graf matice A Pˇ r´ıklad 8.1 (pokraˇ cov´ an´ı) Pod´ıvejme se, jak bude popsan´ y algoritmus fungovat v Mnoˇziny Si jsou 1 0 0 0 0 1 0 1 A= 0 0 1 0 0 1 0 1 1 0 1 0
pˇr´ıpadˇe grafu z obr´ azku 8.1, popsan´eho matic´ı (8.1). 1 0 1 0 1
S1 = {1} S2 = {2} S3 = {3} . S4 = {4} S5 = {5}
Nejv´ıce nenulov´ ych prvk˚ u je v 5. sloupci (v 1., 3. a 5. ˇra ´dku). Nahrad´ıme proto 1. ˇra ´dek logick´ ym souˇctem 1., 3. a 5. ˇra ´dku a mnoˇzinu S1 sjednocen´ım S ∪ S ∪ S . Dostaneme 1 3 5 1 0 1 0 1 S1 = {1, 3, 5} A = 0 1 0 1 0 S2 = {2} . 0 1 0 1 0 S3 = {4}
Nyn´ı je nejv´ıce nenulov´ ych prvk˚ u ve 2. sloupci. Nahrad´ıme proto 2. ˇra ´dek logick´ ym souˇctem 2. a 3. ˇra ´dku a dostaneme S1 = {1, 3, 5} 1 0 1 0 1 . A= S2 = {2, 4} 0 1 0 1 0
Nyn´ı jiˇz vˇsechny sloupce obsahuj´ı nejv´ yˇse jeden nenulov´ y prvek, takˇze algoritmus konˇc´ı. Zjistili jsme, ˇze jednu komponentu tvoˇr´ı uzly 1, 3 a 5 a druhou zb´ yvaj´ıc´ı dva.
8.2
Tranzitivn´ı uz´ avˇ er orientovan´ eho grafu
V tomto odstavci si uk´ aˇzeme algoritmus, kter´ y umoˇzn ˇuje ˇreˇsit n´ asleduj´ıc´ı u ´lohu: M´ ame orientovan´ y graf G s n uzly. Zaj´ım´ a n´ as, jak zjistit, zda pro libovoln´e i a j v tomto grafu existuje cesta (libovoln´e d´elky) z uzlu i do uzlu j. S touto u ´lohou se setk´ ame, jestliˇze budeme napˇr. zjiˇst’ovat, zda se v programu vyskytuje nepˇr´ım´ a rekurze, a budeme m´ıt k disposici seznam podprogram˚ u, kter´e volaj´ı jednotliv´e funkce a procedury. Dalˇs´ı aplikace t´eto u ´lohy se mohou t´ ykat rozkladu matice soustavy rovnic apod. Graf G pop´ıˇseme incidenˇcn´ı matic´ı A (G) = A. Pˇripomeˇ nme si, ˇze v A plat´ı a ij = 1, pr´ avˇe kdyˇz v G existuje orientovan´ a hrana hi, ji. To znamen´ a, ˇze A popisuje vˇsechny cesty d´elky 1. Pod´ıvejme se nyn´ı na cesty d´elky 2. Aby v G existovala orientovan´ a cesta d´elky 2 z i do j, mus´ı existovat uzel s tak, ˇze G obsahuje hrany hi, si a hs, ji. Jestliˇze tedy projdeme vˇsechny moˇzn´e uzly s, zjist´ıme, zda v G hledan´ a hrana existuje. Cesta z i do j pˇres pevnˇe zvolen´e s existuje, pr´ avˇe kdyˇz jsou v incidenˇcn´ı matici grafu G rovny jedn´e prvky ais a asj , tj. je-li ais × asj = 1.
´ ER ˇ ORIENTOVANEHO ´ 8.2. TRANZITIVN´I UZAV GRAFU
133
Jak´ akoli cesta z i do j d´elky 2 existuje, je-li nenulov´ y souˇcet n X bij = ais asj .
(8.2)
s=1
Je zˇrejm´e, ˇze bij je prvek matice B = A2 . Budeme-li v (8.2) uvaˇzovat m´ısto obyˇcejn´eho souˇctu logick´ y souˇcet, bude B incidenˇcn´ı matice grafu G2 , kter´ y m´ a stejn´ y poˇcet uzl˚ u jako G, a ve kter´em jsou uzly hi, ji spojeny hranou, jestliˇze v G existuje cesta z i do j d´elky 2. Podobnˇe uk´ aˇzeme, ˇze A3 je incidenˇcn´ı matice grafu G3 , kter´ y m´ a stejn´ y poˇcet uzl˚ u jako G, a ve kter´em jsou uzly hi, ji spojeny hranou, jestliˇze v G existuje cesta z i do j d´elky 3, . . . , An−1 je incidenˇcn´ı matice grafu Gn−1 , kter´ y m´ a stejn´ y poˇcet uzl˚ u jako G, a ve kter´em jsou uzly hi, ji spojeny hranou, jestliˇze v G existuje cesta z i do j d´elky n − 1. Vzhledem k tomu, ˇze graf G obsahuje n uzl˚ u, m˚ uˇze m´ıt nejkratˇs´ı orientovan´ a cesta mezi dvˇema r˚ uzn´ ymi uzly d´elku nejv´ yˇse n − 1. Chceme-li tedy umˇet pro kaˇzd´e i a j zjistit, zda v G existuje cesta jak´ekoli d´elky z i do j, staˇc´ı spoˇc´ıtat matici n X Ai . (8.3) Q= i=1
V tomto v´ ypoˇctu pouˇz´ıv´ ame m´ısto obyˇcejn´eho sˇc´ıt´ an´ı opˇet logick´ y souˇcet. Q je incidenˇcn´ı matice tzv. tranzitivn´ıho uz´ avˇeru G− grafu G, tedy grafu, kter´ y obsahuje hranu hi, ji, pr´ avˇe kdyˇz G obsahuje cestu libovoln´e d´elky z i do j. Budeme-li souˇcet (8.3) poˇc´ıtat na z´ akladˇe definice n´ asoben´ı matic, budeme potˇrebovat O n4 operac´ı (n n´ asoben´ı matic, kaˇzd´e vyˇzaduje n3 operac´ı). Uk´ aˇzeme si ale, ˇze matici Q m˚ uˇzeme spoˇc´ıtat s pouˇzit´ım O n3 operac´ı. Tento algoritmus vytvoˇr´ı z matice A pˇr´ımo matici Q, tj. z grafu G vytvoˇr´ı pˇr´ımo jeho tranzitivn´ı uz´ avˇer G − . Jde o postup, ve kter´em vytvoˇr´ıme posloupnost graf˚ u G 0 , G1 , . . . , Gn : 1. Poloˇz´ıme G0 = G. 2. Pro i = 1, 2, . . . , n sestroj´ıme Gi z Gi−1 takto: vezmeme i-t´ y uzel grafu Gi−1 a za kaˇzdou dvojici j a k, j ∈ {1, . . . , n}, k ∈ {1, . . . , n}, pro kterou v Gi−1 existuj´ı hrany hj, ii a hi, ki, pˇrid´ ame do Gi hranu hj, ki. 3. Gn = G− . Dok´ aˇzeme, ˇze t´ımto postupem opravdu z´ısk´ ame tranzitivn´ı uz´ avˇer grafu G. Oznaˇc´ıme H mnoˇzinu hran grafu G, Hi mnoˇzinu hran grafu Gi a H − mnoˇzinu hran G− Je jasn´e, ˇze staˇc´ı dok´ azat, ˇze Hn = H − . Z bodu 2 je zˇrejm´e, ˇze plat´ı Hi ⊂ Hi+1 . Matematickou indukc´ı dok´ aˇzeme, ˇze pro vˇsechna i = 1, 2, . . . , n plat´ı H i ⊂ H − . Inkluze H0 ⊂ H − je zˇrejm´ a: kaˇzd´ a hrana p˚ uvodn´ıho grafu mus´ı b´ yt i hranou tranzitivn´ıho uz´ avˇeru. Pˇredpokl´ adejme tedy, ˇze pro nˇejak´e i plat´ı Hi ⊂ H − (tedy ˇze z existence hrany hi, ji v Hi plyne existence cesty z uzlu i do uzlu j v H0 ). Je-li hk, ji ∈ Hi+1 , znamen´ a to, ˇze bud’ hrana hk, ji ∈ Hi , nebo ˇze jsme ji do Hi+1 pˇridali na z´ akladˇe toho, ˇze v Hi existuj´ı pro nˇejak´e l hrany hk, li a hl, ji. V obou pˇr´ıpadech ale z indukˇcn´ıho pˇredpokladu plyne, ˇze v H 0 existuje cesta z k do j. To znamen´ a, ˇze pro vˇsechna i je Hi ⊂ H − , a tedy speci´ alnˇe Hn ⊂ H − . Nyn´ı jeˇstˇe potˇrebujeme dok´ azat opaˇcnou inkluzi, tj. H − ⊂ Hn . Je-li hk, ji ∈ H − , znamen´ a to, ˇze v H existuje cesta hk, i1 i , hi1 , i2 i , . . . , his , ji, s ≤ n. Vezmeme nejmenˇs´ı z ˇc´ısel i1 , . . . , is ; necht’ je to ˇc´ıslo ir . Pak ve druh´em kroku algoritmu pro uzel ir pˇrid´ ame do Hi hranu hir−1 , ir+1 i. To znamen´ a, ˇze Hr obsahuje cestu hk, i1 i , hi1 , i2 i , . . . , hir−1 , ir+1 i , . . . , his , ji. Nyn´ı opˇet vezmeme nejmenˇs´ı ze zb´ yvaj´ıc´ıch index˚ u atd. Tak uk´ aˇzeme, ˇze hk, ji ∈ Hn . T´ım je spr´ avnost algoritmu dok´ az´ ana. Na z´ akladˇe popsan´eho algoritmu m˚ uˇzeme napsat program, kter´ y transformuje incidenˇcn´ı matici A grafu G na incidenˇcn´ı matici G− . Jeho z´ akladem bude konstrukce
ˇ ´ DALSˇ´I ALGORITMY KAPITOLA 8. NEKTER E
134
for j := 1 to n do {pro kaˇ zd´ y for i := 1 to n do {prozkoum´ ame vˇ sechny if A[i,j] <> 0 then {existuje-li cesta z for k := 1 to n do if A[j,k] <> 0 then A[i,k] := 1; {tak budeme zkoumat, zda existuje i cesta z
uzel j} uzly i} i do j} j do k}
Tento u ´sek programu obsahuje 3 do sebe vnoˇren´e cykly pro hodnoty parametru od 1 do n; to znamen´ a, ˇze doba jeho prov´ adˇen´ı bude O n3 .
8.3
N´ asoben´ı matic: Strassen˚ uv algoritmus
Obvykl´ y algoritmus pro n´ asoben´ı matic je zaloˇzen na definici t´eto operace. Jsou-li A a B matice typu n × n a je-li C = AB, (8.4) je i, j-t´ y prvek matice C d´ an vztahem cij =
n X
aik bkj .
(8.5)
k=1
Na v´ ypoˇcet cij potˇrebujeme n n´ asoben´ı a n − 1 sˇc´ıt´ an´ı; na v´ ypoˇcet matice C tedy potˇrebujeme O n3 operac´ı. Kromˇe prostoru na uloˇzen´ı matic A, B a C nepotˇrebujeme prakticky ˇza ´dn´ y dalˇs´ı pamˇet’ov´ y prostor.
8.3.1
Rozdˇ elen´ı matice na bloky
Pˇredpokl´ adejme nyn´ı, ˇze poˇcet ˇra ´dk˚ u a sloupc˚ u matic vyhovuje podm´ınce n = 2k .
(8.6)
Pokus´ıme se pouˇz´ıt techniky rozdˇel a panuj. Matice A, B a C rozdˇel´ıme na ˇctvercov´e bloky o velikosti n/2 = 2k−1 , takˇze n´ asoben´ı (8.4) bude m´ıt tvar A11 A12 B11 B12 C11 C12 = . A21 A22 B21 B22 C21 C22 Snadno se pˇresvˇedˇc´ıme, ˇze pro bloky Cij plat´ı C11 = A11 B11 + A12 B21 ,
C12 = A11 B12 + A12 B22 ,
C21 = A21 B11 + A22 B21 ,
C22 = A21 B12 + A22 B22 .
(8.7)
To znamen´ a, ˇze potˇrebujeme 8 n´ asoben´ı blok˚ u a 4 sˇc´ıt´ an´ı. Protoˇze velikost blok˚ u je 2 k , m˚ uˇzeme bloky pˇri n´ asoben´ı opˇet rozdˇelit na ˇctvercov´e bloky atd.; nakonec dospˇejeme k n´ asoben´ı blok˚ u o velikosti 1 - tedy k n´ asoben´ı ˇc´ısel. Oznaˇcme S (n) poˇcet operac´ı, potˇrebn´ y k n´ asoben´ı dvou matic n × n. Protoˇze na seˇcten´ı dvou matic typu n × n potˇrebujeme O n2 operac´ı, dostaneme, ˇze posloupnost S (n) vyhovuje rekurentn´ımu vztahu n + qn2 pro n ≥ 2, (8.8) S (n) ≤ 8S 2 S (n) = 1 pro n = 1 (q je konstanta). S podobn´ ym rekurentn´ım vztahem se setk´ ame jeˇstˇe jednou, a proto jej vyˇsetˇr´ıme obecnˇeji. Budeme zkoumat posloupnost S (n), pro kterou plat´ı S (1) = d, S (n) ≤ aS (n/c) + bn2 ,
kde a, b, c a d jsou kladn´ a cel´ a ˇc´ısla a n = ck . Ze vztah˚ u (8.9) ym dosazov´ an´ım plyne postupn´ ad 2 2 S (c) ≤ ad + bc = bc 1 + 2 ; bc a2 d ad a S c2 ≤ abc2 1 + 2 + bc4 = bc2 1 + 2 + 4 ; bc c bc
Matematickou indukc´ı snadno dok´ aˇzeme, ˇze
(8.9)
´ ˇ 8.4. VYPO CET HODNOTY POLYNOMU a k−1 a k d a S ck ≤ bc2k 1 + 2 + · · · + 2 . + 2 c c c b
135 (8.10)
Z (8.10) pak plyne, ˇze pro a > c a n = ck plat´ı
! ! a k a k d a k d 2k c2 S (n) = S c ≤ bc + 2 ≤ bc + 2 = (8.11) a c b c b c2 − 1 2 2 ak bc2 d d c c k logc a k d k = = c b = Knlogc a . (8.12) + a b = a b + + a − c2 b a − c2 b a − c2 b V naˇsem pˇr´ıpadˇe je a = 8, c = 2, takˇze na z´ akladˇe (8.12) dost´ av´ ame S (n) ≤ O nlog 8 = O n3 . To znamen´ a, ˇze popsan´e rozdˇelen´ı na bloky nejsp´ıˇs nepˇrineslo ˇza ´dn´ y uˇzitek (vzhledem k tomu, ˇze jsme pracovali s nerovnost´ı, nem˚ uˇzeme si dovolit ostˇrejˇs´ı tvrzen´ı). k
8.3.2
2k
a k −1 c2 a c2 − 1
Strassen˚ uv algoritmus
Strassen˚ uv algoritmus n´ asoben´ı matic je zaloˇzen na postˇrehu, ˇze bloky C ij m˚ uˇzeme poˇc´ıtat tak´e na z´ akladˇe vztah˚ u C11 = P + S − T + V, C12 = R + T (8.13) C21 = Q + S, C22 = P − Q + R + U, kde
P = (A11 + A22 ) (B11 + B22 ) , Q = (A21 + A22 ) B11 , R = A11 (B12 − B22 ) , S = A22 (B21 − B11 ) , T = (A11 + A12 ) B22 , U = (A21 − A11 ) (B11 + B12 ) , V = (A12 − A22 ) (B12 + B22 ) .
(8.14)
Vztahy (8.13 a 8.14) obsahuj´ı celkem 7 n´ asoben´ı a 18 sˇc´ıt´ an´ı. To znamen´ a, ˇze sloˇzitost algoritmu n´ asoben´ı matic, zaloˇzen´eho na tomto rozkladu, bude pro n = 2k d´ ana nerovnost´ı n n 2 S (n) ≤ 7S + 18 . 2 2 Jde opˇet o nerovnost tvaru (8.9), ve kter´e m´ ame a = 7, b = 18, c = 2. Dosazen´ım dostaneme vztah S (n) = O nlog 7 = O n2,81 , nebot’ log2 7 ≈ 2, 81.
Praktick´ y v´ yznam tento algoritmus ovˇsem pˇr´ıliˇs nem´ a. Numerick´e pokusy ukazuj´ı, ˇze vzhledem k implementaˇcn´ı sloˇzitosti v´ ypoˇctu zaloˇzen´eho na rozkladu (8.13 a 8.14) bude ahneme u ´spory ˇcasu aˇz pˇri cca n ≥ 50. Lze dos´ uk´ azat, ˇze minim´ aln´ı spotˇreba pamˇeti bude tak´e O n2,81 , takˇze pro n, pro kter´ y by mohla b´ yt v´ yznamn´ a u ´spora ˇcasu, bude jiˇz hr´ at roli ztr´ ata operaˇcn´ı pamˇeti.
8.4
V´ ypoˇ cet hodnoty polynomu
V´ ypoˇcet funkˇcn´ıch hodnot polynom˚ u patˇr´ı mezi bˇeˇzn´e operace. Pod´ıvejme se napˇr. na v´ ypoˇcet hodnoty polynomu p (x) = x5 + 7x4 − 12x2 + 9x − 25, (8.15)
v nˇejak´em bodˇe x. Ponechme stranou moˇznost vypoˇc´ıtat x5 , pak vypoˇc´ıtat a pˇriˇc´ıst 7x4 atd. To je nejsp´ıˇs nejhorˇs´ı mysliteln´ y postup, nebot’ pˇri nˇem zbyteˇcnˇe opakujeme v´ ypoˇcty mocnin x. Ponˇekud rozumnˇejˇs´ı je postupovat od niˇzˇs´ıch mocnin x a jednou vypoˇcten´e hodnoty si pamatovat. K v´ ypoˇctu x 4 vyuˇzijeme jiˇz vypoˇcten´e 2 hodnoty x atd. Jeˇstˇe v´ yhodnˇejˇs´ı je pouˇz´ıt Hornerova sch´ematu. Postup v´ ypoˇctu bude zˇrejm´ y, uz´ avorkujeme-li polynom (8.15) takto: p (x) = x (x (x (x (x + 7)) − 12) + 9) − 25, Podobn´ ym zp˚ usobem m˚ uˇzeme uz´ avorkovat libovoln´ y polynom dan´eho stupnˇe n. Odtud plyne postup v´ ypoˇctu. Hodnotu koeficientu u xn (nejvyˇsˇs´ı mocniny) vyn´ asob´ıme x, pˇriˇcteme koeficient u xn−1 , opˇet vyn´ asob´ıme x atd. Jsou-li koeficienty polynomu uloˇzeny v poli p o n + 1 prvc´ıch, var p: array [0..n] of real; m˚ uˇzeme zapsat v´ ypoˇcet podle Hornerova sch´ematu takto:
ˇ ´ DALSˇ´I ALGORITMY KAPITOLA 8. NEKTER E
136 s := p[n]; for i := n-1 downto 0 do s := s*x+p[i];
Pˇri tomto v´ ypoˇctu potˇrebujeme celkem n n´ asoben´ı a n − 1 sˇc´ıt´ an´ı. Hornerovo sch´ema pˇrestane b´ yt v´ yhodn´e, je-li n velk´e a polynom p obsahuje velmi m´ alo nenulov´ ych koeficient˚ u. Typick´ ym pˇr´ıkladem m˚ uˇze b´ yt p (x) = x55 . Uk´ aˇzeme, ˇze nen´ı potˇreba 54 n´ asoben´ı, ale podstatnˇe m´enˇe. Rozloˇz´ıme-li ˇc´ıslo 55 na souˇcet mocnin dvou, dostaneme x55 = x32 x16 x4 x2 x1 , odkud je zˇrejm´e, ˇze staˇc´ı spoˇc´ıtat pouze x32 (a pr˚ ubˇeˇznˇe si zapamatovat x2 , x4 atd.). Pˇritom k v´ ypoˇctu x32 55 staˇc´ı 5 n´ asoben´ı, takˇze k v´ ypoˇctu x potˇrebujeme celkem 9 n´ asoben´ı.
8.5
Diskr´ etn´ı Fourierova transformace
Mezi nejˇcastˇeji pouˇz´ıvan´e aritmetick´e algoritmy patˇr´ı bezesporu rychl´ a Fourierova transformace. Tento algoritmus, kter´ y byl publikov´ an v polovinˇe 60. let [33], znamenal zrychlen´ı klasick´eho v´ ypoˇcetn´ıho postupu o ˇra ´d.
´ Uvodn´ ıu ´vahy
8.5.1
Fourierova transformace je pro integrovatelnou funkci f (t) definov´ ana vztahem Z +∞ F (ω) = f (t) e2πiωt dt
(8.16)
−∞
a inverzn´ı transformace m´ a tvar f (t) =
1 2π
Z
+∞
F (ω) e−2πiωt dω.
(8.17)
−∞
Vzor, tedy funkce f , je komplexn´ı funkce jedn´e re´ aln´e promˇenn´e t. Tato promˇenn´ a se obvykle interpretuje jako ˇcas. Obraz, tedy funkce F (w), je opˇet komplexn´ı funkc´ı jedn´e re´ aln´e promˇenn´e, kter´ a se obvykle interpretuje jako frekvence. Fourierova transformace tedy pˇredstavuje pˇrevod ˇcasov´e z´ avislosti na z´ avislost frekvenˇcn´ı. Frekvenˇcn´ı anal´ yza tak´e pˇredstavuje jedno z nejˇcastˇejˇs´ıch pouˇzit´ı t´eto transformace v elektrotechnice. Vedle toho m˚ uˇze Fourierova transformace poslouˇzit v matematick´e fyzice jako u ´ˇcinn´ y n´ astroj pˇri ˇreˇsen´ı nˇekter´ ych u ´loh pro obyˇcejn´e i parci´ aln´ı diferenci´ aln´ı rovnice. V praxi se ovˇsem m´ısto funkc´ı, definovan´ ych na cel´e mnoˇzinˇe re´ aln´ ych ˇc´ısel, setk´ av´ ame s ˇcasto s koneˇcn´ ymi mnoˇzinami diskr´etn´ıch vzork˚ u, tedy s koneˇcn´ ymi posloupnostmi (obecnˇe komplexn´ıch) ˇc´ısel. Pro nˇe zav´ ad´ıme diskr´etn´ı Fourierovu transformaci takto: Diskr´etn´ım Fourierov´ ym obrazem posloupnosti a = {a0 , a1 , . . . , an−1 } je posloupnost A = {A0 , A1 , . . . , An−1 }, jej´ıˇz prvky jsou d´ any vztahy n−1 X 2πijt (8.18) Aj = at e( n ) , j = 0, . . . , n − 1; t=0
inverzn´ı transformace je d´ ana vztahy 2 D˚ ukaz
2
vzorce pro zpˇetnou transformaci: Dosad´ıme za Aj do v´ yrazu na prav´e stranˇe (15b). Dostaneme
q=
1 n
n−1
As exp s=0
2πisk n
=
1 n
n−1 n−1
at exp s=0 t=0
=
1 n
2πist n
n−1
n−1
exp
at t=0
exp
s=0
2πisk n
=
2πis (t − k) n
1 n
n−1 n−1
at exp s=0 t=0
.
Snadno zjist´ıme, ˇze pro t = k bude vnitˇrn´ı souˇcet roven n. Pro t k dostaneme 2πin(t−k) n−1 exp −1 2πis (t − k) n exp = =0 n exp 2πi(t−k) −1 s=0 n
Odtud jiˇz plyne q = ak .
2πis (t − k) n
=
´ ´I FOURIEROVA TRANSFORMACE 8.5. DISKRETN
ak =
137
n−1 2πisk 1X As e(− n ) , k = 0, . . . , n − 1. n s=0
(8.19)
Oznaˇc´ıme-li w = e(
2πi n
),
pˇredstavuj´ı vztahy (8.18) v´ ypoˇcet hodnot polynomu a (x) =
n−1 X
a j xj
j=0
v bodech x = wj a vztahy (8.19) v´ ypoˇcet hodnot polynomu n−1 1X A (x) = A j xj n j=0
v bodˇe x = w−k . K v´ ypoˇctu pˇr´ım´e i inverzn´ı transformace bychom mohli pouˇz´ıt napˇr. Hornerova sch´ematu, se kter´ ym jsme se sezn´ amili v pˇredchoz´ım odd´ılu. Vzhledem k tomu, ˇze v´ ypoˇcet opakujeme pro n r˚ uzn´ ych hodnot a vyˇc´ıslen´ı polynomu a (x) resp. A (x)) t´ ımto zp˚ u sobem vyˇ z aduje O (n) krok˚ u, vyˇ z aduje v´ y sledn´ y algoritmus celkem O n2 krok˚ u.
Ukazuje se vˇsak, ˇze lze vyuˇz´ıt zvl´ aˇstn´ıch vlastnost´ı bod˚ u w j , ve kter´ ych polynomy a (x) resp. A (x) poˇc´ıt´ ame. ˇ C´ıslo w pˇredstavuje hlavn´ı hodnotu n-t´e odmocniny z 1, w n = 1. Pˇrid´ ame-li nav´ıc poˇzadavek, aby pro poˇcet n hodnot v posloupnosti a = {a0 , a1 , . . . , an−1 } platilo n = 2k , m˚ uˇzeme odvodit algoritmus, kter´ y bude vyˇzadovat pouze O (n log2 n) operac´ı.
8.5.2
Rychl´ a Fourierova transformace
Jak jsme jiˇz naznaˇcili, budeme v tomto odd´ılu pˇredpokl´ adat, ˇze je d´ ana koneˇcn´ a posloupnost komplexn´ıch ˇc´ısel a = {a0 , a1 , . . . , an−1 }, kde n = 2k . Symbolem w zde oznaˇcujeme hlavn´ı hodnotu n-t´e odmocniny z 1, tedy ˇc´ıslo 2πi 2π 2π ( ) n w=e + i sin (8.20) = cos n n Z toho, ˇze wn = 1, plyne, ˇze w k = w(k mod n) . To znamen´ a, pro ˇze jak pro pˇr´ımou tak i pro inverzn´ı transformaci n vystaˇc´ıme se znalost´ı w 0 , w1 , w2 , . . . , wn−1 . (Odtud tak´e plyne, ˇze wk = 1, takˇze i ˇc´ıslo w k pˇredstavuje pro libovoln´e k ∈ Z - odmocninu z 1.) Zaˇcneme u pˇr´ım´e transformace. V pˇredchoz´ım odstavci jsme si uk´ azali, ˇze w k Aj = a wj , kde a (x) je polynom s koeficienty dan´ ymi posloupnost´ı a. Pˇri v´ ypoˇctu hodnoty tohoto polynomu se pokus´ıme pouˇz´ıt metodu rozdˇel a panuj. Koeficienty polynomu a rozdˇel´ıme na dvˇe skupiny - na sud´e a lich´e, kter´e oznaˇc´ıme a s a al . T´ım rozloˇz´ıme polynom a na souˇcet dvou polynom˚ u, a (x) = as x2 + xal x2 . (8.21) ´ Ulohu vyˇc´ıslit polynom a stupnˇe n − 1 v bodˇe x jsme tedy pˇrevedli na u ´lohu vyˇc´ıslit dva polynomy a s a al stupnˇe n/2 − 1 v bodˇe x2 .
Jak v´ıme, potˇrebujeme pˇri v´ ypoˇctu hodnoty Ak vyˇc´ıslit (8.21) v bodˇe x = w k , kde w je hlavn´ı hodnota n-t´e odmocniny z 1. Z (8.20) ale plyne, ˇze w 2 je hlavn´ı hodnota (n/2)-t´e odmocniny z 1 - a to je pˇresnˇe to, co k u ´spˇeˇsn´emu pouˇz´ıt´ı metody rozdˇel a panuj potˇrebujeme. Vztah (8.21) totiˇz ˇr´ık´ a, ˇze m´ısto abychom poˇc´ıtali hodnotu polynomu a v n-t´e odmocninˇe z jedn´e, m˚ uˇzeme poˇc´ıtat hodnoty polynom˚ u a s a al v (n/2)-t´e odmocninˇe z 1. Protoˇze plat´ı n = 2k , m˚ uˇzeme pouˇz´ıt t´ yˇz postup i na polynomy as a al . Rekurzivnˇe tak dospˇejeme aˇz k polynom˚ um 1. stupnˇe, kter´e budeme vyˇc´ıslovat v bodech 1 a -1. Pˇri v´ ypoˇctu m˚ uˇzeme nav´ıc vyuˇz´ıt skuteˇcnosti, ˇze w(n/2) = −1, takˇze w i = −w(n/2)+i .
ˇ ´ DALSˇ´I ALGORITMY KAPITOLA 8. NEKTER E
138
Pˇ r´ıklad 8.2 Pod´ıvejme se na polynom 8. stupnˇe,
Je-li nyn´ı
p (x) = p0 + p1 x + p2 x2 + p3 x3 + p4 x4 + p5 x5 + p6 x6 + p7 x7 = = p 0 + p 2 x2 + p 4 x4 + p 6 x6 + x p 1 + p 3 x2 + p 5 x4 + p 7 x6 = = ps x2 + xpl x2 . 2πi 8
w40 + w80 pl w42 + w82 pl w40 + w80 pl w42 + w82 pl
w40 , w42 , w40 , w42 ,
dostaneme p w80 = ps p w82 = ps p w84 = ps p w86 = ps
) = cos π + i sin π , 4 4
w = w 8 = e(
p w81 = ps p w83 = ps p w85 = ps p w87 = ps
w41 + w81 pl w43 + w83 pl w41 + w81 pl w43 + w83 pl
w41 , w43 , w41 , w43 .
Pˇri v´ ypoˇctu uloˇz´ıme transformovanou posloupnost do pole p a d´elce n. V tomto poli tak´e dostaneme v´ ysledek. Algoritmus pˇr´ım´e transformace, tedy vyˇc´ıslen´ı polynomu s koeficienty a 0 , a1 , . . . , an−1 , m˚ uˇzeme popsat n´ asleduj´ıc´ımi kroky: 1. Je-li stupeˇ n polynomu n = 1, jsou hledan´e hodnoty rovny A0 = a0 + a1 a A1 = a0 − a1 a skonˇc´ıme. 2. Je-li stupeˇ n polynomu n > 1, pˇrerovn´ ame pole a v poˇrad´ı a0 , a2 , . . . , an−2 , a1 , a3 , . . . , an−1 . 3. Pouˇzijeme tento algoritmus rekurzivnˇe zvl´ aˇst’ pro prvn´ı polovinu pole a, se sud´ ymi koeficienty a zvl´ aˇst’ pro druhou polovinu pole s lich´ ymi koeficienty. 4. Pole, z´ıskan´e ve 3. kroku, obsahuje obrazy polynom˚ u as a al . Pˇrerovn´ ame je do p˚ uvodn´ıho poˇrad´ı a sloˇz´ıme z nˇej podle (8.21) obraz p˚ uvodn´ıho pole. Program, kter´ y popsan´ y algoritmus implementuje, nap´ıˇseme v C++, nebot’ tento jazyk jako jeden z m´ ala nab´ız´ı program´ ator˚ um jak rekurzi tak i typ complex pro pr´ aci s komplexn´ımi ˇc´ısly. N´ asleduj´ıc´ı procedura vypoˇcti bude pˇri pˇrerovn´ av´ an´ı pouˇz´ıvat pomocn´e pole t. Deklarujeme je jako glob´ aln´ı, stejnˇe jako nˇekter´e dalˇs´ı pomocn´e promˇenn´e. Dalˇs´ı pomocn´e pole w bude obsahovat hodnoty w 0 , w1 , w2 , . . . , wn−1 (mocniny n-t´e odmocniny z 1, viz (8.20)). Prvn´ım vstupn´ım parametrem procedury vypoˇcti je pole a, kter´e obsahuje koeficienty transformovan´eho polynomu (tedy transformovanou posloupnost). Druh´ y parametr N obsahuje stupeˇ n transformovan´eho polynomu. Pˇredpokl´ ad´ ame, ˇze pro hodnotu parametru N plat´ı N = 2r − 1 pro nˇejak´e r. (To znamen´ a, ˇze pole a obsahuje N + 1 prvk˚ u.) tˇret´ım parametrem je index k, od kter´eho se m´ ame polem a zab´ yvat. #include #define M 8 #define PI 3.14159265358979
// Velikost pole - napˇ r. 8...
complex w[M], t[M]; // Pomocn´ a pole complex p0, p1; // Pomocn´ e promˇ enn´ e complex q[M]; // Transformovan´ e pole void vypoˇ cti(complex a[ ], int N, int k){ int i, j; if(N==1) { // Je-li stupeˇ n polynomu N == 1, p0=a[k]; p1=a[k+1]; // vypoˇ cteme hodnotu a[k]=p0+p1; a[k+1]=p0-p1; } else { // Jinak pole nejprve pˇ rerovn´ ame for(i=0; i <= N/2; i++) { // (s pomoc´ ı pole t)
´ ´I FOURIEROVA TRANSFORMACE 8.5. DISKRETN
139
j = k+2*i; t[i] = a[j]; t[i+1+N/2] = a[j+1]; } // a pak zavol´ ame tuto proceduru for(i = 0; i <= N; i++) a[k+i] = t[i]; // rekurzivnˇ e vypoˇ cti(a, N/2, k); // zvl´ as ˇt’ pro prvn´ ı polovinu vypoˇ cti(a, N/2, k+1+N/2); // a zvl´ as ˇt’ pro druhou polovinu j = M/(N+1); for(i=0; i <= N/2; i++) { // Pak pole pˇ rerovn´ ame zpˇ et p0 = w[i*j]*a[k+(N/2)+1+i]; // a sloˇ zı ´me z nˇ ej t[i] = a[k+i] + p0; // obraz p˚ uvodn´ ıho pole t[i+(N/2)+1] = a[k+i] - p0; } for(i=0; i <= N; i++) a[k+i] = t[i]; } } Pˇred prvn´ım vol´ an´ım procedury vypoˇcti mus´ıme vypoˇc´ıtat hodnoty w j , j = 0, 1, . . . , n − 1. Procedura pro pˇr´ımou transformaci proto m˚ uˇze m´ıt n´ asleduj´ıc´ı tvar: void FFTR(complex a[ ], int N) { static jeW = 0; // Indikuje, zda jiˇ z bylo w vypoˇ cteno if(!jeW){ jeW++; w[0]=1; w[1] = complex(cos(2*PI/N), sin(2*PI/N)); for(int i = 2; i < N; i++) w[i] = w[i-1]*w[1]; } vypoˇ cti(a,N-1,0); } Inverzn´ı transformace spoˇc´ıv´ a podle (8.19) ve vyˇc´ıslen´ı polynomu s koeficienty A k v bodech w−j , kde j = 0, 1, . . . , n − 1 a vydˇelen´ı v´ ysledku ˇc´ıslem n. Snadno se ale pˇresvˇedˇc´ıme, ˇze pro n-tou odmocninu z 1 plat´ı w−0 = w0 = 1, w−1 = wn−1 , . . . , w−(n−1) = w−1 . To znamen´ a, ˇze staˇc´ı pouˇz´ıt opˇet funkci vypoˇcti a v´ ysledek pˇrerovnat. Funkce pro inverzn´ı transformaci proto bude m´ıt tento tvar: void IFFTR(complex a[ ], int N) { vypoˇ cti(p, N-1, 0); for(int i = 1; i <= N/2; i++) { complex t = a[i]; a[i] = a[N-i]; a[N-i]= t; } for(i=0; i < N; i++) a[i] /=N; }
// Pˇ rerovn´ an´ ı
Pole q, deklarovan´e pˇred funkc´ı vypoˇcti, transformujeme pˇr´ıkazem FFTR(q,M); a zpˇetnou transformaci obstar´ a pˇr´ıkaz IFFTR(q,M);
8.5.3
Sloˇ zitost rychl´ e Fourierovy transformace
Nejprve se vr´ at´ıme k pˇr´ıkladu transformace pole o 8 prvc´ıch, tedy polynomu 7. stupnˇe. Pˇ r´ıklad 8.2 (pokraˇ cov´ an´ı) Pˇri v´ ypoˇctu A0 pˇrevedeme v´ ypoˇcet hodnoty polynomu 7. stupnˇe na v´ ypoˇcet hodnot dvou polynom˚ u 3. stupnˇe. Pouˇzijeme k tomu proceduru vypoˇcti - zavol´ ame ji s parametry a, N = 7 a k = 0 (bereme pole a od nult´eho
ˇ ´ DALSˇ´I ALGORITMY KAPITOLA 8. NEKTER E
140
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7]
a[0] a[2] a[4] a[6] a[1] a[3] a[5] a[7] Obr. 8.2: Pˇresun prvk˚ u pˇri rychl´e Fourierovˇe transformaci pro n = 8
(7,0)
(3,0)
(1,0)
(1,2)
(3,4)
(1,4)
(1,6)
Obr. 8.3: Druh´ y a tˇret´ı parametr rekurzivn´ıch vol´ an´ı procedury vycisli prvku). Tato procedura nejprve pˇrerovn´ a pole a tak, aby v prvc´ıch a 0 , . . . , a3 leˇzely koeficienty u sud´ ych mocnin a v a4 , . . . , a7 koeficienty u lich´ ych mocnin p˚ uvodn´ıho polynomu (viz obr. 8.1). Pak zavol´ ame proceduru vypoˇcti rekurzivnˇe pro vyˇc´ıslen´ı polynom˚ u a s a al . Pˇri vyˇc´ıslov´ an´ı as bude m´ıt vol´ an´ı procedury vypoˇcti parametry a, N = 3 (stupeˇ n polynomu) a k = 0 (koeficienty jsou uloˇzeny v poli a od nult´eho prvku). Pˇri vyˇc´ıslov´ an´ı al bude m´ıt vol´ an´ı procedury vypoˇcti parametry a, 3 (stupeˇ n polynomu, stejn´ y jako u as ) a 4 (koeficienty al jsou uloˇzeny v poli a od prvku a [4]). Vyˇc´ıslov´ an´ı as a al bude znamenat opˇet dvˇe rekurzivn´ı vol´ an´ı procedury vycisli. Posloupnost rekurzivn´ıch vol´ an´ı m˚ uˇzeme vyj´ adˇrit stromem, kter´ y vid´ıte na obr. 8.3 Je zˇrejm´e, ˇze hloubka rekurze bude rovna log2 n, v pˇr´ıpadˇe n = 8 tedy 3. Sloˇzitost rekurzivn´ıho algoritmu rychl´e Fourierovy transformace odvod´ıme takto: Je-li T (n) poˇcet operac´ı, potˇrebn´ y pˇri dan´em n, mus´ı platit T (n) = 2T (n/2) + cn, kde c je konstanta takov´ a, ˇze cn je ˇcas potˇrebn´ y na obˇe pˇrerovn´ an´ı pole o n prvc´ıch. Protoˇze T (1) = d je tak´e konstanta, dostaneme T (2m ) = 2T 2m−1 + c2m = 4T 2m−2 + 2c2m = · · · cm2m + T (1) 2m = cn log2 n + dn, odkud plyne sloˇzitost O (n log2 n).
Kapitola 9
Softwarov´ y projekt V t´eto kapitole si kr´ atce pov´ıme o hlavn´ıch z´ asad´ ach konstrukce software. Zkuˇsenosti ukazuj´ı, ˇze pr´ aci na velk´ ych softwarov´ ych projektech je v´ yhodn´e maxim´ alnˇe formalizovat 1. C´ılem takov´e formalizace je samozˇrejmˇe vyhnout se nejen chyb´ am, ale i nepˇresnostem ve formulaci projektu a nedorozumˇen´ım v kontaktu se z´ akazn´ıkem. Studie, prov´ adˇen´e napˇr. ministerstvem obrany USA, ukazuj´ı, ˇze pomˇer dodateˇcn´ ych n´ aklad˚ u, vyvolan´ ych zmˇenou v poˇca ´teˇcn´ı f´ azi a n´ aklad˚ u, vyvolan´ ych zmˇenou v koncov´ ych f´ az´ıch projektu, m˚ uˇze b´ yt i vyˇsˇs´ı neˇz 1:200. Tabulku, kter´ a ukazuje relativn´ı n´ aklady na opravu chyby v z´ avislosti na tom, kdy chyba vznikne a kdy my ji odhal´ıme, jsme pˇrevzali ze [7].
Okamˇzik odhalen´ı Anal´ yza N´ avrh Pasivn´ı testy Struktur´ arn´ı testy Funkcion´ aln´ı testy
Okamˇzik vzniku Poˇzadavky N´ avrh Psan´ı programu 1 2 1 5 2 1 15 5 2 25 10 5
Tabulka 9.1 V´ yˇse relativn´ıch n´ aklad˚ u zp˚ usoben´ ych chybou v z´ avislosti na tom, kdy chyba vznikne a kdy ji odhal´ıme. V n´ asleduj´ıc´ım odd´ılu si pov´ıme o jednotliv´ ych f´ az´ıch v´ yvoje softwarov´eho produktu.
9.1
ˇ Zivotn´ ı cyklus softwarov´ eho produktu
ˇ Zivotn´ ı cyklus softwarov´eho produktu m˚ uˇzeme z hlediska v´ yvoj´ aˇre rozdˇelit do n´ asleduj´ıc´ıch f´ az´ı: 1. 3. 5. 7.
Definice probl´emu. N´ avrh architektury Programov´ an´ı a ladˇen´ı Testov´ an´ı syst´emu
2. 4. 6. 8.
Anal´ yza poˇzadavk˚ u Podrobn´ y n´ avrh Testov´ an´ı modul˚ u ´ zba Udrˇ
Na nˇekter´e z nich se nyn´ı pod´ıv´ ame podrobnˇeji.
9.2
Definice probl´ emu
Na poˇca ´tku je nezbytn´e definovat probl´em, kter´ y m´ a budouc´ı softwarov´ y produkt ˇreˇsit. Popis probl´emu m´ a pouˇz´ıvat jazyk (terminologii) uˇzivatele a m´ a vych´ azet z jeho pohledu. Rozsah specifikace by nemˇel pˇrekroˇcit jednu aˇz dvˇe str´ anky. 1 Ve
smyslu u ´ˇrednick´em, nikoli matematick´em. Jde o to dokumentovat jak poˇzadavky z´ akazn´ıka tak i postup projektu.
141
´ PROJEKT KAPITOLA 9. SOFTWAROVY
142
V popisu probl´emu nen´ı vhodn´e naznaˇcovat moˇzn´ a ˇreˇsen´ı, nebot’ jejich hled´ an´ı zpravidla nen´ı probl´em z´ akazn´ıka, n´ ybrˇz ˇreˇsitele. Naznaˇcujeme-li ˇreˇsen´ı jiˇz ve formulaci probl´emu, m˚ uˇzeme ˇreˇsitele zav´est na scest´ı; rozhodnut´ı o zp˚ usobu ˇreˇsen´ı by mˇelo b´ yt aˇz v´ ysledkem anal´ yzy probl´emu. Napˇr´ıklad tvrzen´ı M´ ame probl´emy s evidenc´ı materi´ alu ve skladu“ nejen zn´ı jako probl´em, m˚ uˇzeme je i ” povaˇzovat za rozumnou definici probl´emu. Na druh´e stranˇe vˇeta Potˇrebujeme optimalizovat automatick´e zad´ av´ an´ı dat pˇri evidenci materi´ alu ve skladu“ ” uˇz nezn´ı jako probl´em - zde je jiˇz vnucov´ ano ˇreˇsen´ı. Takov´ ato formulace m˚ uˇze m´ıt smysl aˇz ve specifikaci pˇredbˇeˇzn´ ych poˇzadavk˚ u. Pˇritom je dobr´e uvˇedomovat si, ˇze nejlepˇs´ı ˇreˇsen´ı nemus´ı b´ yt vˇzdy nov´ y program nebo u ´prava programu, kter´ y uˇz existuje. Jestliˇze napˇr. pouˇz´ıv´ ame program, kter´ y um´ı udˇelat p˚ ulroˇcn´ı bilanci hospodaˇren´ı, a potˇrebujeme roˇcn´ı bilanci, m˚ uˇze b´ yt nejlacinˇejˇs´ım a nejrychlejˇs´ım ˇreˇsen´ım ruˇcnˇe seˇc´ıst jednou do roka deset ˇc´ısel.
9.3
Pˇ redbˇ eˇ zn´ e poˇ zadavky
Formulace pˇredbˇeˇzn´ ych poˇzadavk˚ u pˇredstavuje prvn´ı krok k ˇreˇsen´ı probl´emu. Zde vlastnˇe sestav´ıme podrobn´ y popis toho, co se bude od softwarov´eho produktu oˇcek´ avat. Ze zkuˇsenost´ı softwarov´ ych firem plyne, ˇze je vhodn´e sestavit se z´ akazn´ıkem p´ısemn´ y seznam pˇredbˇeˇzn´ ych poˇzadavk˚ u. T´ım mimo jin´e dos´ ahneme toho, ˇze o funkci programu bude rozhodovat z´ akazn´ık, nikoli program´ ator. Form´ aln´ı seznam poˇzadavk˚ u umoˇzn´ı sn´ aze se vyhnout chyb´ am a nedorozumˇen´ım. Jestliˇze pˇri psan´ı programu pˇrijdeme na chybu v k´ odu, staˇc´ı zpravidla pˇrepsat p´ ar ˇra ´dek. Pˇrijdeme-li pˇri psan´ı programu na chybu v poˇzadavc´ıch, mus´ıme se k nim vr´ atit a mus´ıme znovu proj´ıt n´ avrh architektury, podrobn´ y n´ avrh a moˇzn´ a zaˇc´ıt ps´ at program u ´plnˇe od zaˇca ´tku. Adekv´ atn´ı specifikace poˇzadavk˚ u je proto kl´ıˇcov´ ym momentem u ´spˇechu projektu. ˇ Na druh´e stranˇe je pˇredem jasn´e, ˇze poˇzadavky ze strany z´ akazn´ıka se budou pr˚ ubˇeˇznˇe mˇenit. Casto totiˇz teprve samotn´ y proces v´ yvoje produktu z´ akazn´ıkovi umoˇzn´ı, aby si plnˇe uvˇedomil, co vlastnˇe potˇrebuje - a podle toho bude sv´e poˇzadavky mˇenit. (Form´ alnˇe sepsan´e poˇzadavky n´ am pak umoˇzn´ı na´ uˇctovat mu to...) Studie IBM ukazuj´ı, ˇze u typick´eho projektu se v pr˚ ubˇehu v´ yvoje zmˇen´ı poˇzadavky pˇribliˇznˇe z 25% [7]. Proto je d˚ uleˇzit´e, aby z´ akazn´ık znal cenu zmˇen a abychom si s n´ım dohodli form´ aln´ı postup pˇri zmˇen´ ach z jeho strany.
9.3.1
Kontrola seznamu poˇ zadavk˚ u
Pˇri sestavov´ an´ı seznamu pˇredbˇeˇzn´ ych poˇzadavk˚ u by se mohlo st´ at, ˇze opomeneme nˇekterou d˚ uleˇzitou oblast. N´ asleduj´ıc´ı seznam kontroln´ıch ot´ azek je sestaven na z´ akladˇe praktick´ ych zkuˇsenost´ı (podle [7]) a m´ a n´ am pomoci vyhnout se nedopatˇren´ım. Samozˇrejmˇe ne vˇsechny tyto ot´ azky mus´ı m´ıt v souvislosti s konkr´etn´ım probl´emem smysl. N´ asleduj´ıc´ı ot´ azky lze pouˇz´ıt tak´e jako vod´ıtko pˇri sestavov´ an´ı seznamu poˇzadavk˚ u. Rozdˇel´ıme si je do nˇekolika okruh˚ u. Obsah poˇ zadavk˚ u •
Specifikovali jsme vˇsechny vstupn´ı datov´e proudy syst´emu? Zn´ ame jejich zdroje? Zn´ ame pˇresnost vstupn´ıch dat? Zn´ ame rozsahy hodnot? Zn´ ame frekvence vstup˚ u?
•
Specifikovali jsme vˇsechny v´ ystupn´ı datov´e proudy? Zn´ ame jejich pˇr´ıjemce, rozsah hodnot dat a jejich pˇresnost, frekvenci? Zn´ ame jejich form´ at?
•
Specifikovali jsme form´ aty vˇsech sestav?
•
Zn´ ame specifikace vˇsech vnˇejˇs´ıch hardwarov´ ych i softwarov´ ych rozhran´ı pˇripravovan´eho produktu?
ˇ ˇ ZN ˇ E ´ POZADAVKY ˇ 9.3. PREDB E •
143
Zn´ ame potˇrebn´ a komunikaˇcn´ı rozhran´ı, jako jsou komunikaˇcn´ı protokoly, pˇrenosov´e rychlosti, zp˚ usoby kontroly chyb apod.?
•
Jsou z hlediska uˇzivatele specifikov´ any potˇrebn´e doby odezvy pro vˇsechny nezbytn´e operace?
•
Zn´ ame pˇr´ıpadn´ a dalˇs´ı ˇcasov´ a omezen´ı, jako je doba zpracov´ an´ı, doba pˇrenosu dat apod.?
•
Specifikovali jsme vˇsechny u ´lohy, kter´e m´ a syst´em podle pˇredstav uˇzivatele prov´ adˇet?
•
Specifikovali jsme data, kter´ a se budou pouˇz´ıvat v jednotliv´ ych u ´loh´ ach a kter´ a budou v´ ysledkem jednotliv´ ych u ´loh?
•
Specifikovali jsme poˇzadavky na u ´roveˇ n utajen´ı? Specifikovali jsme u ´roveˇ n zabezpeˇcen´ı dat pˇred ztr´ atou nebo zniˇcen´ım (m˚ uˇze j´ıt o zabezpeˇcen´ı pˇred sabot´ aˇz´ı, pˇred poˇc´ıtaˇcov´ ymi viry, pˇred nechtˇen´ ym smaz´ an´ım, pˇred p´ adem meteoritu na poˇc´ıtaˇc...)?
•
Specifikovali jsme poˇzadavky na u ´roveˇ n spolehlivosti - vˇcetnˇe moˇzn´ ych d˚ usledk˚ u selh´ an´ı programu? Specifikovali jsme, kter´e informace jsou ˇzivotnˇe d˚ uleˇzit´e a kter´e je nezbytnˇe tˇreba chr´ anit? Specifikovali jsme strategii detekce chyb a zotavov´ an´ı se z nich?
•
Specifikovali jsme maxim´ aln´ı poˇzadavky na pamˇet’ RAM?
•
Specifikovali jsme maxim´ aln´ı poˇzadavky na vnˇejˇs´ı pamˇet’ov´ a m´edia (disky, magnetick´e p´ asky apod.)?
•
Vyjasnili jsme poˇzadavky na u ´drˇzbu a udrˇzovatelnost syst´emu? Zn´ ame poˇzadavky na moˇznost pˇrenosu programov´eho produktu do jin´eho operaˇcn´ıho prostˇred´ı, pod jin´ y operaˇcn´ı syst´em? Zn´ ame poˇzadavky na komunikaci s jin´ ymi softwarov´ ymi produkty (napˇr. OLE / DDE pod MS Windows)? Jak je to s pˇr´ıpadn´ ymi zmˇenami pˇresnosti vstup˚ u/v´ ystup˚ u? Co v pˇr´ıpadˇe poˇzadavk˚ u na zmˇenu v´ ykonnosti?
•
Jak´e jsou vztahy mezi vlastnostmi, kter´e si mohou navz´ ajem konkurovat nebo se i vyluˇcovat? Dostane napˇr. pˇrednost rychlost pˇred pˇresnost´ı, velikost programu pˇred robustnost´ı?
•
Zn´ ame pˇresnou specifikaci u ´spˇeˇsn´eho zpracov´ an´ı nebo chyby?
´ Uplnost poˇ zadavk˚ u •
Pokud nezn´ ame potˇrebn´e informace pˇred zaˇca ´tkem zpracov´ an´ı, zn´ ame alespoˇ n rozsah jejich ne´ uplnosti?
•
Jsou poˇzadavky u ´pln´e v tom smyslu, ˇze pokud bude produkt vyhovovat opravdu vˇsem uveden´ ym poˇzadavk˚ um, bude pro z´ akazn´ıka pˇrijateln´ y?
•
P˚ usob´ı nˇekter´e z poˇzadavk˚ u pot´ıˇze? Jev´ı se nˇekter´e z nich jako neimplementovateln´e a zaˇradili jste je jen proto, abyste uspokojili z´ akazn´ıka (pˇr´ıpadnˇe ˇs´efa)?
Kvalita poˇ zadavk˚ u • • •
Jsou poˇzadavky seps´ any v jazyku uˇzivatele? Mysl´ı si to uˇzivatel? Rozum´ı jim? Jsou poˇzadavky bezkonfliktn´ı? Nebo jsme se alespoˇ n pˇri jejich specifikaci vyhnuli konflikt˚ um v maxim´ aln´ı moˇzn´e m´ıˇre? Vyhnuli jsme se tomu, aby poˇzadavky urˇcovaly n´ avrh architektury?
•
Jsou vˇsechny poˇzadavky na pˇribliˇznˇe stejn´e u ´rovni podrobnosti? Nen´ı tˇreba specifikovat nˇekter´e z poˇzadavk˚ u podrobnˇeji? Nebo jsou naopak pˇr´ıliˇs podrobn´e?
•
Jsou poˇzadavky formulov´ any natolik jasnˇe, aby je ˇslo implementovat? Porozum´ı jim i ˇclovˇek, kter´ yo nich se z´ akazn´ıkem nejednal?
•
Jsou jednotliv´e poˇzadavky v rozumn´em vztahu k probl´emu a k jeho ˇreˇsen´ı? Je jasn´ y p˚ uvod jednotliv´ ych poˇzadavk˚ u v kontextu v´ ychoz´ıho probl´emu?
•
Lze testovat splnˇen´ı jednotliv´ ych poˇzadavk˚ u? M˚ uˇze o splnˇen´ı jednotliv´ ych poˇzadavk˚ u rozhodnout nez´ avisle zkonstruovan´ y a proveden´ y test?
•
Specifikovali jsme u jednotliv´ ych poˇzadavk˚ u vˇsechny moˇzn´e zmˇeny (pˇr´ıpadnˇe vˇcetnˇe pravdˇepodobnosti takov´e zmˇeny)?
´ PROJEKT KAPITOLA 9. SOFTWAROVY
144
9.4
N´ avrh architektury
Po vyjasnˇen´ı poˇzadavk˚ u pˇrijde na ˇradu n´ avrh architektury; nˇekdy tak´e hovoˇr´ıme o strategii implementace. Jde o n´ avrh na nejvyˇsˇs´ı u ´rovni“, ve kter´e se budeme zab´ yvat pˇredevˇs´ım tˇemito aspekty budouc´ıho produktu: ” Organizace programu Nejprve stanov´ıme organizaci programu. To znamen´ a, ˇze vypracujeme pˇrehled, kter´ y pop´ıˇse programov´ y produkt pomoc´ı obecn´ ych pojm˚ u. V nˇem urˇc´ıme hrub´ y smysl jednotliv´ ych modul˚ u (hlavn´ıch ˇca ´st´ı programu) a jejich rozhran´ı. Pˇritom je vhodn´e jiˇz v t´eto f´ azi v´ yvoje db´ at na to, aby na sobˇe jednotliv´e moduly byly co nejm´enˇe z´ avisl´e, tj. aby jejich rozhran´ı byla pokud moˇzno nejuˇzˇs´ı. Zach´ azen´ı se zmˇ enami Stanov´ıme strategii zach´ azen´ı se zmˇenami v projektu - m˚ uˇze se jednat o ˇc´ısla verz´ı, o n´ avrhy z´ aloˇzn´ıch datov´ ych pol´ı pro dalˇs´ı pouˇzit´ı, o soubory pro pˇrid´ av´ an´ı dalˇs´ıch tabulek apod. Je vhodn´e tak´e je urˇcit zp˚ usob, jak´ ym se bude v projektu zach´ azet s rozhodov´ an´ım. M´ısto pouˇzit´ı tvrdˇe“ ” implementovan´ ych rozhodnut´ı m˚ uˇzeme pouˇz´ıt rozhodov´ an´ı podle tabulek; data, podle kter´ ych se rozhodujeme, mohou b´ yt uloˇzena v extern´ıch souborech atd. Vyv´ıjet ˇ ci koupit Jiˇz v t´eto f´ azi se tak´e mus´ıme rozhodnout, zda budeme program sami vyv´ıjet ˇci zda jej koup´ıme, pˇr´ıpadnˇe kter´e ˇca ´sti koup´ıme a kter´e budeme sami vyv´ıjet. Podobnˇe se mus´ıme rozhodnout, kter´e ˇca ´sti implementujeme sami a kter´e pˇrevezmeme z operaˇcn´ıho prostˇred´ı (napˇr´ıklad zda pouˇzijeme grafickou knihovnu Borland C++ nebo zda si nap´ıˇseme svoji, zda pouˇzijeme standardn´ıch sluˇzeb operaˇcn´ıho syst´emu nebo zda je obejdeme, protoˇze nejsou u ´ˇcinn´e nebo dostateˇcnˇe bezpeˇcn´e, zda vyuˇzijeme standardn´ıch ovladaˇc˚ u zaˇr´ızen´ı ˇci zda vyvineme vlastn´ı atd.). Pokud se rozhodneme pouˇz´ıt koupen´ y software, mus´ıme si ujasnit, jak vyhovuje ostatn´ım poˇzadavk˚ um. Hlavn´ı datov´ e struktury V dalˇs´ım kroku specifikujeme hlavn´ı datov´e struktury. Pˇritom mus´ıme urˇcit pˇredevˇs´ım jejich obsah a zp˚ usob zobrazen´ı. Doporuˇcuje se dokumentovat alternativy, kter´e jsme pˇri v´ ybˇeru zvaˇzovali, a d˚ uvody, proˇc jsme se nakonec rozhodli urˇcit´ ym zp˚ usobem. Pˇri n´ avrhu hlavn´ıch datov´ ych struktur bychom mˇeli vych´ azet z poˇzadavku skr´ yv´ an´ı dat - to znamen´ a pokud moˇzno se vyh´ ybat glob´ aln´ım dat˚ um. K dat˚ um by mˇel m´ıt pˇr´ıstup vˇzdy pouze jeden modul; pokud je potˇrebuj´ı i jin´e moduly, mus´ı pouˇz´ıt pˇr´ıstupov´ ych procedur. ´ Pˇri n´ avrhu architektury modul˚ u se uplatˇ nuje z´ akon zbyteˇcn´ ych dat“: Udaje, kter´e vstoup´ı do programu (mod” ulu, funkce ...) a neovlivn´ı jeho v´ystup nebo chov´ an´ı, jsou zbyteˇcn´e. V objektovˇe orientovan´em n´ avrhu mus´ıme stanovit hlavn´ı tˇr´ıdy objekt˚ u, jejich vz´ ajemn´e vztahy (dˇedick´e hierarchie), hlavn´ı instance, jejich stavy, funkci a tak´e doby trv´ an´ı, tj. rozmez´ı jejich existence v programu. Hlavn´ı algoritmy Souˇcasnˇe s n´ avrhem hlavn´ıch datov´ ych struktur pˇrijdou na ˇradu n´ avrhy hlavn´ıch algoritm˚ u (odkazy na nˇe). Tak´e zde je tˇreba dokumentovat moˇzn´e alternativy a d˚ uvody sv´ ych rozhodnut´ı. Vˇ seobecn´ e probl´ emy Dalˇs´ı aspekt, kter´ ym se ve f´ azi n´ avrhu architektury mus´ıme zab´ yvat, jsou vˇseobecn´e probl´emy, kter´e nez´ avis´ı na konkr´etn´ı podobˇe ˇreˇsen´eho probl´emu. Jde pˇredevˇs´ım o uˇzivatelsk´e rozhran´ı programu, ˇr´ızen´ı vstupn´ıch a v´ ystupn´ıch operac´ı, spr´ avu pamˇeti, ale tak´e tˇreba o zach´ azen´ı se znakov´ ymi ˇretˇezci.
´ 9.4. NAVRH ARCHITEKTURY
145
Uˇzivatelsk´e rozhran´ı m˚ uˇze b´ yt specifikov´ ano jiˇz v poˇzadavc´ıch. Pokud ne, mus´ıme urˇcit struktury pˇr´ıkaz˚ u, menu, vstupn´ıch formul´ aˇr˚ u apod. Pˇritom modul´ arn´ı sloˇzen´ı programu je tˇreba navrhnout tak, aby pˇrechod k jin´emu rozhran´ı neovlivnil jeho funkˇcnost. Jako pˇr´ıklad uvedeme napˇr. pˇrekladaˇce Turbo Pascalu; ty jsou zpravidla dod´ av´ any dva, jeden integrovan´ y v programov´em prostˇred´ı a druh´ y, kter´ y lze spouˇstˇet samostatnˇe z pˇr´ıkazov´e ˇra ´dky. Samozˇrejmˇe jde o t´ yˇz program s jin´ ym uˇzivatelsk´ ym rozhran´ım - jin´ y postup by byl ekonomicky ne´ unosn´ y. Architektura by mˇela specifikovat u ´roveˇ n, na kter´e budou detekov´ any a oˇsetˇrov´ any chyby pˇri vstupn´ıch / v´ ystupn´ıch operac´ıch, bezpeˇcnostn´ı opatˇren´ı (poˇrizov´ an´ı z´ aloˇzn´ıch kopi´ı soubor˚ u) apod. V t´eto f´ azi v´ yvoje bychom tak´e mˇeli odhadnout potˇrebnou velikost pamˇeti v bˇeˇzn´ ych a v mimoˇra ´dn´ ych pˇr´ıpadech. Napˇr´ıklad pokud bude v´ ysledkem datab´ aze, mˇeli bychom odhadnout pamˇet’ potˇrebnou pro jeden z´ aznam a pˇr´ıpadnˇe pˇredpokl´ adan´ y poˇcet z´ aznam˚ u v datab´ azi. Je tˇreba uk´ azat, zda jsou poˇzadavky naˇs´ı aplikace v mez´ıch moˇznost´ı pˇredpokl´ adan´eho operaˇcn´ıho prostˇred´ı. Ve sloˇzitˇejˇs´ıch pˇr´ıpadech m˚ uˇze aplikace m´ıt vlastn´ı syst´em pro spr´ avu pamˇeti. M˚ uˇze se zd´ at, ˇze vˇenovat pozornost zach´ azen´ı se znakov´ ymi ˇretˇezci jiˇz pˇri n´ avrhu architektury je zbyteˇcn´e. Komerˇcnˇe vyuˇz´ıvan´e programy ovˇsem obsahuj´ı velk´ a mnoˇzstv´ı ˇretˇezc˚ u (menu, n´ apovˇedy, texty v dialogov´ ych oknech a jin´e); uˇzivatel si m˚ uˇze pˇra ´t nˇekter´e z nich zmˇenit, pˇrizp˚ usobit sv´ ym potˇreb´ am nebo sv´emu vkusu. Pˇri prodeji produktu do zahraniˇc´ı je tˇreba vˇsechny textov´e ˇretˇezce pˇreloˇzit. Proto je tˇreba odhadnout mnoˇzstv´ı textov´ ych ˇretˇezc˚ u, kter´e budou souˇca ´st´ı programu, a rozhodnout se, jak s nimi naloˇz´ıme. Lze je uloˇzit do zvl´ aˇstn´ıch soubor˚ u (to se obvykle dˇel´ a s n´ apovˇedou) nebo je tˇreba definovat jako konstanty ve zvl´ aˇstn´ım modulu. V programech pro MS Windows je m˚ uˇzeme popsat v tabulce ˇretˇezc˚ u (stringtable) v popisu prostˇredk˚ u programu (resources, soubor .RC). T´ım z´ısk´ ame moˇznost znakov´e ˇretˇezce snadno mˇenit, aniˇz by to mˇelo nˇejak´ y z´ avaˇznˇejˇs´ı dopad na zdrojov´ y k´ od programu. Lze je tak´e samozˇrejmˇe deklarovat pˇr´ımo na m´ıstˇe pouˇzit´ı a nevzruˇsovat se pˇr´ıpadn´ ymi probl´emy se zmˇenami z´ aleˇz´ı na poˇzadavc´ıch a na naˇs´ı pˇredv´ıdavosti. V n´ avrhu architektury bychom se mˇeli rozhodnout pro nˇekterou z moˇznost´ı a zaznamenat d˚ uvody, proˇc jsme se tak rozhodli. Zpracov´ an´ı chyb Zach´ azen´ı s chybami patˇr´ı k nejoˇzehavˇejˇs´ım probl´em˚ um v modern´ım programov´ an´ı. Existuj´ı odhady, kter´e tvrd´ı, ˇze t´emˇeˇr 90% k´ odu v bˇeˇzn´ ych programech se zab´ yv´ a oˇsetˇrov´ an´ım v´ yjimeˇcn´ ych situac´ı. Pˇri rozhodov´ an´ı o architektuˇre programu mus´ıme tak´e urˇcit, jak bude n´ aˇs produkt nakl´ adat s chybov´ ymi situacemi. Jde pˇredevˇs´ım o odpovˇed’ na n´ asleduj´ıc´ı ot´ azky: • Pouˇzijeme sp´ıˇse korektivn´ıho nebo sp´ıˇse detektivn´ıho pˇr´ıstupu? Korektivn´ı pˇr´ıstup znamen´ a, ˇze pokud nastane chyba, pokus´ıme se ji napravit. Pˇri detektivn´ım pˇr´ıstupu m˚ uˇzeme rovnou skonˇcit - m˚ uˇzeme se ale tak´e tv´ aˇrit, jako by se nic nestalo, a doufat, ˇze se opravdu nic nedˇeje. V obou pˇr´ıpadech by ale program mˇel uˇzivateli sdˇelit, jak´e probl´emy nastaly. • Budeme detekovat chyby aktivnˇe nebo pasivnˇe ? Jin´ ymi slovy, budeme se snaˇzit pˇredv´ıdat chyby uˇzivatele a napˇr. testovat spr´ avnost vstupn´ıch dat (tˇreba kontrolovat, zda uˇzivatel nepoˇzaduje v´ ypis ze dne 31. u ´nora), nebo se budeme o chyby starat aˇz v pˇr´ıpadˇe, ˇze se nic jin´eho ned´ a dˇelat - napˇr. kdyˇz hroz´ı dˇelen´ı nulou nebo kdyˇz k nˇemu dokonce uˇz doˇslo? • Aktivn´ı detekce chyb klade znaˇcn´e n´ aroky na uˇzivatelsk´e rozhran´ı. At’ se rozhodneme pro kteroukoli alternativu, mˇel by program v obou pˇr´ıpadech vˇcas informovat uˇzivatele, co se dˇeje. • Jak se v programu chyby ˇs´ıˇr´ı? Jak bude program reagovat na vznik chyby? Podle okolnost´ı m˚ uˇzeme zvolit nˇekterou z n´ asleduj´ıc´ıch alternativ: m˚ uˇzeme ihned odm´ıtnout data, kter´ a chybu zp˚ usobila; m˚ uˇzeme ihned pˇrej´ıt ke zpracov´ an´ı chyby; m˚ uˇzeme pokraˇcovat v p˚ uvodn´ı operaci aˇz do dokonˇcen´ı vˇsech proces˚ u a teprve pak informovat uˇzivatele, ˇze nˇekde nastala chyba. • Jak´e konvence budeme pouˇz´ıvat pro ohlaˇsov´ an´ı chyb? Je rozumn´e, aby vˇsechny moduly pouˇz´ıvaly pˇri ohlaˇsov´ an´ı chyb tot´eˇz rozhran´ı, jinak bude program p˚ usobit zmatenˇe.
146
´ PROJEKT KAPITOLA 9. SOFTWAROVY
• Na jak´e u ´rovni budou chyby v programu oˇsetˇrov´ any? M˚ uˇzeme se jimi zab´ yvat v m´ıstˇe vzniku, m˚ uˇzeme pro nˇe volat zvl´ aˇstn´ı podprogramy nebo se jimi zab´ yvat na nejvyˇsˇs´ı u ´rovni“. Moˇznosti n´ apravy chyby ” (zotaven´ı programu po chybˇe) se samozˇrejmˇe liˇs´ı podle u ´rovnˇe, na kter´e chybu detekujeme. Pokud chybu zachyt´ıme aˇz na u ´rovni cel´eho programu, nem˚ uˇzeme obvykle dˇelat nic jin´eho, neˇz ozn´ amit, co se stalo, a skonˇcit. • Jak´ a je zodpovˇednost jednotliv´ych modul˚ u za spr´ avnost jeho vstupn´ıch dat? Bude se kaˇzd´ y z modul˚ u starat o spr´ avnost sv´ ych dat, nebo budeme m´ıt speci´ aln´ı moduly, kter´e se postaraj´ı o spr´ avnost dat pro cel´ y syst´em? Mohou moduly na nˇejak´e u ´rovni pˇredpokl´ adat, ˇze dost´ avaj´ı bezchybn´ a data? Robustnost Pod robustnost´ı se obvykle ch´ ape schopnost syst´emu pokraˇcovat v ˇcinnosti pot´e, co doˇslo k chybˇe. Pˇri n´ avrhu architektury je rozumn´e urˇcit u ´roveˇ n robustnosti z nˇekolika hledisek. Pˇredevˇs´ım mus´ıme urˇcit m´ıru peˇclivosti, kterou pˇri zpracov´ an´ı poˇzadujeme. Obvykle se od jednotliv´ ych modul˚ u poˇzaduje vyˇsˇs´ı robustnost neˇz od cel´eho syst´emu. Zkuˇsenost totiˇz ukazuje, ˇze softwarov´ y produkt je zpravidla podstatnˇe slabˇs´ı neˇz jeho nejslabˇs´ı ˇca ´st. D´ ale je tˇreba urˇcit rozsah pouˇz´ıv´ an´ı testovac´ıch pˇr´ıkaz˚ u, jako je makro assert v jazyku C. Tyto pˇr´ıkazy testuj´ı, zda je splnˇena nˇejak´ a podm´ınka, plynouc´ı z pˇredpoklad˚ u (m˚ uˇzeme napˇr´ıklad ovˇeˇrovat pˇredpoklad, ˇze vstupn´ı soubor nebude vˇetˇs´ı neˇz 64 KB; pokud ano, ohl´ as´ı program chybu). Vedle toho je tˇreba stanovit m´ıru tolerance k chyb´ am a zp˚ usob reakce na nˇe. Pod´ıvejme se na nˇekter´e jednoduch´e moˇznosti. Syst´em se napˇr. m˚ uˇze pˇri v´ yskytu chyby pokusit vr´ atit k m´ıstu, kde jeˇstˇe bylo vˇse v poˇra ´dku, a pokraˇcovat odtud. M˚ uˇze se pokusit pouˇz´ıt pro zpracov´ an´ı dan´eho probl´emu jinou funkci (napˇr. jestliˇze se pˇri ˇreˇsen´ı soustavy line´ arn´ıch algebraick´ ych rovnic zjist´ı, ˇze soustava m´ a ˇspatnˇe podm´ınˇenou matici a ˇze tedy nelze pouˇz´ıt Gaussovu eliminaci, m˚ uˇze se syst´em pokusit pouˇz´ıt nˇekterou z iteraˇcn´ıch metod). Syst´em tak´e m˚ uˇze pro ˇreˇsen´ı rovnou pouˇz´ıt nˇekolika metod, z´ıskan´e v´ ysledky porovnat a podle zadan´e tolerance pak urˇcit, kter´ y v´ ysledek pouˇzije nebo zda pouˇzije napˇr. jejich aritmetick´ y pr˚ umˇer. Hodnotu, kterou syst´em urˇc´ı jako ˇspatnou, se m˚ uˇze pokusit nahradit hodnotou, kter´ a nebude m´ıt katastrofick´e d˚ usledky. Kromˇe toho m˚ uˇze syst´em pˇrej´ıt do stavu, ve kter´em nen´ı schopen prov´ adˇet vˇsechny sv´e funkce, m˚ uˇze skonˇcit a pˇr´ıpadnˇe se znovu spustit. Syst´em by tak´e mˇel br´ at v u ´vahu moˇznost, ˇze s´ am obsahuje chyby, a mˇel by (v omezen´e m´ıˇre) provˇeˇrovat sv´e vlastn´ı v´ ysledky, aby d˚ usledky tˇechto chyb omezil. V´ ykonnost Pod v´ ykonnost´ı m˚ uˇzeme ch´ apat jak rychlost tak i pamˇet’ov´e n´ aroky. Pokud je v´ ykonnost syst´emu d˚ uleˇzit´ a, je tˇreba specifikovat jej´ı krit´eria. Ve st´ adiu n´ avrhu architektury bychom mˇeli specifikovat odhad v´ ykonnosti a zd˚ uvodnit, proˇc se domn´ıv´ ame, ˇze takov´eto v´ ykonnosti lze dos´ ahnout. Mˇeli bychom specifikovat tak´e oblasti, ve kter´ ych hroz´ı nebezpeˇc´ı, ˇze poˇzadovan´eho v´ ykonu nedos´ ahneme. Pokud je k dosaˇzen´ı v´ ykonnosti tˇreba pouˇz´ıt speci´ aln´ıch algoritm˚ u nebo datov´ ych struktur, je tˇreba to zd˚ uraznit jiˇz v tomto stadiu. Zde je vhodn´e tak´e specifikovat pˇredpokl´ adan´e ˇcasov´e a pamˇet’ov´e n´ aroky jednotliv´ ych modul˚ u. Celkov´ a kvalita n´ avrhu architektury Pro kvalitu n´ avrhu architektury lze vymyslet ˇradu krit´eri´ı. Zejm´ena je samozˇrejm´e, ˇze n´ avrh mus´ı jasnˇe vymezit a zd˚ uvodnit c´ıle projektu a pouˇzit´e metody. Mus´ı b´ yt jasn´ y a pˇrehledn´ y, mus´ı pˇresnˇe specifikovat nebezpeˇcn´e oblasti a urˇcit zach´ azen´ı s nimi. Ten, kdo bude navrˇzenou architekturu implementovat, mus´ı n´ avrhu beze zbytku rozumˇet.
´ 9.4. NAVRH ARCHITEKTURY
9.4.1
147
Kontrola n´ avrhu architektury
Podobnˇe jako v pˇr´ıpadˇe provˇeˇrov´ an´ı u ´plnosti pˇredbˇeˇzn´ ych poˇzadavk˚ u, i v pˇr´ıpadˇe n´ avrhu architektury m´ a smysl sepsat si kontroln´ı ot´ azky a podle nich hledat, na co jsme mohli zapomenout. N´ asleduj´ıc´ı seznam opˇet vych´ az´ı z [7]. •
Je jasn´ a celkov´ a organizace programu, vˇcetnˇe pˇrehled˚ u a zd˚ uvodnˇen´ı architektury?
•
Jsou moduly dobˇre definov´ any, je jasn´ a jejich funkce, jsou jasn´ a jejich rozhran´ı s jin´ ymi moduly?
•
Jsou pokryty vˇsechny funkce, uveden´e v seznamu poˇzadavk˚ u? Jsou pokryty rozumnˇe, tj. nen´ı jim vˇenov´ ano pˇr´ıliˇs m´ alo nebo pˇr´ıliˇs mnoho modul˚ u?
•
Je architektura navrˇzena tak, aby se mohla vyrovnat se kteroukoli z pravdˇepodobn´ ych zmˇen?
•
Obsahuje n´ avrh vˇsechna rozhodnut´ı, zda urˇcit´ y produkt (nebo jeho souˇca ´st) koupit nebo vyv´ıjet?
•
Popisuje n´ avrh, jak se pˇrizp˚ usob´ı knihovn´ı funkce a jin´e opakovanˇe pouˇz´ıvan´e programov´e souˇca ´sti, aby vyhovovaly naˇsemu produktu?
•
Jsou v n´ avrhu pops´ any a zd˚ uvodnˇeny vˇsechny hlavn´ı datov´e struktury?
•
Lze s hlavn´ımi datov´ ymi strukturami zach´ azet pouze prostˇrednictv´ım pˇr´ıstupov´ ych funkc´ı?
•
Je specifikov´ an obsah a organizace datab´ az´ı?
•
Popsali a zd˚ uvodnili jsme vˇsechny hlavn´ı algoritmy?
•
Popsali jsme strategii pˇri zach´ azen´ı se vstupn´ımi a v´ ystupn´ımi operacemi (vˇcetnˇe vstup˚ u a v´ ystup˚ u uˇzivatelsk´ ych)?
•
Definovali jsme kl´ıˇcov´e aspekty uˇzivatelsk´eho rozhran´ı?
•
Je modul´ arn´ı struktura uˇzivatelsk´eho rozhran´ı takov´ a, ˇze neovlivˇ nuje zbytek programu?
•
Popsali a zd˚ uvodnili jsme strategii spr´ avy pamˇeti a odhady pamˇet’ov´ ych n´ arok˚ u?
•
Odhadli jsme ˇcasov´e a pamˇet’ov´e n´ aroky jednotliv´ ych modul˚ u?
•
Obsahuje n´ avrh strategii pro zach´ azen´ı s ˇretˇezci a odhad potˇrebn´eho m´ısta pro nˇe?
•
Obsahuje n´ avrh vhodnou strategii pro oˇsetˇrov´ an´ı chyb? Jsou chybov´ a hl´ aˇsen´ı konzistentn´ı s uˇzivatelsk´ ym rozhran´ım?
•
Specifikovali jsme u ´roveˇ n robustnosti?
•
Formulovali jsme jasnˇe hlavn´ı c´ıle syst´emu?
•
Je n´ avrh vyv´ aˇzen´ y? Nen´ı nˇekter´ a ˇca ´st zpracov´ ana pˇr´ıliˇs peˇclivˇe a jin´ a nedbale?
•
Je n´ avrh architektury jako celek konzistentn´ı? To znamen´ a, je jasn´ a souvislost jednotliv´ ych ˇca ´st´ı?
•
Je n´ avrh na nejvyˇsˇs´ı u ´rovni nez´ avisl´ y na poˇc´ıtaˇci a na programovac´ım jazyku?
•
Jsou jasn´e d˚ uvody vˇsech rozhodnut´ı?
•
Kl´ıˇcov´ a ot´ azka na z´ avˇer: Jste s n´ avrhem spokojen jako program´ ator, kter´ y jej bude implementovat?
´ PROJEKT KAPITOLA 9. SOFTWAROVY
148
9.4.2
Programovac´ı jazyk
Po vyjasnˇen´ı architektury je tˇreba rozhodnout se pro vhodn´ y programovac´ı jazyk. Jeho volba m˚ uˇze podstatn´ ym zp˚ usobem ovlivnit nejen v´ ykonnost programu, ale tak´e program´ atora. Rozhodnut´ı m˚ uˇze ovˇsem z´ aviset na dostupn´ ych prostˇredc´ıch, na poˇzadavc´ıch z´ akazn´ıka, ale i na schopnostech a osobn´ım vkusu program´ atora. Pod´ıvejme se na nˇekter´e nejbˇeˇznˇejˇs´ı moˇznosti. Pokud n´ am jde o maxim´ aln´ı u ´spornost k´ odu a o v´ ykonnost programu, obvykle vol´ıme asembler . Pro numerick´e v´ ypoˇcty se st´ ale ˇcasto pouˇz´ıv´ a Fortran; v´ yhodou tohoto jazyka jsou obrovsk´e knihovny podprogram˚ u pro t´emˇeˇr vˇsechny bˇeˇznˇejˇs´ı matematick´e, fyzik´ aln´ı a obecnˇe technick´e probl´emy, se kter´ ymi se lze setkat. V oblasti hromadn´eho zpracov´ an´ı dat dlouhou dobu pˇrevl´ adal jazyk Cobol . Nab´ızel ˇradu n´ astroj˚ u, zamˇeˇren´ ych na rozs´ ahl´e datov´e soubory (napˇr. pˇr´ıkazy pro tˇr´ıdˇen´ı soubor˚ u, gener´ ator sestav apod.). Jiˇz ve verzi Cobol 70 se objevily prostˇredky pro paraleln´ı programov´ an´ı. Na druh´e stranˇe neumoˇzn ˇoval ukr´ yv´ an´ı dat - veˇsker´ a data se deklarovala jako glob´ aln´ı. Zd´ a se, ˇze jej v souˇcasn´e dobˇe vytlaˇcuj´ı datab´ azov´e jazyky. Pro komunikaci s datab´ azemi se dnes ˇcasto pouˇz´ıv´ a SQL (structured querry language - strukturovan´ y dotazovac´ı jazyk). Vedle toho se lze ˇcasto setkat s aplikacemi, napsan´ ymi v jazyc´ıch, kter´e jsou souˇca ´st´ı datab´ az´ı Gupta, Oracle, PowerBuilder, dBase nebo Paradox. Mezi dnes nejrozˇs´ıˇrenˇejˇs´ı jazyky patˇr´ı Pascal a jazyk C . Moˇznosti, kter´e tyto jazyky nab´ızej´ı, jsou v podstatˇe stejn´e (alespoˇ n pokud jde o implementace tˇechto jazyk˚ u na osobn´ıch poˇc´ıtaˇc´ıch). Jde o jazyky univerz´ aln´ı, kter´e lze rozumnˇe pouˇz´ıt pro ˇreˇsen´ı vˇetˇsiny probl´em˚ u. Jedin´ y z´ avaˇznˇejˇs´ı rozd´ıl mezi nimi pˇredstavuje c´eˇckovsk´ a adresov´ a aritmetika, kter´ a nem´ a v bˇeˇzn´ ych implementac´ıch Pascalu obdobu. Pokud ale vych´ az´ıme z vyloˇzenˇe objektovˇe orientovan´eho n´ avrhu, bude asi nejvhodnˇejˇs´ı jazyk C++. Tento jazyk je v podstatˇe nadmnoˇzinou C´eˇcka, nav´ıc obsahuje pˇredevˇs´ım objektov´e typy s plnˇe rozvinut´ ymi moˇznostmi (omezov´ an´ı pˇr´ıstupu ke sloˇzk´ am, v´ıcen´ asobnou dˇediˇcnost, polymorfismus). Pro objektov´e typy lze v C++ tak´e definovat vlastn´ı verze vˇetˇsiny oper´ ator˚ u. Kromˇe toho nab´ız´ı C++ ˇsablony (generick´e typy a funkce) a moˇznost vyvol´ avat a oˇsetˇrovat v´ yjimeˇcn´e situace (to se pouˇz´ıv´ a zejm´ena pˇri zpracov´ an´ı chyb). Bohuˇzel zdaleka ne vˇsechny pˇrekladaˇce dosud tyto pokroˇcil´e moˇznosti jazyka C++ implementuj´ı. Posledn´ı dobou se objevuj´ı i ˇcistˇe objektov´e“ programovac´ı jazyky, jako Actor, Eiffel nebo Smalltalkk. V ” souˇcasn´e dobˇe jde vˇetˇsinou o interpretaˇcn´ı syst´emy, kter´e se pˇr´ıliˇs nehod´ı k implementaci v´ ykonn´ ych aplikac´ı; v budoucnosti se vˇsak mohou st´ at u ´ˇcinn´ ym v´ yvojov´ ym n´ astrojem. Pokud nepˇredstavuje velikost programu a rychlost velk´ y probl´em, lze pouˇz´ıt nˇekter´ ych v´ yvojov´ ych prostˇredk˚ u zaloˇzen´ ych na gener´ atorech k´ odu. Napˇr. pˇri v´ yvoji aplikac´ı pro MS Windows v prostˇred´ı Borland C++ 4.0 m˚ uˇzeme vyuˇz´ıt AppExpert, aplikaci, kter´ a na z´ akladˇe jak´ehosi dotazn´ıku vytvoˇr´ı zdrojov´ y k´ od v C++, zaloˇzen´ y na objektovˇe orientovan´e knihovnˇe Object Windows Library. Do tohoto prototypu“ programu pak dopln´ıme ” v´ ykonn´e souˇca ´sti. V posledn´ı dobˇe se pˇri tvorbˇe aplikac´ı pro Windows dosti rozˇs´ıˇrilo vyuˇz´ıv´ an´ı vizu´ aln´ıch v´ yvojov´ ych prostˇred´ı, jako je Visual Basic nebo Visual C++. V tˇechto prostˇred´ıch se obvykle pomoc´ı myˇsi sestav´ı z pˇreddefinovan´ ych prvk˚ u uˇzivatelsk´e rozhran´ı aplikace a pˇr´ıpadnˇe i nˇekter´e funkˇcn´ı souˇca ´sti. (Souˇca ´stmi mohou b´ yt i velmi rozs´ ahl´e funkˇcn´ı celky - tˇreba cel´ y tabulkov´ y procesor, textov´ y procesor, utilita pro kreslen´ı diagram˚ u apod. [16].) Na z´ akladˇe graficky“ sestaven´eho uˇzivatelsk´eho rozhran´ı pak prostˇred´ı vytvoˇr´ı program, ke kter´emu dopln´ıme ” dalˇs´ı potˇrebn´e ˇca ´sti.
9.5
Dalˇ s´ı kroky
N´ asleduj´ıc´ım krokem pˇri v´ yvoji softwarov´eho produktu je podrobn´ y n´ avrh. Zde urˇcujeme vlastnˇe vnitˇrn´ı strukturu modul˚ u. Definujeme v´ yznam jednotliv´ ych funkc´ı v modulech a navrhujeme algoritmy, kter´e v nich pouˇzijeme. Pˇritom specifikujeme i datov´e struktury, kter´e jsme neuvaˇzovali pˇri n´ avrhu architektury.
9.5. DALSˇ´I KROKY
149
Teprve po sestaven´ı podrobn´eho n´ avrhu lze pˇristoupit k vlastn´ımu programov´ an´ı, tedy k pˇrepisu algoritm˚ u do zvolen´eho programovac´ıho jazyka. Pak pˇrijde na ˇradu ladˇen´ı. Prvn´ı z moˇzn´ ych krok˚ u, doporuˇcovan´ y pˇri t´ ymov´e pr´ aci, je nechat pˇreˇc´ıst sv˚ uj k´ od ˇ jin´emu program´ atorovi. Casto se tak podaˇr´ı odhalit vˇcas r˚ uzn´ a nepˇr´ıjemn´ a opomenut´ı. Dalˇs´ım krokem, kter´ y n´ asleduje po form´ aln´ım odladˇen´ı, je testov´ an´ı. Nejprve se zpravidla testuj´ı jednotliv´e funkce, potom jednotliv´e moduly. Teprve nakonec se testuje syst´em jako celek. Pˇritom se vol´ı r˚ uzn´e pˇr´ıstupy. Napˇr. seznam pˇredbˇeˇzn´ ych poˇzadavk˚ u bude z´ akladem test˚ u ovˇeˇruj´ıc´ıch, zda programov´ y produkt vyhovuje poˇzadavk˚ um z´ akazn´ıka. Obvykle se tak´e testuj´ı extr´emn´ı pˇr´ıpady vstupn´ıch dat, pˇr´ıpady, kdy m´ a aplikace jen minimum pamˇeti apod. Pˇri testov´ an´ı lze s aplikac´ı zach´ azet jako s ˇcernou skˇr´ıˇ nkou nebo lze analyzovat jej´ı zdrojov´ y k´ od a podle toho hledat chyby. U aplikac´ı, kter´e budou komerˇcnˇe ˇs´ıˇreny, se zpravidla tak´e prov´ ad´ı tzv. beta-testov´ an´ı, pˇri kter´em se produkt poskytne zdarma nebo za minim´ aln´ı poplatek vybran´ ym uˇzivatel˚ um a ti shrom´ aˇzd´ı informace o jeho vad´ ach, probl´emech a nedostatc´ıch. Posledn´ı f´ az´ı, o kter´e se zm´ın´ıme, je u ´drˇzba. Ta zahrnuje napˇr. pr˚ ubˇeˇzn´e odstraˇ nov´ an´ı nedostatk˚ u (chyby by se v koneˇcn´em produktu vyskytnout nemˇely, ale zkuˇsenosti s produkty velk´ ych firem ukazuj´ı, ˇze se tam se ˇzeleznou pravidelnost´ı objevuj´ı - viz napˇr. probl´emy kolem pˇrekladaˇc˚ u Borland C++). Vedle toho p˚ ujde o v´ yvoj nov´ ych verz´ı, kter´e budou vyhovovat nov´ ym n´ arok˚ um uˇzivatel˚ u, kter´e bude moˇzno provozovat v jin´ ych operaˇcn´ıch prostˇred´ıch (m˚ uˇze j´ıt napˇr. o pˇrechod z Windows pod Windows NT) nebo kter´e budou obsahovat u ´ˇcinnˇejˇs´ı algoritmy. Je samozˇrejm´e, ˇze speci´ alnˇe pˇri u ´drˇzbˇe programu ocen´ıme pr˚ uzraˇcnost architektury, pˇrehlednost n´ avrhu na vˇsech u ´rovn´ıch, dobˇre zpracovanou dokumentaci, zkr´ atka dobr´ y programovac´ı styl.
150
´ PROJEKT KAPITOLA 9. SOFTWAROVY
Kapitola 10
N´ avrh architektury zaloˇ zen´ y na anal´ yze poˇ zadavk˚ u Metody anal´ yzy poˇzadavk˚ u se zpravidla - pˇr´ımo nebo nepˇr´ımo - op´ıraj´ı bud’ o rozbor toku dat v syst´emu nebo o rozbor struktury dat. Tok dat se obvykle charakterizuje v souvislosti s funkcemi, kter´e pˇretv´ aˇrej´ı vstupn´ı data na data v´ ystupn´ı. Jako pˇr´ıklady si uk´ aˇzeme metodu zaloˇzenou na diagramech toku dat [31] a Jacksonovu metodu (Jackson System Development [14], [15]; u n´ as se pro ni obˇcas pouˇz´ıv´ a oznaˇcen´ı Jacksonovo strukturovan´e programov´ an´ı“). ”
10.1
Diagramy toku dat
Pˇri rozboru toku dat v syst´emu se zab´ yv´ ame transformacemi informac´ı pˇri pr˚ uchodu syst´emem, ˇr´ızen´ ym poˇc´ıtaˇcem. Do syst´emu pˇrich´ azej´ı informace v r˚ uzn´ ych podob´ ach a z r˚ uzn´ ych zdroj˚ u. Jejich transformace m˚ uˇze zahrnovat stejnˇe dobˇre n´ aroˇcn´e numerick´e v´ ypoˇcty jako pouh´e porovn´ an´ı. V´ ystupem pak m˚ uˇze b´ yt tisk sestavy nebo tˇreba jen rozsv´ıcen´ı kontrolky. Informace, kter´e proch´ azej´ı syst´emem, jsou podrobov´ any ˇradˇe transformac´ı. Tyto transformace vyj´ adˇr´ıme diagramem, ve kter´em se obvykle pouˇz´ıvaj´ı n´ asleduj´ıc´ı znaˇcky: •
Obd´eln´ık vyznaˇcuje vnˇejˇs´ı objekt, kter´ y pˇredstavuje bud’ zdroj, odkud pˇrich´ azej´ı informace do syst´emu, nebo pˇr´ıjemce informac´ı od syst´emu. Od jednoho zdroje m˚ uˇze vych´ azet i v´ıce datov´ ych tok˚ u.
•
Kruh ( bublina“) oznaˇcuje proces, tedy transformaci dat. ” • Spojnice oznaˇcuj´ı pˇrenosy informac´ı mezi procesy navz´ ajem nebo mezi procesy a vnˇejˇs´ımi objekty. ˇ Sipky ukazuj´ı smˇer pˇrenosu dat. Kaˇzd´ a spojnice by mˇela b´ yt pojmenov´ ana. •
Dvojice vodorovn´ ych rovnobˇeˇzek oznaˇcuje zaˇr´ızen´ı na ukl´ ad´ an´ı dat.
Pˇr´ıklad diagramu vid´ıte na obr. 10.1; v nˇem data ze zdroje (D1) proch´ azej´ı transformaˇcn´ım procesem T 1, pˇri kter´em se pˇremˇen´ı na data D2. Ta pˇrevezme proces T 2, uloˇz´ı si je jako data D3 do zaˇr´ızen´ı pro u ´schovu dat. Odtud si je vyzvedne a jako data D4 je pˇred´ a procesu T 3, kter´ y je po patˇriˇcn´e transformaci pˇred´ a jako data D5 koneˇcn´emu pˇr´ıjemci.
T1
D4
T2
D2 D3
D3
Pamˇet’
T3 D5
D1 Pomoc´ı takov´ ychto diagram˚ u lze popisovat syst´em nebo program na libovoln´e u ´rovni abstrakce. Na z´ ac´ıl zdroj kladn´ı u ´rovni se zpravidla cel´ y softwarov´ y syst´em vyjadˇruje jedin´ ym kruhem - to znamen´ a, ˇze na t´eto u ´rovni popisujeme pouze informaˇcn´ı toky mezi sysObr. 10.1: Pˇr´ıklad diagramu toku dat. t´emem a jeho okol´ım. V dalˇs´ıch kroc´ıch pak upˇresˇ nujeme strukturu syst´emu, tj. rozkl´ ad´ ame jednotliv´e procesy na podprocesy a urˇcujeme datov´e toky mezi nimi. 151
152
´ ˇ Y ´ NA ANALYZE ´ ˇ ˚ KAPITOLA 10. NAVRH ARCHITEKTURY ZALOZEN POZADAVK U
pacient
data pˇr´ıstroj˚ u
poˇzadavek v´ ypisu
v´ ypis syst´em pro monitorov´ani pacient˚ u
person´al varovn´ y sign´al u ´daje v z´aznamu
person´al z´aznam o pacientovi Obr. 10.2: Z´ akladn´ı diagram toku dat v syst´emu pro sledov´ an´ı pacient˚ u
Tyto diagramy nevyjadˇruj´ı pˇr´ımo poˇrad´ı ud´ alost´ı (napˇr. poˇrad´ı vstup˚ u od jednotliv´ ych zdroj˚ u apod.). Nevyznaˇcuj´ı tak´e ˇza ´dn´ ym zp˚ usobem ˇr´ıdic´ı struktury programu (cykly, podm´ınky apod.). Takov´eto probl´emy se ˇreˇs´ı pozdˇeji, aˇz pˇri vlastn´ım softwarov´em n´ avrhu. Pˇ r´ıklad 10.1: monitorovac´ı syst´ em Jako pˇr´ıklad pouˇzit´ı diagram˚ u toku dat k rozboru poˇzadavk˚ u si uk´ aˇzeme n´ avrh programov´eho vybaven´ı pro sledov´ an´ı pacient˚ u na jednotce intenzivn´ı p´eˇce [12]. Na obr´ azku 10.2 vid´ıte, jak m˚ uˇze vypadat z´ akladn´ı diagram. Pˇri zjemˇ nov´ an´ı tohoto diagramu mus´ıme rozloˇzit syst´em pro monitorov´ an´ı pacient˚ u na podsyst´emy a ujasnit si tok dat mezi nimi. Zm´ınˇen´ y syst´em se napˇr. bude skl´ adat z centr´ aln´ıho monitorovac´ıho syst´emu, kter´ y bude pˇrij´ımat informace od syst´em˚ u pro sledov´ an´ı jednotliv´ych pacient˚ u a bude vyuˇz´ıvat soubor s informacemi o mezn´ıch hodnot´ ach sledovan´ ych u ´daj˚ u. Z´ıskan´e u ´daje bude pˇred´ avat syst´emu pro aktualizaci z´ aznam˚ u o pacientech. Syst´em pro generov´ an´ı v´ypis˚ u (sestav) m˚ uˇze b´ yt na centr´ aln´ım monitorovac´ım syst´emu nez´ avisl´ y, potˇrebuje m´ıt pˇr´ıstup pouze k z´ aznam˚ um o pacientech. Person´ al bude pˇrij´ımat varovn´e sign´ aly od centr´ aln´ıho monitorovac´ıho syst´emu. S poˇzadavky na v´ ypis se bude obracet na gener´ ator v´ ypis˚ u. Diagram, kter´ y vznikne na z´ akladˇe uveden´ ych zjemnˇen´ı, vid´ıte na obr´ azku 10.3. ˇ aˇr se m˚ Cten´ uˇze s´ am pokusit navrhnout dalˇs´ı zjemnˇen´ı, kter´ a se budou t´ ykat nejsp´ıˇse centr´ aln´ıho monitorovac´ıho syst´emu. Anal´ yza ovˇsem nebude u ´pln´ a, pokud se budeme zab´ yvat pouze tokem dat. Mus´ıme si samozˇrejmˇe vˇs´ımat i obsahu tˇechto tok˚ u, tedy samotn´ ych dat, kter´ a zde proud´ı. Z popisu tˇechto dat vych´ az´ıme pˇri n´ avrhu funkc´ı, kter´e prov´ adˇej´ı jejich transformaci. Jednu z moˇzn´ ych metod pˇredstavuj´ı slovn´ıky dat. Jde o popis struktury dat, zaloˇzen´ y na vhodnˇe definovan´e form´ aln´ı gramatice, kter´ a ˇcasto pˇripom´ın´ a popis datov´ ych typ˚ u programovac´ıho jazyka. Slovn´ık mus´ı obsahovat definice vˇsech dat, na kter´ a v tokov´em diagramu nar´ aˇz´ıme. Element´ arn´ı data definujeme tak, ˇze pop´ıˇseme jejich v´ yznam, sloˇzen´ a data vyj´ adˇr´ıme rozkladem na komponenty. Pˇri popisu dat stanov´ıme z´ akladn´ı datov´e struktury (posloupnost u ´daj˚ u, opakuj´ıc´ı se data, selekci). Vyznaˇc´ıme - je-li to moˇzn´e - poˇcet opakov´ an´ı a u ´daje, kter´e se mohou, ale nemus´ı vyskytovat (voliteln´ a data). Pˇ r´ıklad 10.2: slovn´ık dat Jako jednoduch´ y pˇr´ıklad se pod´ıv´ ame se na strukturu telefonn´ıho ˇc´ısla. M˚ uˇzeme je popsat takto: telefonn´ı ˇc´ıslo = [m´ıstn´ı ˇc´ıslo | mezimˇestsk´e ˇc´ıslo] m´ıstn´ı ˇc´ıslo = {ˇc´ıslice}83 mezimˇestsk´e ˇc´ıslo = pˇredˇc´ısl´ı + k´ od mˇesta + m´ıstn´ı ˇc´ıslo pˇredˇc´ısl´ı = [vnitrost´ atn´ı pˇredˇc´ısl´ı | mezin´ arodn´ı pˇredˇc´ısl´ı] vnitrost´ atn´ı pˇredˇc´ısl´ı = 0 mezin´ arodn´ı pˇredˇc´ısl´ı = 00 + k´ od st´ atu
10.2. JACKSONOVA METODA
153
data pˇr´ıstroj˚ u system pro jednoho pacient pacienta varovn´e sign´aly person´al
mezn´ı hodnoty mezn´ı hodnoty u ´daj˚ u pˇr´ıstroj˚ u centr´aln´ı monitorovac´ı syst´em
form´atovan´a data
v´ ypis poˇzadavek v´ ypisu person´al
gener´ator sestav
u ´daje ze z´aznamu
aktualizace z´aznamu u ´daje ze z´aznamu
z´aznam o pacientovy Obr. 10.3: Zjemnˇen´ı diagramu toku dat v syst´emu pro sledov´ an´ı pacient˚ u
Pˇri tomto popisu jsme pouˇz´ıvali +“ pro vyznaˇcen´ı sekvence (mezin´ arodn´ı pˇredˇc´ısl´ı se skl´ ad´ a ze dvou nul, za ” kter´ ymi n´ asleduje k´ od st´ atu), [ | ]“ pro vyznaˇcen´ı selekce (telefonn´ı ˇc´ıslo je bud’ m´ıstn´ı nebo mezimˇestsk´e) a ” n {} “ oznaˇcovalo opakov´ an´ı (m´ıstn´ı ˇc´ıslo se skl´ ad´ a ze tˇr´ı aˇz osmi ˇc´ıslic). ” m Pˇri n´ avrhu rozs´ ahl´ ych softwarov´ ych produkt˚ u m˚ uˇze b´ yt slovn´ık dat velice rozs´ ahl´ y a pr´ ace s n´ım m˚ uˇze pˇresahovat moˇznosti manu´ aln´ıho zpracov´ an´ı. Proto nˇekter´e syst´emy pro poˇc´ıtaˇcovou podporu tvorby software (CASE) umoˇzn ˇuj´ı automatickou tvorbu a zpracov´ an´ı datov´ ych slovn´ık˚ u. Jakmile skonˇc´ıme popis dat, mus´ıme popsat funkce, kter´e budou obstar´ avat jejich transformace.
10.2
Jacksonova metoda
Tuto metodu vyvinul na z´ akladˇe anal´ yzy informac´ı a dat a jejich vztah˚ u k n´ avrhu programu M. A. Jackson koncem sedmdes´ at´ ych let [14], [15]. Soustˇred’uje se, podobnˇe jako nˇekter´e podobn´e syst´emy, na oblast informac´ı o re´ aln´em svˇetˇe“. Program´ ator (v´ yvoj´ aˇr) vytvoˇr´ı nejprve model reality, kterou se bude syst´em zab´ yvat. ” V´ yvoj programu Jacksonovou metodou prob´ıh´ a v n´ asleduj´ıc´ıch kroc´ıch: •
V´ybˇer objekt˚ u a akc´ı. Urˇc´ıme objekty, entity vnˇejˇs´ıho svˇeta, se kter´ ymi bude program pracovat, a akce, ud´ alosti, kter´e se tˇechto objekt˚ u t´ ykaj´ı. Vyjdeme od formulace probl´emu, tedy od struˇcn´eho popisu probl´emu v bˇeˇzn´em jazyku. Zhruba lze tvrdit, ˇze objekty odpov´ıdaj´ı podstatn´ ym jm´en˚ um v tomto popisu, akce pak sloves˚ um. (Samozˇrejmˇe takov´eto tvrzen´ı je tˇreba br´ at se znaˇcnou rezervou.)
•
Urˇcen´ı struktury objekt˚ u. Pod pojmem struktura objektu v souvislosti s Jacksonovou metodou rozum´ıme dopad jednotliv´ ych akc´ı na objekt. Akce, p˚ usob´ıc´ı na objekt, mohou tvoˇrit posloupnost, m˚ uˇze nastat pr´ avˇe jedna akce z nˇekolika moˇzn´ ych nebo se m˚ uˇze urˇcit´ a akce periodicky opakovat. Ud´ alosti a akce, kter´e se t´ ykaj´ı jednotliv´ ych objekt˚ u, uspoˇra ´d´ ame podle ˇcasov´eho hlediska a pop´ıˇseme je pomoc´ı Jacksonov´ ych diagram˚ u.
•
Vytvoˇren´ı poˇca ´teˇcn´ıho modelu. V tomto kroku zaˇcneme tvoˇrit specifikaci syst´emu jako modelu re´ aln´eho svˇeta. Komunikaci mezi procesy vyznaˇcujeme pomoc´ı diagram˚ u pro specifikaci syst´emu. V nich kruhem vyznaˇc´ıme pˇrenos pomoc´ı vyrovn´ avac´ı pamˇeti FIFO (fronty) o neomezen´e kapacitˇe a kosoˇctvercem komunikaci, pˇri kter´e jeden proces m˚ uˇze pˇr´ımo pouˇz´ıt stavov´ y vektor druh´eho procesu. V tˇechto diagramech se objekty re´ aln´eho svˇeta obvykle oznaˇcuj´ı pˇr´ıponou 0 a pˇr´ıponou 1 se oznaˇcuj´ı procesy, kter´e je modeluj´ı.
´ ˇ Y ´ NA ANALYZE ´ ˇ ˚ KAPITOLA 10. NAVRH ARCHITEKTURY ZALOZEN POZADAVK U
154
A
A1
A2
....
An
Obr. 10.5: Zn´ azornˇen´ı posloupnosti V tomto kroku tedy sestav´ıme objekty a ud´ alosti do modelu procesu a urˇc´ıme vztahy a spojen´ı mezi modelem a re´ aln´ ym svˇetem. Pˇri popisu struktury modelu m˚ uˇzeme pouˇz´ıvat tak´e popis pomoc´ı jazyka pro popis logick´ych struktur (v origin´ ale naz´ yvan´ y structure text). Pokud jej pouˇzijeme, budeme jej oznaˇcovat jako textov´y popis. V nˇem zkratkami seq, itr a sel oznaˇcujeme postupnˇe posloupnost (sekvenci), cyklus (iteraci) a vˇetven´ı (selekci - viz d´ ale). S pouˇzit´ım textov´eho popisu se setk´ ame v n´ asleduj´ıc´ım pˇr´ıkladu. Dalˇs´ı kroky pˇri n´ avrhu Jacksonovou metodou jsou: •
Specifikace funkc´ı. Pop´ıˇseme funkce, kter´e odpov´ıdaj´ı akc´ım, zahrnut´ ym do modelu. To znamen´ a, ˇze diagramy pro specifikaci syst´emu rozˇs´ıˇr´ıme o novˇe definovan´e funkce (procesy), kter´e propoj´ıme s modelov´ ym procesem pomoc´ı datov´ ych proud˚ u.
•
Popis ˇcasov´ych vztah˚ u. V tomto kroku specifikujeme ˇcasov´ a omezen´ı, kladen´ a na syst´em. V pˇredchoz´ıch kroc´ıch jsme z´ıskali model, sloˇzen´ y ze sekvenˇcn´ıch proces˚ u, kter´e pro vz´ ajemnou komunikaci vyuˇz´ıvaj´ı jednak datov´ ych proud˚ u a jednak pˇr´ım´eho pˇr´ıstupu ke sv´ ym stavov´ ym vektor˚ um. Nyn´ı je tˇreba urˇcit ˇcasov´e vazby mezi nimi a pˇr´ıpadnˇe synchronizaˇcn´ı mechanismy pro komunikaci mezi jednotliv´ ymi procesy.
•
Implementace. Vytvoˇr´ıme n´ avrh hardwarov´e a softwarov´e implementace syst´emu. Pˇritom z´ akladem naˇseho postupu bude rozklad hierarchick´ ych struktur na menˇs´ı ˇca ´sti, kter´e lze vyj´ adˇrit jako nˇekter´e ze z´ akladn´ıch ˇr´ıdic´ıch konstrukc´ı (posloupnost, iterace, selekce).
10.2.1
Jacksonovy diagramy
Jacksonovy diagramy zn´ azorˇ nuj´ı z´ akladn´ı ˇr´ıdic´ı algoritmick´e struktury (posloupnosti, cykly a podm´ınky, jak jsme o nich hovoˇrili v kapitole o algoritmech, viz 1.1.4.). Z´ akladn´ı sloˇzka struktury se oznaˇcuje obd´eln´ıkem, ve kter´em je veps´ an n´ azev t´eto sloˇzky - viz obr. 10.4. Posloupnost
A
Jm´eno sloˇzky
Obr. 10.4: Z´ akladn´ı sloˇzka Jacksonova diagramu Cyklus
Jestliˇze se posloupnost operac´ı A skl´ ad´ a z operac´ı A1 , A2 , . . . , An , kter´e prov´ ad´ıme postupnˇe v uveden´em poˇrad´ı, vyznaˇc´ıme to zp˚ usobem, kter´ y ukazuje obr. 10.5.
Jestliˇze je v cyklu A opakov´ an´ı operace B (tˇela cyklu) v´ az´ ano na podm´ınku C, zn´ azorn´ıme to zp˚ usobem, kter´ y vid´ıte na obr. 10.6(a). Podm´ınka opakov´ an´ı C je samozˇrejmˇe ned´ılnou souˇca ´st´ı cyklu A jako celku; pˇresto ji zapisujeme k tˇelu cyklu. Selekce Selekce A m´ a obecnˇe takov´ yto tvar: je-li splnˇena podm´ınka C1 , provede se operace B1 , jinak je-li splnˇena podm´ınka C2 , provede se akce B2 , . . . , jinak je-li splnˇena podm´ınka Cn , provede se akce Bn . Takovouto selekci vyj´ adˇr´ıme diagramem, kter´ y vid´ıte na obr´ azku 10.6(b). Tak´e zde jsou podm´ınky C 1 , C2 , . . . , Cn ned´ılnou souˇca ´st´ı selekce A jako celku, nikoli akc´ı B1 , B2 , . . . , Bn . Pˇresto se zapisuj´ı ke sloˇzk´ am. Podobn´e diagramy lze pouˇz´ıt i k vyj´ adˇren´ı struktury dat. Tyto diagramy se pouˇz´ıvaj´ı tak´e pˇri n´ avrhu algoritm˚ u (tj. pˇri n´ avrhu na niˇzˇs´ı u ´rovni) Jacksonovou metodou na z´ akladˇe anal´ yzy struktury dat.
10.2. JACKSONOVA METODA
155
A
A
B∗
C
B10
(a)
C1
B20
C2
(b)
Obr. 10.6: Zn´ azornˇen´ı iterace (a) a selekce (b);C resp. Ci jsou podm´ınky Pˇ r´ıklad 10.3: m´ıstn´ı doprava Tento pˇr´ıklad jsme v podstatˇe pˇrevzali z [12]. Jde o rozbor poˇzadavk˚ u pro poˇc´ıtaˇcem ˇr´ızenou m´ıstn´ı dopravu mezi dvˇema objekty velk´e univerzity. Vyjdeme od zad´ an´ı, tedy od slovn´ıho popisu probl´emu. Velk´ a univerzita vyuˇz´ıv´ a dvou budov, kter´e jsou od sebe vzd´ aleny v´ıce neˇz 2 km. Protoˇze studenti maj´ı pˇredn´ aˇsky v obou budov´ ach, chce univerzita vybudovat mezi nimi automaticky ˇr´ızenou kyvadlovou dopravu. P˚ ujde o jeden v˚ uz, jezd´ıc´ı po kolej´ıch a ˇr´ızen´y poˇc´ıtaˇcem. Trat’ bude m´ıt dvˇe stanice, u kaˇzd´e budovy jednu. V kaˇzd´e ze stanic bude pˇrivol´ avac´ı tlaˇc´ıtko, jehoˇz stisknut´ım si studenti mohou vyˇza ´dat transport do druh´e stanice. Pokud bude v˚ uz jiˇz ˇcekat ve stanici, studenti nastoup´ı a v˚ uz odjede. Pokud je v˚ uz na cestˇe, mus´ı studenti poˇckat, aˇz v˚ uz dojede do opaˇcn´e stanice, nastoup´ı pˇr´ıpadn´ı cestuj´ıc´ı a v˚ uz s nimi pˇrijede. Pokud ˇcek´ a v˚ uz v opaˇcn´e stanici, odjede a vezme studenty, kteˇr´ı stiskli tlaˇc´ıtko. Jinak bude v˚ uz ˇcekat ve stanici, dokud si jej nˇekdo stisknut´ım tlaˇc´ıtka nevyˇza ´d´ a 1. Prvn´ı krok: v´ ybˇ er objekt˚ u a akc´ı. Pˇrezkoum´ ame podstatn´ a jm´ena, kter´ a se v tomto popisu vyskytuj´ı, a tak urˇc´ıme objekty n´ avrhu. Zde by mohly pˇripadat v u ´vahu tyto entity: univerzita, budova, v˚ uz, studenti, pˇredn´ aˇsky, transport, stanice. Budova, univerzita, stanice, studenti, pˇredn´ aˇsky a transport nesouvis´ı pˇr´ımo s modelem a proto je ponech´ ame stranou. N´ as se bezprostˇrednˇe t´ yk´ a pouze v˚ uz a tlaˇc´ıtko. D´ ale si vˇsimneme akc´ı, kter´e se t´ ykaj´ı zvolen´ ych objekt˚ u, tedy sloves. P˚ ujde o slovesa stisknout (t´ yk´ a se tlaˇc´ıtka), pˇrijet a odjet (t´ ykaj´ı se vozu). Zam´ıtneme nastoupit, nebot’ se t´ yk´ a pˇredevˇs´ım student˚ u, a ˇcekat, nebot’ pˇredstavuje sp´ıˇse stav neˇz akci. (Pˇri dalˇs´ı anal´ yze se objev´ı jako pˇr´ıznak stavu, spolu s moˇznost´ı transport“, ” tedy v˚ uz je na cestˇe“.) Vyˇza ´dat si znamen´ a zde tot´eˇz co stisknout (tlaˇc´ıtko). ” Poznamenejme, ˇze pozdˇeji m˚ uˇzeme dospˇet k z´ avˇeru, ˇze potˇrebujeme urˇcit´e objekty a akce pˇridat. Pokud bychom napˇr. chtˇeli, aby n´ aˇs program tak´e sledoval, kolik student˚ u tuto dopravu vyuˇz´ıv´ a, museli bychom v naˇs´ı anal´ yze vz´ıt v u ´vahu i objekt student a akci nastoupit. Druh´ y krok: specifikace struktury objekt˚ u. V˚ uz vyjede na poˇca ´tku z prvn´ı stanice, pak opakovanˇe pˇrej´ıˇzd´ı mezi prvn´ı a druhou stanic´ı a skonˇc´ı opˇet v prvn´ı stanici. Pˇrej´ıˇzdˇen´ı mezi stanicemi se skl´ ad´ a z pˇr´ıjezdu do i-t´e stanice a z odjezdu z i-t´e stanice. V diagramu to vyznaˇc´ıme indexem i. Jedin´ a akce, kter´ a se t´ yk´ a tlaˇc´ıtka, je stisknut´ı. Tato akce se bude samozˇrejmˇe opakovat. Struktur´ aln´ı diagramy zvolen´ ych objekt˚ u vid´ıte na obr. 10.7. 1 Nenechte se zm´ ast skuteˇcnost´ı, ˇze by se u n´ as nˇeco podobn´eho nemohlo st´ at. Existuj´ı st´ aty, kde maj´ı univerzity dokonce prostˇredky na to, aby se snaˇzily student˚ um usnadnit ˇzivot, a st´ at to nepokl´ ad´ a za d˚ uvod k drastick´ ym u ´sporn´ ym opatˇren´ım.
156
´ ˇ Y ´ NA ANALYZE ´ ˇ ˚ KAPITOLA 10. NAVRH ARCHITEKTURY ZALOZEN POZADAVK U
tlaˇc´ıtko
v˚ uz
odjezd(1)
v˚ uz-tˇelo
stisknut´ı=
pˇr´ıjezd(1)
stanice(i)∗
pˇr´ıjezd(i)
odjezd(i)
Obr. 10.7: Struktur´ aln´ı diagramy pro objekty v˚ uz a tlaˇc´ıtko
tlaˇc´ıtko-0
DT
tlaˇc´ıtko-1
v˚ uz-0
SV
v˚ uz-1
Obr. 10.8: Struktur´ aln´ı diagramy pro v˚ uz a tlaˇc´ıtko Tento diagram pˇredstavuje ˇcasovˇe uspoˇra ´dan´e akce, kter´e se t´ ykaj´ı zvolen´ ych objekt˚ u. Je-li tˇreba, m˚ uˇzeme jej doprovodit vysvˇetlivkami a pozn´ amkami - napˇr. index i sm´ı nab´yvat pouze hodnot 1 a 2. Tˇ ret´ı krok: vytvoˇ ren´ı poˇ c´ ateˇ cn´ıho modelu. Nyn´ı propoj´ıme model s re´ aln´ ym svˇetem“. Proces tlaˇc´ıtko-1 ” m˚ uˇze ˇc´ıst data (´ udaje o stisknut´ı tlaˇc´ıtka) z vyrovn´ avac´ı pamˇeti. Na druh´e stranˇe proces v˚ uz -1 mus´ı m´ıt pˇr´ıstup k okamˇzit´ ym hodnot´ am pˇrep´ınaˇc˚ u, kter´e ˇr´ıd´ı funkci skuteˇcn´eho vozu. To znamen´ a, ˇze bude pˇr´ımo ˇc´ıst data ze stavov´eho vektoru vozu-0. Pˇri jeho zpracov´ an´ı naraz´ıme na ˇ ´ ´I a TRANZIT, kter´e vyjadˇruj´ı okamˇzit´ pˇr´ıznaky CEK AN y stav vozu. V obou pˇr´ıpadech mus´ıme neust´ ale kontrolovat, zda nedoˇslo ke zmˇenˇe stavu. Syst´emov´e specifikace pro zkoumanou kyvadlovou dopravu, ke kter´ ym jsme doposud dospˇeli, vid´ıte na obr´ azku 10.8. Textov´ y popis pro proces tlaˇc´ıtko-1 bude m´ıt tvar TLAˇ C´ ITKO-1 c ˇti DT; I-tˇ elo: itr pokud DT STISKNUT´ I STISKNUT´ c ˇti DT; I-tˇ elo: konec STISKNUT´ ITKO-1: konec C´ TLAˇ Tento textov´ y popis struktury tlaˇc´ıtko-1 plnˇe odpov´ıd´ a struktur´ aln´ımu diagramu; nav´ıc specifikuje vztah k re´ aln´emu svˇetu (ˇcten´ı dat DT prostˇrednictv´ım vyrovn´ avac´ı pamˇeti) a upˇresˇ nuje podm´ınku iterace. Podobnˇe pop´ıˇseme strukturu v˚ uz -1 (viz struktur´ aln´ı diagram na obr. 10.9).
10.2. JACKSONOVA METODA
157
v˚ uz ˇcek´ani(1) tˇelo ˇcek´an´ı(1)∗
odjezd(1)
tranzit(1) tˇelo
v˚ uz-tˇelo
pˇr´ıjezd(1)
tranzit(1)∗ stanice(i)=
pˇr´ıjezd(i)
ˇcek´an´ı(i) tˇelo ˇcek´an´ı(i)∗
odjezd(i)
tranzit(i) tˇelo tranzit(i)∗
Obr. 10.9: Struktur´ aln´ı diagram pro objekt v˚ uz po dalˇs´ım zpˇresnˇen´ı V˚ UZ-1 seq c ˇti SV; // c ˇten´ ı stavov´ eho vektoru ˇ CEK´ AN´ I-tˇ elo: itr pokud ˇ CEKEJ1 c ˇti SV; ˇ CEK´ AN´ I-tˇ elo: konec ODJEZD(1); TRANZIT-tˇ elo1: itr pokud TRANZIT1 c ˇti SV; TRANZIT-tˇ elo1: konec V˚ UZ-tˇ elo1 itr STANICE seq Pˇ R´ IJEZD(i); ˇ CEK´ AN´ I-tˇ elo: itr pokud ˇ CEKEJi c ˇti SV; ˇ CEK´ AN´ I-tˇ elo: konec ODJEZD(i); TRANZIT-tˇ elo itr pokud TRANZITi c ˇti SV; TRANZIT-tˇ elo: konec STANICE: konec V˚ UZ-tˇ elo: konec Pˇ R´ IJEZD(1); V˚ UZ-1: konec ˇ Ctvrt´ y krok: specifikace funkc´ı. Ve voze jsou sign´ aln´ı svˇetla, kter´ a se rozsv´ıt´ı pˇri pˇr´ıjezdu do i-t´e stanice. Pˇredpokl´ adejme, ˇze k rozsv´ıcen´ı resp. zhasnut´ı slouˇz´ı funkce ZAPSV(i) resp. VYPSV(i). Pˇr´ıkazy k rozsv´ıcen´ı ˇci zhasnut´ı tˇechto svˇetel mus´ı d´ at proces v˚ uz -1. To znamen´ a, ˇze struktur´ aln´ı diagram tohoto procesu uprav´ıme zp˚ usobem, kter´ y vid´ıte na obr. 10.10. Vedle toho se mus´ıme postarat o pˇr´ıkazy pro motory. Zavedeme proto nov´ y funkˇcn´ı proces motory, kter´ y bude napojen na proces v˚ uz -1. Pˇr´ısluˇsn´ y datov´ y tok oznaˇc´ıme V1D. Pˇr´ıkazy pro motory budou m´ıt tvar napˇr. START a STOP. Pˇr´ıkaz STOP je tˇreba vydat v okamˇziku, kdy senzory ozn´ am´ı pˇr´ıjezd do stanice, a start po prvn´ım stisknut´ı tlaˇc´ıtka, jestliˇze v˚ uz ˇcek´ a ve stanici. Samozˇrejmˇe je nezbytn´e zabezpeˇcit, aby proces v˚ uz-1 ˇcetl stavov´ y vektor vozu a aby proces motory ˇcetl
158
´ ˇ Y ´ NA ANALYZE ´ ˇ ˚ KAPITOLA 10. NAVRH ARCHITEKTURY ZALOZEN POZADAVK U
v˚ uz-0
SV
v˚ uz-1
pˇr´ıkazy pro svˇetla
Obr. 10.10: Upraven´ y struktur´ aln´ı diagram pro objekt v˚ uz (s dostateˇcnou frekvenc´ı opakov´ an´ı) informace o moˇzn´em pˇr´ıjezdu do stanice, aby stihl v˚ uz vˇcas zastavit. T´ım ovˇsem pˇredb´ıh´ ame - ˇcasov´e aspekty budeme rozeb´ırat v n´ asleduj´ıc´ım kroku. Pod´ıvejme se na zpˇresnˇen´ y popis vozu (zmˇeny oznaˇc´ıme pro snazˇs´ı orientaci ˇcten´ aˇre vykˇriˇcn´ıkem): V˚ UZ-1 seq ZAPSV(1); // ! c ˇti SV; // c ˇten´ ı stavov´ eho vektoru ˇ CEK´ AN´ I-tˇ elo: itr pokud ˇ CEKEJ1 c ˇti SV; ˇ CEK´ AN´ I-tˇ elo: konec VYPSV(1); // ! ODJEZD(1); TRANZIT-tˇ elo1: itr pokud TRANZIT1 c ˇti SV; TRANZIT-tˇ elo1: konec V˚ UZ-tˇ elo1 itr STANICE seq Pˇ R´ IJEZD(i); zapiˇ s pˇ rı ´jezd do V1D // ! data pro motory ZAPSV(i); // ! ˇ CEK´ AN´ I-tˇ elo: itr pokud ˇ CEKEJi c ˇti SV; ˇ CEK´ AN´ I-tˇ elo: konec VYPSV(i) // ! ODJEZD(i); TRANZIT-tˇ elo itr pokud TRANZITi c ˇti SV; TRANZIT-tˇ elo: konec STANICE: konec V˚ UZ-tˇ elo: konec Pˇ R´ IJEZD(1); zapiˇ s pˇ rı ´jezd do V1D V˚ UZ-1: konec Nakonec se jeˇstˇe vr´ at´ıme k procesu tlaˇc´ıtko. Nyn´ı je nezbytn´e rozliˇsovat mezi prvn´ım stisknut´ım, kter´e znamen´ a opravdu poˇzadavek na pˇrivol´ an´ı vozu, a mezi dalˇs´ımi stisknut´ımi, kter´ a jsou jiˇz bezv´ yznamn´ a, nebot’ v˚ uz je jiˇz na cestˇe. Pop´ıˇseme tedy tento proces znovu, podrobnˇeji, a nov´ y popis oznaˇc´ıme tlaˇc´ıtko-2. Proces motory informuje proces tlaˇc´ıtko o vyˇr´ızen´ı poˇzadavku, tj. o tom, ˇze v˚ uz pˇrijel do stanice. Zde ale mus´ıme opˇet rozliˇsovat mezi pˇr´ıjezdem vyˇza ´dan´ ym stisknut´ım tlaˇc´ıtka a pˇr´ıjezdem nevyˇza ´dan´ ym ( nav´ıc“, tj. ” pˇrejezdem, vyˇza ´dan´ ym v opaˇcn´e stanici). Zpˇresnˇen´ y popis tlaˇc´ıtka ukazuje diagram na obr. 10.11. Textov´ y popis tlaˇc´ıtka m˚ uˇze m´ıt tvar TLAˇ C´ ITKO-2: seq poˇ zadavek := ne; // z´ apis do stavov´ eho vektoru c ˇti DT a DM; // nav´ ıc c ˇte data motoru TLAˇ C´ ITKO-tˇ elo: itr
10.2. JACKSONOVA METODA
159
tlaˇc´ıtko-2 skupina stisknut´ı pˇr´ıjezd nav´ıc-tˇelo pˇr´ıjezd∗ nav´ıc
stisknut´ı -poˇzadavek
stisknut´ı nav´ıc-tˇelo
pˇr´ıjezd
stisknut´ı∗ nav´ıc
Obr. 10.11: Zpˇresnˇen´ y struktur´ aln´ı diagram pro objekt tlaˇc´ıtko SKUPINA-STISKNUT´ I: seq Pˇ R´ IJEZD-NAV´ IC-tˇ elo: itr pokud (Pˇ R´ IJEZD) c ˇti DM a DT; Pˇ R´ IJEZD-NAV´ IC-tˇ elo: konec STISKNUT´ I-POˇ ZADAVEK: seq poˇ zadavek := ano; c ˇti DT a DM; STISKNUT´ I-POˇ ZADAVEK: konec ´ STISKNUTI-NAV´ IC: itr pokud (Pˇ R´ IJEZD) c ˇti DT a DM; STISKNUT´ I-NAV´ IC: konec ˇ ´ PRIJEZD seq poˇ zadavek := ne; c ˇti DT a DM; Pˇ R´ IJEZD: konec SKUPINA-STISKNUT´ I: konec TLAˇ C´ ITKO-tˇ elo: konec TLAˇ C´ ITKO-2: konec Vstup procesu tlaˇc´ıtko-2 se skl´ ad´ a ze dvou datov´ ych proud˚ u - od skuteˇcn´eho tlaˇc´ıtka a od motor˚ u. Zde postaˇc´ı hrub´e slouˇcen´ı“ tˇechto proud˚ u (proces ˇcte ta data, kter´ a m´ a pr´ avˇe k disposici); existuj´ı ovˇsem i jin´e zp˚ usoby ” zpracov´ an´ı v´ıce vstupn´ıch proud˚ u - viz [14]. Vz´ ajemn´e propojen´ı tˇechto proces˚ u vid´ıte na obr. 10.12. P´ at´ y krok: urˇ cen´ı ˇ casov´ ych vztah˚ u. Zde mus´ıme urˇcit napˇr´ıklad okamˇzik, ve kter´em je tˇreba vydat pˇr´ıkaz STOP, a to v z´ avislosti na rychlosti vozu a na kvalitˇe brzd. D´ ale mus´ıme stanovit doby odezvy pˇri pˇrepnut´ı svˇetel apod. Jejich hodnoty budou samozˇrejmˇe z´ aviset na technick´ ych parametrech pouˇzit´ ych zaˇr´ızen´ı. Je zˇrejm´e, ˇze se program bude skl´ adat z nˇekolika paralelnˇe bˇeˇz´ıc´ıch proces˚ u. Z proveden´e anal´ yzy ale plyne, ˇze nen´ı tˇreba zav´ adˇet ˇza ´dn´e speci´ aln´ı synchronizaˇcn´ı mechanismy. ˇ ym krokem, implementac´ı, se zde jiˇz zab´ Sest´ yvat nebudeme.
160
´ ˇ Y ´ NA ANALYZE ´ ˇ ˚ KAPITOLA 10. NAVRH ARCHITEKTURY ZALOZEN POZADAVK U
tlaˇc´ıtko-0
v˚ uz-0
DT
SV
tlaˇc´ıtko-1
v˚ uz-1
T1D
tlaˇc´ıtko-2
MD
T2D
V1D
motory
PS Obr. 10.12: Struktur´ aln´ı diagram pro v˚ uz a tlaˇc´ıtko
PM
Kapitola 11
Objektovˇ e orientovan´ y n´ avrh Objektovˇe orientovan´ y n´ avrh, podobnˇe jako napˇr. n´ avrh zaloˇzen´ y na Jacksonovˇe metodˇe, vytv´ aˇr´ı programovou reprezentaci re´ aln´eho svˇeta. V´ ysledkem je ovˇsem syst´em sloˇzen´ y z objekt˚ u, programov´ ych struktur, kter´e jsou - nebo sp´ıˇse mohou b´ yt - odrazem objekt˚ u re´ aln´eho svˇeta a kter´e modularizuj´ı z´ aroveˇ n informace i jejich zpracov´ an´ı (zat´ımco klasick´e“ metody modularizovaly pouze zpracov´ an´ı dat). ” Objekty navz´ ajem komunikuj´ı prostˇrednictv´ım zpr´ av, kter´e si pos´ılaj´ı. D´ ale si vysvˇetl´ıme, co to vlastnˇe znamen´ a.
11.1
Z´ akladn´ı pojmy objektovˇ e orientovan´ eho programov´ an´ı
Objektovˇe orientovan´e programov´ an´ı (OOP) vych´ az´ı z pˇredstavy objektu jako z´ akladn´ı programov´e struktury. Objekt v programu pˇredstavuje obvykle model nˇejak´e sloˇzky re´ aln´eho svˇeta. Z hlediska toku informac´ı v programu pˇredstavuje objekt zpravidla bud’ zdroj informac´ı nebo jejich pˇr´ıjemce. M˚ uˇze ovˇsem tak´e pˇredstavovat informaci samu o sobˇe. Objekt se skl´ ad´ a zpravidla z datov´e struktury, kter´ a b´ yv´ a soukrom´ a (nepˇr´ıstupn´ a jin´ ym sloˇzk´ am programu) a z operac´ı, kter´e lze s tˇemito daty prov´ adˇet. Sloˇzky datov´e struktury, kter´ a tvoˇr´ı objekt, obvykle oznaˇcujeme jako atributy 1 ; procedury, funkce a oper´ atory, kter´e implementuj´ı operace s daty, oznaˇcujeme jako metody. ˇ ast z metod m˚ Metody smˇej´ı pracovat s daty objektu. C´ uˇze b´ yt tak´e soukrom´ a. Kaˇzd´ y objekt m´ a sv´e rozhran´ı, pˇres kter´e pˇrij´ım´ a zpr´ avy. Kaˇzd´ a zpr´ ava vlastnˇe pˇredstavuje ˇza ´dost, aby objekt provedl nˇekterou z moˇzn´ ych operac´ı. Pokud objekt zpr´ avu pˇrijme, zavol´ a nˇekterou z metod. Zd˚ uraznˇeme, ˇze posl´ an´ım zpr´ avy - tedy zpravidla vol´ an´ım metody - ˇr´ık´ ame, kterou operaci m´ a objekt prov´est, neˇr´ık´ ame vˇsak, jak ji m´ a prov´est. To je vnitˇrn´ı z´ aleˇzitost objektu. T´ım, ˇze ˇca ´st objektu (obvykle vˇsechna data a ˇca ´st metod) oznaˇc´ıme za soukrom´e, dos´ ahneme ukryt´ı informace, nebo pˇresnˇeji ukryt´ı implementace. Podrobnosti implementace z˚ ustanou skryty pˇred ostatn´ımi ˇca ´stmi programu. V´ ysledkem je, ˇze k dat˚ um maj´ı pˇr´ıstup pouze urˇcit´e ˇca ´sti k´ odu. Jin´ ymi slovy, data objektu jsou programov´ ym k´ odem metod chr´ anˇena pˇred neopr´ avnˇen´ ym pouˇzit´ım. K´ od data identifikuje a zabezpeˇcuje opr´ avnˇen´e operace s nimi. Tento aspekt OOP b´ yv´ a obˇcas vyjadˇrov´ an metaforou o k´ odov´e zdi okolo kaˇzd´eho kousku dat a zn´ azorˇ nov´ an obr´ azky podobn´ ymi jako obr. 11.1. Objektov´ y n´ avrh vede k rozkladu programu na pˇrirozen´e moduly, kter´e l´epe neˇz jin´e konstrukce odpov´ıdaj´ı objekt˚ um re´ aln´eho svˇeta a shrnuj´ı jak data tak i akce, kter´e se k nim v´ aˇz´ı. 1 R´ ad bych ˇcten´ aˇre upozornil, ˇze v oblasti OOP vl´ adne v odborn´e literatuˇre neuvˇeˇriteln´ y terminologick´ y zmatek. N´ azvy podobn´ ych konstrukc´ı se mohou liˇsit nejen u r˚ uzn´ ych programovac´ıch jazyk˚ u, ale i u r˚ uzn´ ych autor˚ u. Typick´ ym pˇr´ıkladem m˚ uˇze b´ yt pr´ avˇe atribut: zde tento term´ın pouˇz´ıv´ ame ve smyslu ”datov´ a sloˇzka objektu”. V literatuˇre o programovac´ım jazyku Simula 67 se takto oznaˇcuj´ı datov´e sloˇzky spolu se sloˇzkami funkˇcn´ımi (tedy metodami) a napˇr. v dokumentaci k programovac´ımu jazyku Actor se tak oznaˇcuj´ı data, popisuj´ıc´ı okna programu (tedy nˇeco, co t´emˇeˇr v˚ ubec nesouvis´ı s OOP).
161
ˇ ORIENTOVANY ´ NAVRH ´ KAPITOLA 11. OBJEKTOVE
162
Objekt
Rozhran´ı
Data Zpr´ava
K´odov´a zed’
Akce k´odov´e zdi na z´akladˇe pˇrijat´e zpr´avy
Obr. 11.1: Objekt jako k´ odov´ a zed’ kolem kaˇzd´eho kusu dat
11.1.1
Tˇ r´ıda
Objekty v programu jsou pˇredstavuj´ı modely objekt˚ u re´ aln´eho svˇeta. Objekty v programu jsou instancemi 2 datov´ ych typ˚ u, kter´e se zpravidla oznaˇcuj´ı jako tˇr´ıdy (objektov´e typy). Tˇr´ıda jako datov´ y typ tedy pˇredstavuje abstrakci spoleˇcn´ ych vlastnost´ı jist´e tˇr´ıdy objekt˚ u re´ aln´eho svˇeta (proto se pro nˇe obˇcas pouˇz´ıv´ a ponˇekud matouc´ı oznaˇcen´ı abstraktn´ı datov´e typy). Ne kaˇzd´ y datov´ y typ, kter´ y vznikne abstrakc´ı spoleˇcn´ ych vlastnost´ı skupiny objekt˚ u vnˇejˇs´ıho svˇeta, lze ovˇsem povaˇzovat za objektov´ y typ. Jako objektov´e typy, tedy tˇr´ıdy, budeme oznaˇcovat datov´e typy, kter´e umoˇzn ˇuj´ı vyuˇz´ıvat n´ asleduj´ıc´ı tˇri vlastnosti: zapouzdˇren´ı, dˇediˇcnost a polymorfismus. Rozhl´edneme-li se po odborn´e literatuˇre, snadno zjist´ıme, ˇze tyto term´ıny - stejnˇe jako ˇrada dalˇs´ıch - je pouˇz´ıv´ ana r˚ uzn´ ymi autory v lehce odliˇsn´ ych v´ yznamech. Proto si zde uvedeme pouze nejobvyklejˇs´ı interpretace. Zapouzdˇ ren´ı Term´ınem zapouzdˇren´ı (encapsulation) oznaˇcujeme skuteˇcnost, ˇze v objektov´em typu definujeme datov´e sloˇzky spolu metodami - tedy operacemi, kter´e lze s datov´ ymi sloˇzkami tˇr´ıdy prov´ adˇet. Obvykle ovˇsem se pod zapouzdˇren´ım rozum´ı tak´e skuteˇcnost, ˇze nˇekter´e sloˇzky tˇr´ıdy mohou b´ yt soukrom´e, tj. ˇze k nim - kromˇe metod tˇr´ıdy - nemaj´ı pˇr´ıstup ˇza ´dn´e jin´e souˇca ´sti programu. Poznamenejme ale, ˇze moˇznost omezit pˇr´ıstup k nˇekter´ ym sloˇzk´ am instanc´ı nebo tˇr´ıd poskytuj´ı jen nˇekter´e programovac´ı jazyky (ˇca ´steˇcnˇe napˇr. Turbo Pascal od verze 6.0, v pln´e m´ıˇre napˇr. C++; naprosto chyb´ı napˇr. v Actoru, i kdyˇz to je ˇcistˇe ” objektov´ y“ jazyk). Omezen´ı pˇr´ıstupu k nˇekter´ ym sloˇzk´ am objekt˚ u umoˇzn ˇuje program´ atorovi pˇresnˇe vymezit, kdo m˚ uˇze mˇenit datov´e sloˇzky, a t´ım zabr´ anit nˇekter´ ym chyb´ am z nepozornosti. Vzhledem k tomu, ˇze pˇr´ıstup ke sloˇzk´ am m˚ uˇze ve znaˇcn´e m´ıˇre kontrolovat pˇrekladaˇc, m˚ uˇzeme tak zjistit ˇradu chyb jiˇz v dobˇe kompilace. 3 Poznamenejme, ˇze pr´ avˇe zapouzdˇren´ı je z´ akladem metafory o k´ odov´e zdi, kter´ a chr´ an´ı data pˇred neopr´ avnˇen´ ym pouˇzit´ım, zabezpeˇcuje opr´ avnˇen´e pouˇzit´ı a v pˇr´ıpadˇe potˇreby data tak´e identifikuje (ˇca ´st t´eto k´ odov´e zdi“, ” tedy nˇekter´e metody, mohou napˇr. poskytovat informace o typu instance). Protokol Soubor zpr´ av, kter´e m˚ uˇze tˇr´ıda jako celek nebo instance urˇcit´e tˇr´ıdy pˇrijmout, spolu s popisem odezev na tyto zpr´ avy, oznaˇcujeme jako protokol dan´e tˇr´ıdy. 2 V angliˇ ctinˇe znamen´ a slovo instance (vedle v´ yznam˚ u, zn´ am´ ych v ˇceˇstinˇe) tak´e pˇr´ıklad nebo pˇr´ıpad. V souvislosti s programovac´ımi jazyky a programov´ an´ım se pouˇz´ıv´ a ve v´ yznamu ”realizace abstraktn´ıho vzoru”. Oznaˇcuje napˇr. promˇennou (konstantu, form´ aln´ı parametr...) objektov´eho typu, typy nebo funkce, vytvoˇren´e podle ˇsablony (v C++) apod. 3 Dalˇ s´ı nezanedbatelnou v´ yhodou zapouzdˇren´ı je, ˇze ˇrada jmen (identifik´ ator˚ u) bude ukryta uvnitˇr tˇr´ıdy. Pˇri t´ ymov´e pr´ aci se t´ım sn´ aze vyhneme koliz´ım, kdy dva program´ atoˇri pouˇzij´ı t´ehoˇz jm´ena pro dvˇe r˚ uzn´e konstrukce v t´emˇze programu.
´ ´I POJMY OBJEKTOVE ˇ ORIENTOVANEHO ´ ´ ´I 11.1. ZAKLADN PROGRAMOVAN
163
Chceme-li nˇejakou tˇr´ıdu pouˇz´ıvat, staˇc´ı zn´ at jej´ı protokol; implementace metod, uloˇzen´ı dat apod. mohou uˇzivateli z˚ ustat skryty. ˇ Casto se v t´eto souvislosti hovoˇr´ı o kontraktu: Tˇr´ıda si na jedn´e stranˇe klade urˇcit´e poˇzadavky a na druh´e stranˇe, jsou-li tyto poˇzadavky splnˇeny, zavazuje se poskytovat urˇcit´e sluˇzby. (Podrobnˇejˇs´ı pov´ıd´ an´ı na toto t´ema najdeme napˇr. v Meyerovˇe knize [35].) Dˇ ediˇ cnost Dˇediˇcnost (inheritance) je vlastnost, kter´ a umoˇzn ˇuje snadno od jednoho objektov´eho typu - pˇredka - odvodit typ dalˇs´ı (potomka).4 Potomek zdˇed´ı vˇsechny vlastnosti pˇredka. To znamen´ a, ˇze bude m´ıt stejn´e atributy a stejn´e metody jako tˇr´ıda, od kter´e je odvozen. Obvykle vˇsak u potomka definujeme nˇekter´e dalˇs´ı datov´e sloˇzky nebo metody specifikujeme nˇejak´e dalˇs´ı vlastnosti. M˚ uˇzeme tak´e v potomkovi pˇrekr´ yt nˇekterou z metod pˇredka novou verz´ı. Rozhran´ı potomka proto automaticky obsahuje rozhran´ı pˇredka, m˚ uˇze vˇsak b´ yt ˇsirˇs´ı, nebot’ v potomkovi m˚ uˇzeme definovat nov´e metody. Implementace potomka v sobˇe obsahuje implementaci pˇredka. To znamen´ a, ˇze potomek m´ a vˇsechny vlastnosti pˇredka a nˇejak´e dalˇs´ı nav´ıc. Z toho ale plyne, ˇze odvozen´ a tˇr´ıda pˇredstavuje podtˇr´ıdu 5 tˇr´ıdy rodiˇcovsk´e. Pˇ r´ıklad 11.1 Vezmˇeme tˇr´ıdu lod’, kter´ a bude reprezentovat obecn´e plavidlo. Atributy tˇr´ıdy lod’ mohou b´ yt napˇr. okamˇzit´ a rychlost, smˇer, poloha, v´ytlak, d´elka, rok spuˇstˇen´ı na vodu. Metody tˇr´ıdy lod’ mohou b´ yt napˇr. zmˇen ˇ smˇer, zmˇen ˇ rychlost, zakotvi nebo potop se. Potomkem tˇr´ıdy lod’ m˚ uˇze b´ yt napˇr. tˇr´ıda plachetnice, kter´ a bude m´ıt nav´ıc atribut poˇcet stˇeˇzn ˇ˚ u, celkov´y poˇcet plachet a poˇcet vytaˇzen´ych plachet. Novou metodou m˚ uˇze b´ yt napˇr. vyt´ ahni plachtu, sviˇ n plachtu atd. u Jin´ ym potomkem tˇr´ıdy lod’ m˚ uˇze b´ yt napˇr. tˇr´ıda parn´ık, kter´ a bude m´ıt nav´ıc atributy poˇcet lodn´ıch ˇsroub˚ a z´ asoba uhl´ı a metody doplˇ n uhl´ı a houkej. Je zˇrejm´e, ˇze kaˇzd´ a plachetnice je lod’ - jin´ ymi slovy tˇr´ıda plachetnic je podtˇr´ıdou tˇr´ıdy lod´ı. Tˇr´ıdy plachetnice a parn´ık jsou maj´ı stejn´eho pˇredka - oznaˇcujeme je jako sourozence. U tˇechto tˇr´ıd se budou nejsp´ıˇs liˇsit metody plav, nebot’ parn´ık nepouˇz´ıv´ a plachty a plachetnice nem´ a parn´ı stroj. Jestliˇze je ale potomek podtˇr´ıdou - tedy vlastnˇe zvl´ aˇstn´ım pˇr´ıpadem - pˇredka, znamen´ a to, ˇze instanci odvozen´e tˇr´ıdy m˚ uˇzeme kdykoli pouˇz´ıt jako instanci rodiˇcovsk´e tˇr´ıdy. Odtud plyne, ˇze pro objektov´e typy mus´ı platit takov´ ato pravidla: • • •
Promˇenn´e typu ukazatel na rodiˇcovskou tˇr´ıdu lze pˇriˇradit hodnotu, kter´ a pˇredstavuje ukazatel na instanci odvozen´eho typu. Instanci rodiˇcovsk´eho typu lze pˇriˇradit hodnotu odvozen´eho typu. Ukazatel na instanci odvozen´eho typu lze pouˇz´ıt na m´ıstˇe form´ aln´ıho parametru typu ukazatel na rodiˇcovskou tˇr´ıdu a podobnˇe hodnotu odvozen´eho typu lze pouˇz´ıt jako skuteˇcn´ y parametr pˇri vol´ an´ı funkce nebo procedury, jej´ıˇz form´ aln´ı parametr je rodiˇcovsk´eho typu.
K tˇemto pravidl˚ um je ale tˇreba dodat, ˇze v mnoha programovac´ıch jazyc´ıch se pˇri pˇriˇrazen´ı hodnoty typu potomek instanci typu pˇredek se pˇrenesou pouze data, kter´ a lze v pˇredkovi uloˇzit, a tak se vlastnˇe hodnota typu potomek transformuje na hodnotu typu pˇredek. Podobn´e je to i pˇri pˇred´ av´ an´ı parametr˚ u hodnotou. Na druh´e stranˇe pˇri pˇriˇrazov´ an´ı ukazatel˚ u nebo pˇri pˇred´ av´ an´ı parametr˚ u odkazem k podobn´e zmˇenˇe nedojde. 4 Jestliˇ ze od typu (tˇr´ıdy) A odvod´ıme typ B, oznaˇcujeme typ A jako pˇredka, b´ azovou tˇr´ıdu nebo rodiˇcovskou tˇr´ıdu, typ B pak jako potomka, odvozenou tˇr´ıdu, dceˇrinnou tˇr´ıdu nebo podtˇr´ıdu. 5 Pozor na terminologick´ e zmatky: Obˇcas se setk´ ame s n´ azorem, ˇze podtˇr´ıda je pˇredek, nikoli potomek. Toto pojet´ı vych´ az´ı ze skuteˇcnosti, ˇze instance potomka obsahuje vˇzdy podobjekt, kter´ y je instanc´ı pˇredka. Proto se budeme radˇeji trem´ın˚ um podtˇr´ıda a nadtˇr´ıda vyh´ ybat.
164
ˇ ORIENTOVANY ´ NAVRH ´ KAPITOLA 11. OBJEKTOVE
Odvozen´ y typ m˚ uˇzeme pouˇz´ıt jako rodiˇcovsk´ y typ pro dalˇs´ı objektov´e typy; tak vznikne dˇedick´ a hierarchie tˇr´ıd. Pravidlo o tom, ˇze potomek m˚ uˇze vˇzdy zastoupit pˇredka, plat´ı i pro vˇsechny tˇr´ıdy v dˇedick´e hierarchii. To znamen´ a, ˇze pˇredka m˚ uˇze zastoupit i nepˇr´ım´ y potomek, tedy potomek, vzd´ alen´ y v dˇedick´e hierarchii o nˇekolik u ´rovn´ı. Vedle jednoduch´e dˇediˇcnosti, kdy odvozen´ y typ m˚ uˇze m´ıt pouze jednoho pˇredka, se m˚ uˇzeme setkat i s dˇediˇcnost´ı v´ıcen´ asobnou, kdy odvozen´ y typ m´ a dva nebo v´ıce pˇredk˚ u. V´ıcen´ asobn´ a dˇediˇcnost nen´ı bˇeˇznou souˇca ´st´ı objektovˇe orientovan´ ych programovac´ıch jazyk˚ u; najdeme ji napˇr. v C++. V´ıcen´ asobn´ a dˇediˇcnost umoˇzn ˇuje snadno popsat objekty, kter´e vznikly sloˇzen´ım nˇekolika (v podstatˇe rovnocenn´ ych) sloˇzek. Ve vˇetˇsinˇe pˇr´ıpad˚ u vˇsak nen´ı nezbytn´ a. Polymorfismus Polymorfismus znamen´ a v pˇrekladu mnohotvarost. V OOP t´ım vyjadˇrujeme skuteˇcnost, ˇze stejnou zpr´ avu m˚ uˇzeme poslat instanc´ım nˇekolika r˚ uzn´ ych tˇr´ıd, zpravidla ovˇsem tˇr´ıd ze stejn´e dˇedick´e hierarchie. Pˇritom typ pˇr´ıjemce nemus´ıme v okamˇziku odesl´ an´ı zpr´ avy zn´ at (a nemus´ı jej zn´ at pˇrekladaˇc v dobˇe pˇrekladu programu). Pˇr´ıjemce m˚ uˇze samozˇrejmˇe na pˇrijatou zpr´ avu reagovat r˚ uzn´ ym zp˚ usobem v z´ avislosti na sv´em skuteˇcn´em typu. Polymorfismus se uplatˇ nuje pˇredevˇs´ım v souvislosti s pravidlem, kter´e ˇr´ık´ a, ˇze potomek m˚ uˇze kdykoli zastoupit pˇredka (viz pˇredchoz´ı odstavec). Z nˇej totiˇz plyne, ˇze pˇri operac´ıch s instanc´ı nemus´ıme zn´ at jej´ı pˇresn´ y typ staˇc´ı vˇedˇet, ˇze patˇr´ı do urˇcit´e dˇedick´e hierarchie a tedy ˇze m˚ uˇze pˇrijmout danou zpr´ avu. Pˇ r´ıklad 11.2 V pˇredchoz´ım pˇr´ıkladu jsme zavedli tˇr´ıdu lod’ a dalˇs´ı odvozen´e tˇr´ıdy. V programu pouˇz´ıv´ ame proceduru AkceSLod´ı, jej´ımˇz form´ aln´ım parametrem pˇred´ avan´ ym odkazem je objekt jm´enem Nˇejak´ aLod’ typu lod’. To znamen´ a, ˇze skuteˇcn´ ym parametrem m˚ uˇze b´ yt jak instance typu plachetnice tak instance typu lod’ nebo parn´ık. V t´eto proceduˇre poˇsleme objektu Nˇejak´ aLod’ zpr´ avu plav. Pokud je typ lod’ polymorfn´ı, nemus´ıme se o typ skuteˇcn´eho parametru starat, pouˇzije se metoda odpov´ıdaj´ıc´ı skuteˇcn´emu typu instance. Jestliˇze tedy byla skuteˇcn´ ym parametrem instance typu parn´ık, budou se lodi ot´ aˇcet kolesa, zat´ımco pokud by byla skuteˇcn´ ym parametrem instance typu plachetnice, budou se na lodi tˇrepetat plachty. ˇ Casn´ a a pozdn´ı vazba Polymorfismus pˇredpokl´ ad´ a tzv. pozdn´ı vazbu. To znamen´ a, ˇze skuteˇcn´ y typ instance, kter´ a je pˇr´ıjemcem zpr´ avy, se vyhodnocuje aˇz pˇri bˇehu programu. Program pak ale mus´ı pˇri zpracov´ an´ı tohoto vol´ an´ı zpravidla prohled´ avat tabulky metod. To znamen´ a prodlouˇzen´ı k´ odu a zpomalen´ı bˇehu programu. Proto se ve vˇetˇsinˇe objektovˇe orientovan´ ych jazyk˚ u zpravidla implicitnˇe pouˇz´ıv´ a ˇcasn´ a vazba, pˇri kter´e se typ pˇr´ıjemce, a tedy tak´e volan´ a metoda, vyhodnot´ı jiˇz pˇri kompilaci. Pozdn´ı vazba se pouˇz´ıv´ a jen pro vybran´e metody, kter´e oznaˇcujeme (a deklarujeme) jako virtu´ aln´ı.6 V pˇredchoz´ım pˇr´ıkladu bychom tedy museli metodu plav deklarovat jak ve tˇr´ıdˇe lod’ tak i ve tˇr´ıd´ ach odvozen´ ych jako virtu´ aln´ı. Pozn´ amka: implementace virtu´ aln´ıch metod v Turbo Pascalu V t´eto pozn´ amce si pov´ıme, jak se implementuj´ı virtu´ aln´ı metody v Turbo Pascalu. V ˇradˇe implementac´ı jin´ ych programovac´ıch jazyk˚ u je postup podobn´ y, nepˇredstavuje vˇsak jedinou moˇznost. Pro kaˇzd´ y objektov´ y typ, kter´ y m´ a (nebo zdˇed´ı) alespoˇ n jednu virtu´ aln´ı metodu, zˇr´ıd´ı pˇrekladaˇc tabulku virtu´ aln´ıch metod (oznaˇcujeme ji tak´e zkratkou VMT, podle anglick´eho virtual method table). Tato tabulka bude obsahovat adresy vˇsech virtu´ aln´ıch metod tˇr´ıdy, ke kter´e patˇr´ı. program´ atorovi nen´ı pˇr´ımo dostupn´ a. 6 Slovo virtu´ aln´ı znamen´ a nejen zd´ anliv´ y, ale tak´e takov´ y, kter´ y m´ a schopnost nˇeco konat. (Slovn´ık spisovn´eho jazyka ˇcesk´eho, Academia 1989.) Odtud zˇrejmˇe poch´ az´ı oznaˇcen´ı virtu´ aln´ıch metod. (Nˇekteˇr´ı autoˇri, napˇr. B. Stroustrup, mu pˇripisuj´ı v´ yznam ”ˇr´ızen´ y pomoc´ı skryt´ ych ukazatel˚ u”.)
´ ´I POJMY OBJEKTOVE ˇ ORIENTOVANEHO ´ ´ ´I 11.1. ZAKLADN PROGRAMOVAN
165
Vedle toho do kaˇzd´e instance t´eto tˇr´ıdy uloˇz´ı pˇrekladaˇc ukazatel na VMT. Adresu VMT do tohoto ukazatele uloˇz´ı konstruktor. (Tak´e tento ukazatel nen´ı program´ atorovi pˇr´ımo dostupn´ y.) D˚ uleˇzit´e je, ˇze ukazatel na VMT je uloˇzen v instanc´ıch vˇsech typ˚ u v dan´e dˇedick´e hierarchii na stejn´em m´ıstˇe (v Turbo Pascalu je VMT uloˇzena za atributy, deklarovan´ ymi v prvn´ım ˇclenu dan´e objektov´e hierarchie, kter´ y obsahuje alespoˇ n jednu virtu´ aln´ı metodu; jin´e pˇrekladaˇce mohou VMT ukl´ adat napˇr. jako u ´plnˇe prvn´ı datovou sloˇzku instance). Pˇri vol´ an´ı virtu´ aln´ı metody se nejprve z dan´e instance vezme ukazatel na tabulku virtu´ aln´ıch metod. V tabulce virtu´ aln´ıch metod se pak vyhled´ a adresa volan´e metody a ta se koneˇcnˇe zavol´ a. Pod´ıvejme se na pˇr´ıklad. Vezmeme objektov´e typy A a B, deklarovan´e takto: type A = object i: integer; procedure p; virtual; procedure q; virtual; end; type B = object(A) j: integer; procedure p; virtual; procedure q; virtual; end; var bb: B; ua: ^A; { ... } ua := @bb; ua^.p;
{zde vol´ ame virtu´ aln´ ı metodu}
Promˇenn´e ua typu ukazatel na A m˚ uˇzeme pˇriˇradit adresu instance bb typu B, kter´ y je potomkem typu A. Pˇri vol´ an´ı virtu´ aln´ı procedury p se nejprve v instanci uaˆ z´ısk´ a ukazatel na VMT (t´ım se vlastnˇe urˇc´ı skuteˇcn´ y typ t´eto instance, na kterou ua ukazuje). V nalezen´e tabulce virtu´ aln´ıch metod se pak vyhled´ a adresa procedury p a ta se zavol´ a. Viz t´eˇz obr. 11.1.
11.1.2
Sloˇ zky instanc´ı a sloˇ zky tˇ r´ıd7
Atributy instanc´ı Jiˇz jsme si ˇrekli, ˇze datov´e sloˇzky objekt˚ u naz´ yv´ ame atributy. Zat´ım jsme ovˇsem hovoˇrili pouze o atributech, kter´e jsou individu´ alnˇe vytv´ aˇreny pro kaˇzdou jednotlivou instanci; oznaˇcujeme je proto jako atributy instanc´ı. Atributy instanc´ı mohou m´ıt v kaˇzd´e z existuj´ıc´ıch instanc´ı jinou hodnotu, takˇze se hod´ı k vyjadˇrov´ an´ı individu´ aln´ıch vlastnost´ı r˚ uzn´ ych instanc´ı t´eˇze tˇr´ıdy. Vedle toho m˚ uˇze m´ıt tˇr´ıda jako celek sv´e vlastn´ı atributy, kter´e budeme oznaˇcovat jako atributy tˇr´ıdy. Jde o datov´e struktury, kter´e existuj´ı pouze jednou pro celou tˇr´ıdu a jsou spoleˇcn´e pro vˇsechny instance. (Atribut tˇr´ıdy je tedy vlastnˇe glob´ aln´ı promˇenn´ a, ukryt´ a uvnitˇr tˇr´ıdy.) Atributy tˇr´ıdy obvykle vyjadˇruj´ı skuteˇcnosti, spoleˇcn´e pro vˇsechny instance, a proto nejsou na ˇza ´dnou konkr´etn´ı instanci v´ az´ any. V programu mohou existovat i v pˇr´ıpadˇe, ˇze jsme od dan´e tˇr´ıdy dosud nevytvoˇrili ani jednu instanci. Pˇ r´ıklad 11.3 Z˚ ustaneme st´ ale u typu parn´ık, kter´ y jsme zavedli v pˇr´ıkladu 11.1 v t´eto kapitole, a pˇredpokl´ adejme, ˇze Vltava a Labe jsou dvˇe instance t´eto tˇr´ıdy. Atribut poˇcet lodn´ıch ˇsroub˚ u lodi Vltava m˚ uˇze m´ıt hodnotu 2, zat´ımco t´ yˇz atribut instance Labe m˚ uˇze m´ıt hodnotu 1. Tento atribut existuje pro kaˇzdou instanci zvl´ aˇst’ a vyjadˇruje individu´ aln´ı vlastnosti jednotliv´ ych lod´ı. 7 Turbo Pascal nab´ ız´ı program´ atorovi pouze atributy a metody instanc´ı. Atributy a metody tˇr´ıdy najdeme napˇr. v C++ nebo v ”ˇcistˇe objektov´ ych” jazyc´ıch, jako je Actor nebo Smalltalk (metody tˇr´ıdy jsou tak´e souˇca ´st´ı Object Pascalu v borlandsk´em produktu Delphi). Poznamenejme, ˇze C++ se atributy a metody tˇr´ıd oznaˇcuj´ı jako ”statick´e”.
ˇ ORIENTOVANY ´ NAVRH ´ KAPITOLA 11. OBJEKTOVE
166
instance tˇr´ıdy A i
i
@VMT
@VMT
instance tˇr´ıdy B, kter´a je potomkem A i
VMT tˇr´ıdy A
A.p A.q
Virtu´aln´ı metoda tˇr´ıdy A
procedura A.p
@VMT j
B.p B.q
procedura B.p
Virtu´aln´ı metoda tˇr´ıdy B
Obr. 11.2: Implementace pozdn´ı vazby v Turbo Pascalu u a bude evidovat Vedle toho m˚ uˇzeme definovat atribut tˇr´ıdy parn´ık, kter´ y se bude jmenovat poˇcet parn´ık˚ aktu´ aln´ı poˇcet parn´ık˚ u. Tato promˇenn´ a bude na poˇca ´tku - pˇred vytvoˇren´ım prvn´ı instance - m´ıt hodnotu 0. Po zkonstruov´ an´ı kaˇzd´e z instanc´ı se hodnota tohoto atributu zv´ yˇs´ı o 1, po zruˇsen´ı instance (d´ ame parn´ık do ˇsrotu) se jeho hodnota zmenˇs´ı o 1. Metody instanc´ı a metody tˇ r´ıd Dosud jsme hovoˇrili pouze o metod´ ach, kter´e pˇredstavuj´ı odezvu na zpr´ avu poslanou instanci, a tedy pracuj´ı s jednotliv´ ymi instancemi. Takov´e metody oznaˇcujeme jako metody instanc´ı. Metody instanc´ı vol´ ame vˇzdy pro urˇcitou konkr´etn´ı instanci a zavolan´ a metoda pak pracuje s atributy dan´e instance. Metody instanc´ı mohou samozˇrejmˇe vedle atribut˚ u instanc´ı pouˇz´ıvat i atribut˚ u tˇr´ıdy. V urˇcit´ ych situac´ıch je ale tˇreba poslat zpr´ avu tˇr´ıdˇe jako celku, nebot’ tˇr´ıda pln´ı mj. u ´lohu spr´ avce sv´ ych instanc´ı. Odezvy na takov´eto zpr´ avy budou pak implementovat metody tˇr´ıdy, metody, kter´e jsou sdruˇzeny s tˇr´ıdou jako celkem a kter´e nepracuj´ı s ˇza ´dnou konkr´etn´ı instanc´ı. Nemohou proto pouˇz´ıvat atribut˚ u instanc´ı; smˇej´ı ale pracovat s atributy tˇr´ıdy. Metody tˇr´ıdy lze volat i tehdy, kdyˇz ˇza ´dn´ a instance dan´e tˇr´ıdy neexistuje. Jako typick´ y pˇr´ıklad m˚ uˇze poslouˇzit metoda, kter´ a za bˇehu programu zkonstruuje novou instanci dan´e tˇr´ıdy. Instance, kterou chceme vytvoˇrit, jeˇstˇe neexistuje (nemus´ı existovat v˚ ubec ˇza ´dn´ a instance dan´e tˇr´ıdy), takˇze j´ı ˇza ´dnou zpr´ avu poslat nem˚ uˇzeme8 . Adresujeme tedy zpr´ avu tˇr´ıdˇe jako celku; tato zpr´ ava bude vyjadˇrovat ˇza ´dost, aby tˇr´ıda vytvoˇrila novou instanci.
11.1.3
Pozn´ amka k pouˇ z´ıv´ an´ı dˇ ediˇ cnosti
Skuteˇcnost, ˇze pˇredek je souˇca ´st´ı potomka, tedy ˇze instance odvozen´e tˇr´ıdy vˇzdy obsahuje podobjekt, kter´ y je instanc´ı pˇredka, m˚ uˇze sv´ adˇet k nevhodn´emu uˇzit´ı dˇediˇcnosti. Pod´ıvejme se na tˇr´ıdu plachetnice, definovanou v pˇr´ıkladu 11.2 Souˇca ´st´ı plachetnice jsou i plachty - v programu pro nˇe definujeme zvl´ aˇstn´ı objektov´ y typ, kter´ y v´ ystiˇznˇe pojmenujeme plachta. Co kdybychom definovali plachetnici jako potomka typu plachta? 8 Nenechte se zm´ ast skuteˇcnost´ı, ˇze napˇr. v Turbo Pascalu instanci nejprve deklarujeme a pak zavol´ ame konstruktor, kter´ y se tv´ aˇr´ı jako metoda instanc´ı. Deklarace pouze vyhrad´ı voln´e m´ısto, o kter´em nelze jeˇstˇe dost dobˇre hovoˇrit jako o objektu. Teprve konstruktor udˇel´ a z vyhrazen´eho m´ısta objekt.
ˇ ORIENTOVANY ´ NAVRH ´ 11.2. OBJEKTOVE
167
Takto definovan´ a tˇr´ıda plachetnice by jistˇe mohla fungovat. Mˇela by ale ˇradu neˇza ´douc´ıch vlastnost´ı, kter´e by byly pˇr´ım´ ym d˚ usledkem logick´e chyby v n´ avrhu: plachetnice nen´ı plachta. Pro instance tˇr´ıdy plachta jistˇe m´ a smysl volat metody napni se, tˇrepetej se apod. Kdybychom definovali plachetnici jako potomka plachty, mohli bychom volat tyto metody i pro lodˇe, coˇz zjevnˇe ned´ av´ a smysl. Dalˇs´ı probl´em, na kter´ y bychom narazili: co kdyˇz bude plachetnice m´ıt v´ıce plachet? Bˇeˇzn´e programovac´ı jazyky nedovoluj´ı, aby tˇr´ıda mˇela nˇekolik stejn´ ych pˇredk˚ u. To znamen´ a, ˇze bychom jednu plachtu zdˇedili a ostatn´ı museli definovat jako atributy; jedna plachta na lodi by tedy mˇela privilegovan´e postaven´ı oproti ostatn´ım 9 , coˇz obvykle neodpov´ıd´ a skuteˇcnosti. Pˇri n´ avrhu tˇr´ıdy plachetnice nem´ a smysl pˇren´ aˇset na ni rozhran´ı tˇr´ıdy plachta. Tˇr´ıda plachetnice bude vyuˇz´ıvat vlastnost´ı plachty, ale bude to lod’, nikoli plachta. Proto definujeme plachtu jako atribut; budeme-li cht´ıt napnout plachty na plachetnici, poˇsleme j´ı zpr´ avu napni plachty a plachetnice na z´ akladˇe toho poˇsle vˇsem sv´ ym placht´ am zpr´ avu napni se. (Moˇzn´ a, ˇze pˇritom vezme v u ´vahu informace o s´ıle vˇetru a napne jen nˇekter´e - to z´ avis´ı na implementaci tˇr´ıdy plachetnice.) Z pˇredchoz´ıho v´ ykladu plyne, ˇze: • •
Plachetnice je lod’ - m´ a tedy smysl definovat tˇr´ıdu plachetnice jako potomka tˇr´ıdy lod’. Plachetnice nen´ı plachta, plachetnice m´ a plachtu. Plachtu m´ a tedy smysl definovat jako atribut tˇr´ıdy plachetnice.
• Vztah potomka k pˇredkovi se nˇekdy oznaˇcuje anglick´ ym term´ınem isa (rozloˇzeno is a, tj. je ˇc´ımsi). Potomek je zvl´ aˇstn´ım pˇr´ıpadem pˇredka. • Vztah tˇr´ıdy k atributu se potom oznaˇcuje term´ınem hasa (has a, tj. m´ a cosi). Tˇr´ıda m´ a atribut, ale nen´ı jeho zvl´ aˇstn´ım pˇr´ıpadem - vyuˇz´ıv´ a pouze jeho sluˇzeb. Atribut poskytuje (nˇekter´e) sv´e sluˇzby dan´e tˇr´ıdˇe. Pozn´ amka v pozn´ amce: soukrom´ı pˇ redkov´ e V nˇekter´ ych programovac´ıch jazyc´ıch (m´ am na mysli opˇet zejm´ena sv´e obl´ıben´e C++) m˚ uˇzeme pˇri deklaraci odvozen´e tˇr´ıdy urˇcit, zda bude pˇredek soukrom´ y nebo veˇrejnˇe pˇr´ıstupn´ y (veˇrejn´ y). Veˇrejnˇe pˇr´ıstupn´e sloˇzky veˇrejn´eho pˇredka budou veˇrejn´e i v potomkovi; to znamen´ a, ˇze potomek zdˇed´ı jak rozhran´ı tak i implementaci (a jde tedy o dˇediˇcnost, jak jsme ji popsali v odstavci 11.1.1.). Specifikujeme-li pˇredka jako soukrom´eho, budou vˇsechny zdˇedˇen´e sloˇzky, jak atributy tak i metody, v potomkovi soukrom´e. V tomto pˇr´ıpadˇe potomek z´ısk´ av´ a implementaci, nikoli vˇsak rozhran´ı. Pokud chceme, aby nˇekter´e sloˇzky soukrom´eho pˇredka byly veˇrejnˇe pˇr´ıstupn´e, mus´ıme je v potomkovi explicitnˇe zveˇrejnit. Postaven´ı soukromˇe zdˇedˇen´eho pˇredka je proto sp´ıˇse podobn´e postaven´ı atributu (hasa). Potomek m´ a pˇredka, ale nechlub´ı se s n´ım.
11.2
Objektovˇ e orientovan´ y n´ avrh
Objektovˇe orientovan´ y n´ avrh vych´ az´ı z objektov´e anal´ yzy zad´ an´ı. Jeden z moˇzn´ ych postup˚ u pˇri objektovˇe orientovan´em n´ avrhu se skl´ ad´ a z n´ asleduj´ıc´ıch krok˚ u: 1. Definujeme probl´em. 2. Sestav´ıme seznam poˇzadavk˚ u. 3. Na z´ akladˇe poˇzadavk˚ u navrhneme neform´ alnˇe architekturu (vyvineme neform´ aln´ı strategii) softwarov´eho modelu probl´emu z re´ aln´eho svˇeta“. ” 9 To nen´ ı jen metafora. V nˇekter´ ych programovac´ıch jazyc´ıch se m˚ uˇze liˇsit zach´ azen´ı se zdˇed´enou sloˇzkou od zach´ azen´ı s atributem napˇr. v konstruktoru pˇri inicializaci nebo v destruktoru pˇri likvidaci instance. Napˇr. v C++ konstruktor nejprve inicializuje zdˇrdˇrn´e podobjekty (vol´ a se jejich konstruktory), pak inicializuje odkazy na VMT v dan´e instanci a teprve pak vol´ a konstruktory atribut˚ u dan´e instance. To znamen´ a, ˇze ”zdˇedˇen´ a” plachta by byla inicializov´ ana v jin´em prostˇred´ı neˇz plachty, deklarovan´e jako atributy. Stˇeˇz´ı si lze pˇredstavit rozumnou situaci, kde bychom nˇeco takov´eho potˇrebovali.
ˇ ORIENTOVANY ´ NAVRH ´ KAPITOLA 11. OBJEKTOVE
168
4. Architekturu postupnˇe formalizujeme a zpˇresˇ nujeme v n´ asleduj´ıc´ıch kroc´ıch: (a) Urˇc´ıme objekty a jejich atributy. (b) Urˇc´ıme operace, kter´e mohou objekty prov´ adˇet nebo kter´e lze na nˇe aplikovat. (c) Urˇc´ıme rozhran´ı objekt˚ u tak, ˇze vyˇsetˇr´ıme vztahy mezi objekty a operacemi. (d) Tam, kde je to vhodn´e, uplatn´ıme dˇediˇcnost. (e) Podrobnou anal´ yzou dojdeme k n´ avrhu implementace jednotliv´ ych objekt˚ u. 5. Rekurzivn´ım opakov´ an´ım krok˚ u 3, 4 a 5 dokonˇc´ıme n´ avrh. Pod´ıvejme se nyn´ı na tento postup podrobnˇeji. Neform´ aln´ı popis architektury Prvn´ı tˇri kroky pˇri objektovˇe orientovan´em n´ avrhu se v podstatˇe neliˇs´ı od pˇredchoz´ıch, neobjektov´ ych“ pos” tup˚ u. Zaˇcneme u specifikace probl´emu a pak na z´ akladˇe popisu pˇredbˇeˇzn´ ych poˇzadavk˚ u urˇc´ıme neform´ aln´ı strategii implementace. D´ ale budeme muset urˇcit tˇr´ıdy, kter´e k ˇreˇsen´ı dan´eho probl´emu pouˇzijeme, a jejich atributy a metody. Objekty a tˇ r´ıdy Pˇri formalizaci strategie mus´ıme na z´ akladˇe anal´ yzy poˇzadavk˚ u stanovit objekty a jejich tˇr´ıdy, kter´e budeme v programu pouˇz´ıvat. Pˇri urˇcov´ an´ı tˇr´ıd a jejich instanc´ı si m˚ uˇzeme vypomoci t´ım, ˇze v popisu odpov´ıdaj´ı objekt˚ um zpravidla podstatn´ a jm´ena nebo fr´ aze s v´ yznamem podstatn´ ych jmen 10 . Pˇri urˇcov´ an´ı, co bude tˇr´ıda a co bude instance, si m˚ uˇzeme pomoci t´ım, ˇze obecn´ a podstatn´ a jm´ena, pouˇzit´ a v popisu, budou zpravidla pˇredstavovat tˇr´ıdy objekt˚ u, zat´ımco konkr´etn´ı podstatn´ a jm´ena budou oznaˇcovat instance. Pˇri rozliˇsov´ an´ı tˇechto kategori´ı se samozˇrejmˇe mus´ıme op´ırat o skuteˇcn´ y v´ yznam v dan´em kontextu (a ledacos si domyslet). Jakmile jsme v popisu probl´emu vyhledali vˇsechna podstatn´ a jm´ena, sestav´ıme tabulku objekt˚ u. V n´ı vyznaˇc´ıme, zda jde o objekt v prostoru probl´emu (tedy v re´ aln´em svˇetˇe) nebo v prostoru ˇreˇsen´ı (tedy v programu), a pˇr´ıpadnˇe pˇripoj´ıme struˇcn´ y koment´ aˇr. Pˇri zjemˇ nov´ an´ı ˇreˇsen´ı se m˚ uˇze st´ at, ˇze nˇekter´e objekty vylouˇc´ıme jako nadbyteˇcn´e nebo nesouvisej´ıc´ı s probl´emem. Na druh´e stranˇe ˇcasto mus´ıme do n´ avrhu pˇridat objekty nebo tˇr´ıdy, kter´e se z popisu probl´emu nedaly bezprostˇrednˇe odvodit, ale jejichˇz potˇreba vyplynula z anal´ yzy probl´emu. Atributy Pˇri urˇcov´ an´ı vlastnost´ı objekt˚ u si m˚ uˇzeme pomoci t´ım, ˇze v popisu strategie vyˇsetˇr´ıme pˇr´ıdavn´ a jm´ena a mluvnick´e vazby s podobn´ ym v´ yznamem a urˇc´ıme, ke kter´ ym objekt˚ um se vztahuj´ı. Vlastnostem budou zpravidla ˇ odpov´ıdat atributy instanc´ı. Casto je ovˇsem tˇreba k atribut˚ um, popisuj´ıc´ım fyzik´ aln´ı a jin´e vlastnosti pˇredmˇetu z re´ aln´eho svˇeta, pˇridat dalˇs´ı atributy, kter´e usnadn´ı softwarovou realizaci. Ty vˇsak obvykle specifikujeme aˇz pˇri podrobn´em n´ avrhu. Metody D´ ale mus´ıme urˇcit akce, kter´e mohou objekty prov´ adˇet, a operace, kter´e na nˇe m˚ uˇzeme aplikovat. Je jasn´e, ˇze v neform´ aln´ım popisu strategie jim budou odpov´ıdat slovesa a slovesn´e fr´ aze. Pˇritom budeme br´ at v u ´vahu i predik´ aty (tvrzen´ı jako je menˇs´ı neˇz cosi“), nebot’ tak´e odpov´ıdaj´ı operac´ım s objekty - zjiˇst’uj´ı jejich ” vlastnosti. Operace pˇripoj´ıme do tabulky k objekt˚ um. Pˇritom se m˚ uˇze st´ at, ˇze se urˇcit´ a operace vztahuje k v´ıce objekt˚ um. Jak potom urˇcit, ke kter´emu ji pˇripojit? Jako dostateˇcn´e vod´ıtko obvykle poslouˇz´ı n´ asleduj´ıc´ı pravidlo: operace bude souˇca ´st´ı (metodou) toho objektov´eho typu, jehoˇz soukrom´e souˇca ´sti vyuˇz´ıv´ a. 10 Odvol´ avky na slovn´ı druhy jsou samozˇrejmˇe jen pom˚ uckou, kter´ a m˚ uˇze usnadnit pr´ aci. N´ avrh samozˇrejmˇe nelze vytvoˇrit (jen) na z´ akladˇe slovn´ıho rozboru zad´ an´ı.
ˇ ORIENTOVANY ´ NAVRH ´ 11.2. OBJEKTOVE
169
Jestliˇze naraz´ıme na operaci, kter´ a vyˇzaduje pˇr´ıstup k soukrom´ ym ˇca ´stem nˇekolika tˇr´ıd, znamen´ a to nejsp´ıˇs, ˇze jsme udˇelali chybu ve specifikaci rozhran´ı nˇekter´ ych tˇr´ıd (typ˚ u) nebo ve specifikaci operace. V´ ysledkem t´eto f´ aze anal´ yzy je tabulka objekt˚ u a operac´ı. Pˇritom kaˇzd´emu objektu by mˇela odpov´ıdat alespoˇ n jedna operace a kaˇzd´ a operace by mˇela odpov´ıdat nˇejak´emu typu. Pokud se stane, ˇze se nˇejak´eho objektu nebude t´ ykat ˇza ´dn´ a operace, nebo ˇze nˇejakou operaci nebude moˇzno pˇridˇelit ˇza ´dn´emu z objekt˚ u, m˚ uˇze to znamenat, ˇze •
neform´ aln´ı strategie je ne´ upln´ a a chyb´ı v n´ı nˇejak´ y objekt nebo operace s n´ım;
•
objekt nebo operaci, kter´ a patˇr´ı do prostoru ˇreˇsen´ı, jsme zaˇradili do prostoru probl´emu nebo naopak;
•
pˇrehl´edli jsme, ˇze nˇekter´ a operace, uveden´ a v tabulce, vyˇzaduje znalost osamˇel´eho“ objektu; ”
•
neform´ aln´ı strategie nen´ı pops´ ana dobˇre - r˚ uzn´e ˇca ´sti popisu jsou na r˚ uzn´e u ´rovni abstrakce.
V kaˇzd´em pˇr´ıpadˇe to znamen´ a, ˇze se mus´ıme vr´ atit k popisu strategie a opravit jej. Rozhran´ı: komunikace mezi objekty Jakmile zn´ ame operace, kter´e lze s objekty prov´ adˇet, urˇc´ıme zpr´ avy, kter´e si mohou objekty navz´ ajem pos´ılat. To znamen´ a, ˇze definujeme vztahy mezi metodami a zpr´ avami, kter´e metody volaj´ı. Zde jiˇz podrobnosti mohou z´ aviset i na konvenc´ıch pouˇzit´eho programovac´ıho jazyka. (Pˇredstavu komunikace modul˚ u pomoc´ı zpr´ av lze vyuˇz´ıt i v neobjektov´em n´ avrhu - usnadn´ı napˇr. testov´ an´ı n´ avrhu pomoc´ı sc´en´ aˇr˚ u.) Podrobn´ a anal´ yza Podrobn´ y n´ avrh, vyuˇz´ıvaj´ıc´ı OOP, je v mnoha ohledech velice podobn´ y ostatn´ım technik´ am n´ avrhu. Navrhneme rozdˇelen´ı programu do hlavn´ıch modul˚ u. D´ ale vyjdeme od podrobn´eho popisu rozhran´ı; zjemˇ nujeme a zpˇresˇ nujeme datov´e struktury; navrhneme algoritmy pro jednotliv´e moduly programu. Objektovˇe orientovan´ y n´ avrh ovˇsem umoˇzn ˇuje kdykoli rekurzivnˇe aplikovat v´ yˇse uveden´ y postup, nebot’ objekt na urˇcit´e u ´rovni abstrakce se m˚ uˇze skl´ adat z dalˇs´ıch objekt˚ u, kter´e zabezpeˇcuj´ı jeho funkˇcnost, podobnˇe jako operace (metoda) se m˚ uˇze skl´ adat z ˇrady jednoduˇsˇs´ıch operac´ı. ˇ Casto se uplatˇ nuje takov´eto pravidlo: Jestliˇze implementace urˇcit´e operace vyˇzaduje pˇr´ıliˇs velk´e mnoˇzstv´ı k´ odu (ˇreknˇeme nad 200 ˇra ´dk˚ u - to samozˇrejmˇe nen´ı z´ avazn´ a hodnota), vezmeme jej´ı popis jako nov´e zad´ an´ı a opakujeme v´ yˇse popsan´ y proces. Testov´ an´ı n´ avrhu V tomto st´ adiu m˚ uˇzeme pˇredbˇeˇznˇe testovat schopnost produktu vyhovˇet poˇzadavk˚ um, kter´e se na nˇej kladou. Pouˇz´ıvaj´ı se k tomu sc´en´ aˇre“, kter´ y se skl´ ad´ a ze zpr´ av pos´ılan´ ych objekt˚ um. Pro r˚ uzn´e u ´rovnˇe abstrakce, a ” tedy r˚ uzn´e u ´rovnˇe podrobnosti n´ avrhu, je samozˇrejmˇe tˇreba pouˇz´ıt r˚ uzn´e sc´en´ aˇre. Dˇ ediˇ cnost Dˇediˇcnost umoˇzn ˇuje opakovan´e pouˇz´ıv´ an´ı jiˇz hotov´eho k´ odu. Na moˇznost vyuˇzit´ı dˇediˇcnosti naraz´ıme jak pˇri n´ avrhu shora dol˚ u tak i pˇri cestˇe opaˇcn´e. Pˇri postupu shora dol˚ u“ pˇrech´ az´ıme od abstraktnˇejˇs´ıch, m´enˇe specifick´ ych pojm˚ u (napˇr. lod’ ) k pojm˚ um ” konkr´etnˇejˇs´ım, kter´e pˇresnˇeji urˇcuj´ı vlastnosti objekt˚ u (napˇr. plachetnice). Pˇri definici potomka, odvozen´e tˇr´ıdy, si mus´ıme vˇsimnout, kter´e metody m˚ uˇze potomek zdˇedit a kter´e je tˇreba pˇrekr´ yt novou verz´ı ( pˇredefinovat“). ” M˚ uˇze se st´ at, ˇze si pˇri zjemˇ nov´ an´ı n´ avrhu vˇsimneme spoleˇcn´ ych operac´ı a/nebo spoleˇcn´ ych dat v nˇekolika tˇr´ıd´ ach; v takov´em pˇr´ıpadˇe se m˚ uˇzeme pokusit spojit je v n´ avrhu nov´e tˇr´ıdy. Nˇekdy se stane, ˇze objekty
ˇ ORIENTOVANY ´ NAVRH ´ KAPITOLA 11. OBJEKTOVE
170
(instance) takto vznikl´e tˇr´ıdy nemaj´ı v programu ˇza ´dn´ y v´ yznam - jedin´ ym smyslem takov´e tˇr´ıdy je, ˇze poskytuje spoleˇcn´ a data a metody sv´ ym potomk˚ um. Takov´eto tˇr´ıdy oznaˇcujeme jako abstraktn´ı. 11 V pˇr´ıkladu 11.1 na zaˇca ´tku t´eto kapitoly bude lod’ abstraktn´ı tˇr´ıda, nebot’ nem´ a smysl pouˇz´ıvat v programu obecnou lod’; vˇzdy to bude pouze parn´ık nebo plachetnice.
11.2.1
Pˇ r´ıklad: jednoduch´ y grafick´ y editor
Poˇc´ıtaˇcov´ a grafika patˇr´ı mezi nejjednoduˇsˇs´ı aplikace OOP. Proto se s tˇemito pˇr´ıklady setk´ ame t´emˇeˇr v kaˇzd´e publikaci, kter´ a se o OOP alespoˇ n zmiˇ nuje. Tento text nebude v´ yjimkou. Na n´ asleduj´ıc´ım pˇr´ıkladu si uk´ aˇzeme postupn´e zjemˇ nov´ an´ı n´ avrhu. Definice probl´ emu: Vytvoˇrit jednoduch´ y grafick´ y editor. Specifikace poˇ zadavk˚ u: Editor m´ a umoˇznit manipulovat na obrazovce s dvourozmˇern´ymi ˇcarami, kuˇzeloseˇckami a aproximaˇcn´ımi kˇrivkami. Uˇzivatel bude pomoc´ı myˇsi (nebo jin´eho ukazov´ atka“) pˇrem´ıst’ovat a ot´ aˇcet ” grafick´e objekty, mˇenit jejich velikost a barvu. Tato specifikace je velice neurˇcit´ a. Uˇzivatelsk´e rozhran´ı aplikace je specifikov´ ano jen zhruba, o v´ ystupu na jin´ a zaˇr´ızen´ı, o ukl´ ad´ an´ı vytvoˇren´ ych obr´ azk˚ u apod. se v nˇem nehovoˇr´ı v˚ ubec. (Vˇsechny tyto probl´emy si nyn´ı dovol´ıme velkoryse pominout.) Lze ji vˇsak povaˇzovat neform´ aln´ı popis strategie implementace. Naznaˇcuje, ˇze obr´ azek se m´ a skl´ adat z jednotliv´ ych primitivn´ıch“ objekt˚ u, se kter´ ymi lze samostatnˇe manipulovat. ” Zpˇresˇ nov´ an´ı a formalizaci strategie provedeme v nˇekolika pr˚ uchodech. 1. zjemnˇ en´ı Urˇcen´ı tˇr´ıd: Mezi podstatn´ ymi jm´eny v popisu poˇzadavk˚ u m˚ uˇzeme vynechat uˇzivatele a obrazovku, nebot’ se bezprostˇrednˇe net´ ykaj´ı implementace samotn´e - sp´ıˇse urˇcuj´ı jej´ı okolnosti. Tak´e myˇs je na t´eto u ´rovni anal´ yzy nezaj´ımav´ a. Editor bude tˇr´ıda, kter´ a bude m´ıt na starosti uˇzivatelsk´e rozhran´ı a kter´ a bude obsahovat seznam existuj´ıc´ıch grafick´ ych objekt˚ u. Nebudeme se s n´ı zde zab´ yvat; za prv´e zad´ an´ı neobsahuje bliˇzˇs´ı specifikaci a za druh´e pˇr´ıklad bude i tak dosti dlouh´ y. Zb´ yvaj´ı n´ am ˇca ´ry, kuˇzeloseˇcky a aproximaˇcn´ı kˇrivky. Ty lze ale vˇsechny na nejvyˇsˇs´ı u ´rovni shrnout pod oznaˇcen´ı grafick´y objekt. Dalˇs´ı podstatn´ a jm´ena, se kter´ ymi se v popisu setk´ av´ ame, velikost, poloha a barva, vyjadˇruj´ı vlastnosti objekt˚ u. Grafick´y objekt (GO) bude tedy pˇredstavovat abstraktn´ı tˇr´ıdu, kter´ a ponese vlastnosti spoleˇcn´e vˇsem tˇr´ıd´ am. (Vzhledem k tomu, ˇze zat´ım uvaˇzujeme o jedin´e tˇr´ıdˇe, nem´ a smysl poˇrizovat tabulku objekt˚ u.) Vyhled´ an´ı atribut˚ u jednotliv´ ych tˇ r´ıd: GO je grafick´ y objekt na obrazovce poˇc´ıtaˇce. Je tedy zˇrejm´e, ˇze bude m´ıt barvu a polohu. Barva bude urˇcena jedn´ım cel´ ym ˇc´ıslem, poloha dvojic´ı cel´ ych ˇc´ısel (souˇradnic referenˇcn´ıho bodu, napˇr. stˇredu, na obrazovce). Dalˇs´ı vlastnosti, kter´e pˇripadaj´ı v u ´vahu, jsou orientace (vypl´ yv´ a z poˇzadavku na ot´ aˇcen´ı objekt˚ u) a velikost. Z poˇzadavk˚ u je zˇrejm´e, ˇze tyto vlastnosti budeme potˇrebovat. Jak orientace tak i velikost budou urˇceny jedn´ım cel´ ym ˇc´ıslem. Urˇ cen´ı operac´ı s jednotliv´ ymi objekty (metod): Operace odvozujeme od sloves. Podle souvislosti ale mus´ıme ˇcasto pˇridat i operace, o kter´ ych se v poˇzadavc´ıch pˇr´ımo nehovoˇr´ı. GO budeme pˇrem´ıst’ovat, ot´ aˇcet a mˇenit jejich velikost. Vedle toho mus´ıme samozˇrejmˇe m´ıt moˇznost GO vytvoˇrit a zruˇsit (odstranit). Prvn´ı tˇri operace pˇredstavuj´ı vlastnˇe zmˇenu nˇekter´eho z atribut˚ u GO. Pˇri pr´ aci 11 V nˇ ekter´ ych programovac´ıch jazyc´ıch - napˇr. v C++ - je term´ın abstraktn´ı tˇr´ıda pouˇz´ıv´ an pro tˇr´ıdy, kter´e maj´ı alespoˇ n jednu ˇ cistˇ e virtu´ aln´ı metodu, tedy metodu, kterou (zhruba ˇreˇceno) sice deklarujeme, ale neimplementujeme. Takov´ a metoda pouze ”drˇz´ı m´ısto” pro metody stejn´eho jm´ena, kter´e mus´ıme implementovat v odvozen´ ych tˇr´ıd´ ach. Podrobnˇejˇs´ı informace o ˇcistˇe virtu´ aln´ıch metod´ ach v C++ najdete napˇr. v [18]. Souˇcasn´e verze Turbo Pascalu ˇcistˇe virtu´ aln´ı metody neznaj´ı.
ˇ ORIENTOVANY ´ NAVRH ´ 11.2. OBJEKTOVE
171
s GO mus´ıme umˇet v programu tak´e z´ıskat informace o GO - pˇresnˇeji grafick´ y objekt mus´ı umˇet vr´ atit informaci o sv´e velikosti, poloze a orientaci. Pˇrehled potˇrebn´ ych metod m˚ uˇzeme uspoˇra ´dat do n´ asleduj´ıc´ı tabulky: Metoda Vytvoˇr GO Zruˇs GO Nastav polohu Zjisti polohu Nastav orientaci Zjisti orientaci Nastav velikost Zjisti velikost Nastav barvu Zjisti barvu
V´ yznam Vytvoˇr´ı grafick´ y objekt Zruˇs´ı grafick´ y objekt Nastav´ı souˇradnice x,y referenˇcn´ıho bodu GO Zjist´ı souˇradnice x,y referenˇcn´ıho bodu GO Nastav´ı orientaci GO Zjist´ı orientaci GO Nastav´ı velikost GO Zjist´ı velikost GO Nastav´ı barvu GO Zjist´ı barvu GO
Tato tabulka vlastnˇe definuje protokol tˇr´ıdy GO. Komunikace mezi objekty (rozhran´ı): Pˇredpokl´ adejme pro urˇcitost, ˇze pouˇzijeme Turbo Pascal. Potom m˚ uˇzeme prvn´ı dvˇe zpr´ avy implementovat jako konstruktor a destruktor. Destruktor bude bez parametr˚ u; parametry konstruktoru budou hodnoty atribut˚ u nov´eho objektu. Vzhledem k tomu, ˇze objekty budou urˇcitˇe vytv´ aˇreny a ruˇseny dynamicky, pouˇzijeme vol´ an´ı konstruktoru v proceduˇre New a vol´ an´ı destruktoru v procuduˇre Dispose. Metody Zjisti velikost, Zjisti barvu a Zjisti orientaci budou funkce bez parametr˚ u, vracej´ıc´ı velikost, barvu a orientaci grafick´eho objektu12 . Metody Nastav velikost, Nastav barvu a Nastav orientaci budou procedury s jedn´ım parametrem, vyjadˇruj´ıc´ım odpov´ıdaj´ıc´ı veliˇciny. Metoda Nastav polohu bude procedura s parametrem (parametry), vyjadˇruj´ıc´ım polohu objektu na obrazovce; metoda Zjisti polohu bude nejsp´ıˇs - vzhledem k omezen´ım Turbo Pascalu - tak´e procedura, jej´ıˇz parametr (parametry), popisuj´ıc´ı polohu, se budou pˇred´ avat odkazem. Test n´ avrhu pomoc´ı sc´ en´ aˇ re: Ovˇeˇr´ıme, zda kaˇzd´emu z poˇzadavk˚ u, kladen´ ych na n´ aˇs editor, odpov´ıd´ a nˇejak´ a zpr´ ava: Vytvoˇren´ı a zruˇsen´ı GO (ugo je ukazatel na GO): New(ugo, Vytvoˇ r_GO(parametry)); Dispose(ugo, Zruˇ s_GO); Otoˇcen´ı GO: ugo^.Nastav_orientaci(t); Zmˇena velikosti GO: ugo^.Nastav_velikost(v); Pˇresun GO: ugo^.Nastav_polohu(x,y); Zmˇena barvy: ugo^.Nastav_barvu(b); ´ Uvahy o dˇ ediˇ cnosti jsou zat´ım nem´ıstn´e, nebot’ na souˇcasn´e u ´rovni abstrakce m´ ame jedinou tˇr´ıdu. 12 V t´ eto f´ azi n´ avrhu zat´ım nehovoˇr´ıme o tom, jak budeme barvu, velikost, orientaci nebo polohu grafick´ ych objekt˚ u reprezentovat. Napˇr. barva je pro n´ as zat´ım prostˇe jak´ ysi abstraktn´ı typ, kter´ y popisuje barvu grafick´ ych objekt˚ u v navrhovan´em editoru.
ˇ ORIENTOVANY ´ NAVRH ´ KAPITOLA 11. OBJEKTOVE
172
GO
ˇca´ra
kuˇzeloseˇcka
aprox. kˇrivka
Obr. 11.3: Hierarchie tˇr´ıd na 2. u ´rovni zjemnˇen´ı 2. zjemnˇ en´ı Nyn´ı zopakujeme pˇredchoz´ı kroky a pˇritom zjemn´ıme rozliˇsen´ı typ˚ u grafick´ ych objekt˚ u. (Vlastnˇe tak postoup´ıme na dalˇs´ı u ´roveˇ n abstrakce.) ˇ ary, kuˇzeloseˇcky a aproximaˇcn´ı kˇrivky jsou r˚ Tˇ r´ıdy: C´ uzn´e druhy grafick´ ych objekt˚ u. Definujeme je tedy jako samostatn´e tˇr´ıdy, kter´e budou samozˇrejmˇe potomky tˇr´ıdy GO. ˇ aˇr se m˚ V dalˇs´ı anal´yze se budeme pro struˇcnost zab´yvat pouze kuˇzeloseˇckami. Cten´ uˇze pokusit dokonˇcit rozbor i pro zb´yvaj´ıc´ı dvˇe tˇr´ıdy. Atributy: Kuˇzeloseˇcka je obecnˇe pops´ ana kvadratickou rovnic´ı druh´eho stupnˇe ax2 + bxy + cy 2 + dx + ey + f = 0 Atributy kuˇzeloseˇcky tedy budou koeficienty a, . . . , f . Zmˇen´ı-li se nˇekter´ y z tˇechto koeficient˚ u, zmˇen´ı se kuˇzeloseˇcka - jin´ ymi slovy kaˇzd´ a kuˇzeloseˇcka mus´ı m´ıt svou vlastn´ı ˇsestici koeficient˚ u. To znamen´ a, ˇze p˚ ujde o atributy instanc´ı. V t´eto f´ azi n´ avrhu se jiˇz tak´e m˚ uˇzeme rozhodnout, jak implementujeme barvu (budeme ji reprezentovat cel´ ymi ˇc´ısly, v´ yˇctov´ ym typem ...) a dalˇs´ı atributy grafick´ ych objekt˚ u. Metody: K metod´ am, spoleˇcn´ ym vˇsem GO, mus´ıme pˇridat metody pro nastaven´ı a zjiˇstˇen´ı koeficient˚ u. Protoˇze tˇr´ıda kuˇzeloseˇcek m´ a tak´e jinou datovou strukturu neˇz tˇr´ıda GO, mus´ıme pro ni definovat zvl´ aˇstn´ı metody pro vytvoˇren´ı a zruˇsen´ı instance. V protokolu budou tedy nav´ıc tyto zpr´ avy: Metoda Nastav koeficienty Zjisti koeficienty Vytvoˇr kuˇzeloseˇcku Zruˇs kuˇzeloseˇcku
V´ yznam Nastav´ı koeficienty kuˇzeloseˇcky Zjist´ı koeficienty dan´e kuˇzeloseˇcky Konstruktor kuˇzeloseˇcky Destruktor kuˇzeloseˇcky
Rozhran´ı: Konstruktor koˇzeloseˇcky bude m´ıt stejn´e parametry jako konstruktor obecn´eho GO a k tomu nav´ıc parametry a, . . . , f . Tak´e metody Nastav koeficienty a Zjisti koeficienty budou m´ıt parametry a, . . . , f ; v pˇr´ıpadˇe metody Zjisti koeficienty je mus´ıme pˇred´ avat odkazem. Test pomoc´ı sc´ en´ aˇ re pˇrenech´ av´ ame ˇcten´ aˇri. Dˇ ediˇ cnost: Je zˇrejm´e, ˇze ˇca ´ra, kuˇzeloseˇcka a aproximaˇcn´ı kˇrivka budou tˇr´ıdy odvozen´e od GO. Mezi tˇemito tˇr´ıdami nem´ a smysl o dˇediˇcnosti uvaˇzovat. Souˇcasn´ y tvar dˇedick´e hierarchie 13 vid´ıte na obr. 11.3. 3. zjemnˇ en´ı D´ ale se pro jednoduchost budeme zab´ yvat pouze tˇr´ıdou kuˇzeloseˇcka. 13 V
diagramech, kter´e popisuj´ı dˇedick´e hierarchie objekt˚ u, je obvykl´e, ˇze ˇsipka smˇeˇruje od potomka k pˇredkovi.
ˇ ORIENTOVANY ´ NAVRH ´ 11.2. OBJEKTOVE
173
GO
ˇca´ra
kuˇzeloseˇcka
aprox. kˇrivka
hyperbola
elipsa
parabola
kruˇznice Obr. 11.4: Hierarchie tˇr´ıd na 3. u ´rovni zjemnˇen´ı Tˇ r´ıdy: Zad´ av´ an´ı kuˇzeloseˇcek pomoc´ı koeficient˚ u a, . . . , f nen´ı nejpohodlnˇejˇs´ı. K vyj´ adˇren´ı tvaru se sp´ıˇse pouˇz´ıvaj´ı jin´e parametry (napˇr. souˇradnice stˇredu, velikosti a orientace poloos apod.), kter´e se ovˇsem liˇs´ı podle druhu kˇrivky. To znamen´ a, ˇze tˇr´ıdu kuˇzeloseˇcka bude rozumn´e d´ ale rozdˇelit na podtˇr´ıdy kruˇznice, elipsa, parabola a hyperbola; degenerovan´e kuˇzeloseˇcky (napˇr. dvojici r˚ uznobˇeˇzek) nem´ a samozˇrejmˇe smysl br´ at v u ´vahu. D´ ale sv´e pov´ıd´ an´ı z´ uˇz´ıme pouze na kruˇznici, rozbor ostatn´ıch kuˇzeloseˇcek pˇrenech´ ame ˇcten´ aˇri. Atributy: Kruˇznice je urˇcena stˇredem a polomˇerem. Do tˇr´ıdy kruˇznice tedy pˇrid´ ame nov´ y atribut polomˇer. Metody: Protoˇze jsme pˇridali nov´ y atribut, budeme potˇrebovat i metody pro manipulaci s n´ım. Nazveme je uvodu definujeme tak´e nov´ y konstruktor a destruktor (Vytvoˇr kruˇzNastav polomˇer a Zjisti polomˇer. Z t´ehoˇz d˚ nici a Zruˇs kruˇznici ) a novou verzi metody pro urˇcen´ı koeficient˚ u. Rozhran´ı: Konstruktor kruˇznice bude m´ıt jako vstupn´ı parametry polohu stˇredu, barvu a polomˇer - vˇse celoˇc´ıseln´e hodnoty. Destruktor nebude m´ıt - podobnˇe jako u ostatn´ıch uvaˇzovan´ ych tˇr´ıd - ˇza ´dn´e parametry. Funkce Zjisti polomˇer bude bez parametr˚ u a bude vracet cel´e ˇc´ıslo; procedura Nastav polomˇer bude m´ıt jeden celoˇc´ıseln´ y parametr, a to hodnotu polomˇeru. Test pomoc´ı sc´ en´ aˇ re pˇrenech´ av´ ame opˇet ˇcten´ aˇri. Dˇ ediˇ cnost: Vzhledem k tomu, ˇze kruˇznici lze povaˇzovat za zvl´ aˇstn´ı pˇr´ıpad elipsy, m˚ uˇze b´ yt vhodn´e definovat tˇr´ıdu kruˇznice jako potomka tˇr´ıdy elipsa. V´ ysledkem bude hierarchie tˇr´ıd, zn´ azornˇen´ a na obr. 11.5. Nyn´ı bychom mohli pokraˇcovat ve zjemˇ nov´ an´ı: elipsa je vlastnˇe zvl´ aˇstn´ım pˇr´ıpadem eliptick´eho oblouku, podobnˇe jako tˇreba parabola je zvl´ aˇstn´ım pˇr´ıpadem parabolick´eho oblouku atd. D´ ale je tˇreba navrhnout algoritmy pro nakreslen´ı oblouku kuˇzeloseˇcky, implementovat je atd. Z hlediska v´ ykladu objektovˇe orientovan´eho n´ avrhu bychom se vˇsak nesetkali jiˇz s niˇc´ım nov´ ym.
174
ˇ ORIENTOVANY ´ NAVRH ´ KAPITOLA 11. OBJEKTOVE
Literatura [1] Wirth, N.: Algoritmy a ˇstrukt´ ury u ´dajov. Alfa, Bratislava 1987 [2] Horowitz, E. - Sartaj, S.: Fundamentals of Computer Algorithms. Pittman Publishing Ltd., 1978 [3] Knuth, D.E.: The Art of Computer Programming. Vol II, Seminumerical Algorithms. Addison Wesley Publishing Comp., 1969. [4] Knuth, D.E.: The Art of Computer Programming. Vol III, Sorting and Searching. Addison Wesley Publishing Comp., 1973. (Obˇe pˇredchoz´ı knihy, [3] a [4], existuj´ı t´eˇz v rusk´em pˇrekladu Iskusstvo programirovanija dlja EVM. Vydal Mir, Moskva 1978) [5] Sedgewick, R.: Algorithms in C++. Addison Wesley Publishing Comp., 1992. (Tato kniha existuje i ve verzi pro jazyky Pascal a C.) [6] Barron, D.W.: Rekurz´ıvne met´ ody v programovan´ı. Alfa, Bratislava 1973. [7] McConnell, S.: Code Complete. Microsoft Press, 1993. [8] Suchomel, J.: Technologie strukturovan´eho programov´ an´ı. Kancel´ aˇrsk´e stroje 1987. ˇ a kol.: Pohl’ad do dej´ın matematiky. Alfa, Bratislava 1986. [9] Zn´ am, S. ˇ ´ [10] CSN 36 4030. Znaˇcky v´ yvojov´ ych diagram˚ u pro syst´emy zpracov´ an´ı informac´ı. UNM, Praha 1974 [11] Kernighan, B.W. - Plauger, B.W.: The Elements of Programming Style. McGraw - HillBook Comp., 1978 [12] Pressman, R.S.: Software engineering.A Practitioner’s Approach. McGraw - Hill, 1987 [13] Maguire, S.: Writing Solid Code. Microsoft Press, 1993 [14] Jackson, M.A.: Principles of Program design. Academic Press, 1975 [15] Jackson, M. A.: System Development. Prentince Hall 1983 [16] Borland Visual Solutions Pack for Windows v.1.0. User’s Guide. Borland International Inc., 1993 [17] Kaˇsp´ arek, F. - Min´ arik, M. - Nikolov, V. - Pecinovsk´ y, R. - Virius, M.: Borland C++. Co v man´ alu nenajdete. Unis, Brno 1993 [18] Virius, M.: Programovac´ı jazyky C/C++. G-Comp, Praha 1992 ´ ˇ [19] Virius, M.: Z´ aklady programov´ an´ı (Uvod do Turbo Pascalu). CVUT, Praha 1991 (skriptum) [20] Stroustrup, B.: Design and Evolution of C++. Addison-Wesley, 1994 [21] Seige, V.: Kdy bude poraˇzen Gari Kasparov? Softwarov´e noviny 1/1994, str. 26 [22] Shell, D. L.: A Highspeed Sorting Procedure. Communications of the ACM, 2, No. 7 (1959), s. 30 [23] Williams, J.W.J.: Heapsort (Algorithm 232). Communications of the ACM, 7, No. 6 (1964), s. 347 [24] Hoare, C.A.R.: Quicksort. Comp. Journal, 5, No. 1 (1962), s. 10 [25] Hoare, C.A.R.: Proof of a Programm: FIND. Communications of the ACM, 13, No. 1 (1970), s. 39 ˇ [26] Reiterman, J.: Anal´ yza algoritm˚ u. CVUT Praha, 1991 (skriptum) 175
176
LITERATURA
[27] Aho, A.V. - Hopcroft, J.E. - Ullman, J.D.: The Design and Analysis of Computer Algorithms. Addison Wesley, Reading 1974; rusk´ y pˇreklad Mir, Moskva 1979 [28] Gilstadt, R. L.: Polyphase Merge Sorting - An Advanced Technique. Proc. AFIPS Eastern Jt. Comp. Conf., 18 (1960), 143 ˇ ´ [29] CSN 01 0181 Abecedn´ı ˇrazen´ı. Vydavatelstv´ı UNM, Praha 1977 [30] ANSI/IEEE std 754/1985: IEEE Standard for Binary Floating-Point Arithmetic. 1985 [31] Myers, G.: Composite Structured design. Van Nostrand, 1978 [32] Adelson-Velskij, G.M. - Landis, M.E.:Doklady Akademii nauk SSSR 146 (1962), 263. [33] Cooley, J.M. - Tukey, J.W.: An algorithm for the machine calculation of complex Fourier series. Math. Comp. 19, 1965, str. 297. ˇ [34] Val´ aˇsek, P.: Numerick´ y koprocesor 8087.CSVTS, Praha 1989 [35] Meyer, B.: Object-Oriented Software Construction. Prentince Hall, 1988
Rejstˇ r´ık u ´plnost poˇzadavk˚ u, 143 u ´roveˇ n vrcholu, 25 ˇc´ısla Fibonacciova, 59 ˇc´ısla cel´ a - znam´enkov´ a reprezentace, 116 ˇc´ısla denorm´ aln´ı, 121 ˇc´ısla normalizovan´ a, 120, 123 ˇc´ısla re´ aln´ a, 120 ˇc´ısla s pohybovou ˇra ´dovou ˇca ´rkou, 120 ˇc´ıslice, 115 ˇc´ıslice v´ yznamˇejˇs´ı, 115 ˇcelo fronty, 38 ˇrada Taylorova, 129 ˇretˇezec, 145 2-3-strom, 34
cyklus, 11, 154 cyklus v grafu, 43 d´elka cesty vnˇejˇs´ı, 26 d´elka cesty vnitˇrn´ı, 25 dˇelen´ı re´ aln´ ych ˇc´ısel, 124 data - z´ akon zachov´ an´ı, 144 datov´e struktury, 19, 144 definice probl´emu, 141 detekce chyb, 143 diagram Jackson˚ uv, 13, 154 diagram pro specifikaci syst´emu, 153 diagram toku, 151 diagram v´ yvojov´ y, 13, 123 doplˇ nkov´ y k´ od, 116
Actor, 148 adresa, 15 Algol, 12 algoritmus, 9, 144 algoritmus dˇelen´ı cel´ ych ˇc´ısel, 119 algoritmus hladov´ y, 46 algoritmus n´ asoben´ı cel´ ych ˇc´ısel, 119 algoritmus odeˇc´ıt´ an´ı cel´ ych ˇc´ısel, 118 algoritmus opaˇcn´eho ˇc´ısla, 118 algoritmus rekurzivn´ı, 12 algoritmus sˇc´ıt´ an´ı cel´ ych ˇc´ısel, 117 algoritmus Strassen˚ uv, 134 anal´ yza syntaktick´ a, 62 architektura - kontrola n´ avrh˚ u, 147 asembler, 148 atribut, 19 AVL-strom, 101
exces, 120 exponent, 120 f´ aze, 86 Fortran, 58 fronta, 38, 92 fronta kruhov´ a, 39 fronta s prioritami, 39 funkce Ackermannova, 63 funkce heˇsovac´ı, 40 funkce referenˇcn´ı, 16 Gauss, F.K., 52 graf orientovan´ y, 43, 95, 131 graf orientovan´ yu ´rovˇ nov´ y, 50 graf orientovan´ y ohodnocen´ y, 50 graf souvisl´ y, 43 grafu ohodnocen´ y, 43 grafu orientovan´ y ohodnocen´ y, 48
b-strom, 34 bˇeh, 86 bb-strom, 34
halda, 75 hlava seznamu, 19 hled´ an´ı s n´ avratem, 52 Hoare, 78, 83 Hornerovo sch´ema, 135, 137 hrana grafu, 43
C (jazyk), 58, 148 C++ (jazyk), 58, 148 cesta, 43 cesta nejkratˇs´ı, 48, 50 cesta orientovan´ a, 132 cesta vnˇejˇs´ı, 26 cesta vnitˇrn´ı, 25 chyba - detekce, 145 chyba - n´ aklady na opravu, 141 chyba - zpracov´ an´ı, 145 chyba relativn´ı, 125 chyba zaokrouhlovac´ı, 122 Cobol, 148
iInstance, 18 INF, 121, 122 iterace, 11, 12 Jackson, M.A., 153 jazyk, 12 jazyk - programovac´ı, 148 jazyk pro popis logick´ ych struktur, 154 177
ˇ ´IK REJSTR
178 jazyk pro popis program˚ u, 12 k´ od dolˇ nkov´ y, 120 k´ od pˇr´ım´ y, 116, 120 kl´ıˇc, 65 koˇren, 25 koˇs, 40 komponenta, 131 komponenta grafu, 43 konstanta Planckova, 120 kontrola poˇzadavk˚ u, 142 krit´erium D’Alembertova, 129 krok element´ arn´ı, 9 kvalifikace, 18 kvalita poˇzadavk˚ u, 143
postup shora dol˚ u, 12, 45 posun, 120, 128 princip optimality, 49 probl´em n dam, 53 probl´em osmi dam, 52 procesor, 9 programov´ an´ı dynamick´e, 49 prohled´ av´ an´ı do ˇs´ıˇrky, 55 projekt softwarov´ y, 141 promˇenn´ a, 15 promˇenn´ a dynamick´ a, 16 promˇenn´ a glob´ aln´ı, 16 promˇenn´ a lok´ aln´ı, 16 proud datov´ y, 142 Pseudopascal, 12
ladˇen´ı, 149 list, 25
quicksort, 78
mantisa, 120 matice incidenˇcn´ı, 43, 132 medi´ an, 79 metoda Jacksonova, 153 metoda shora dol˚ u, 10
rekurze, 45, 80 rekurze nepˇr´ım´ a, 58 rekurze pˇr´ım´ a, 58 robustnost, 146 rovnost re´ aln´ ych ˇc´ısel, 127 rozdˇel a panuj, 45, 79, 84, 137 rozhran´ı, 142
n´ asledovn´ık, 25 n´ asoben´ı matic, 134 n´ asoben´ı re´ aln´ ych ˇc´ısel, 124 n´ avrh - podrobn´ y, 148 n´ avrh architektury, 144 NaN, 121 normalizace, 119, 123 objektovˇe orientovan´ y n´ avrh, 144 obsah poˇzadavk˚ u, 142 odchylka smˇerodatn´ a, 127 odeˇc´ıt´ an´ı re´ aln´ ych ˇc´ısel, 123 ohon seznamu, 19 organizace programu, 144 pˇr´ıstup do souboru, 84 pˇredch˚ udce, 25 pˇrenos, 117–119 pˇreplnˇen´ı, 41 pˇreteˇcen´ı, 123 pˇrevod cel´ ych ˇc´ısel na re´ aln´ a, 124 parametr pˇred´ avan´ y hodnotou, 15 parametr pˇred´ avn´ y odkazemt, 16 Pascal, 58, 148 PDL, 12 poˇzadavky - anal´ yza, 151 poˇzadavky pˇredbˇeˇzn´e, 142 podm´ınˇen´ a operace, 11 podprogram, 11 podteˇcen´ı, 123 pole, 16 popis textov´ y, 154 posloupnost, 11 posloupnost operac´ı, 154 posloupnost zdrojov´ a, 65
sˇc´ıt´ an´ı cel´ ych ˇc´ısel, 123 sˇc´ıt´ an´ı modulo z k , 117 sˇc´ıt´ an´ı re´ aln´ ych ˇc´ısel, 123 sekvence, 11 selekce, 11, 154 sestava, 142 seznam, 19 seznam dvousmˇern´ y, 24 seznam jednosmˇern´ y, 20 seznam kruhov´ y, 24 seznam poˇzadavk˚ u, 142 Shell, D.L., 73 slovn´ık dat, 152 slovo stavov´eho matematick´eho koprocesoru, 121 souˇcet logick´ y, 131 soubor, 84 soubor objektov´ y typ, 87 soustava ˇc´ıseln´ a, 115 SQL, 148 stabilita tˇr´ıdˇen´ı, 92, 93 standardn´ı oˇsetˇren´ı z´ asobn´ıku, 38 strategie implementace, 144 strom, 25 strom n-´ arn´ı, 25 strom 2-3-strom, 34 strom bin´ arn´ı, 27, 105 strom bin´ arn´ı vyhled´ avac´ı, 107 strom dokonale vyv´ aˇzen´ y, 101, 105 strom porovn´ avac´ı, 74 strom stavov´ y, 53 strom typ, 25 strom vyv´ aˇzen´ y, 101 struktogram, 13
ˇ ´IK REJSTR struktura, 18 struktury datov´e, 15 struktury datov´e odvozen´e, 19 tˇr´ıdˇen´ı, 65 tˇr´ıdˇen´ı abecedn´ı, 94 tˇr´ıdˇen´ı bin´ arn´ı vkl´ ad´ an´ı, 68 tˇr´ıdˇen´ı bublinkov´e, 70 tˇr´ıdˇen´ı haldou, 74, 75 tˇr´ıdˇen´ı lexikografick´e, 93 tˇr´ıdˇen´ı pˇr´ım´ ym sluˇcov´ an´ım, 84 tˇr´ıdˇen´ı pˇr´ım´ ym v´ ybˇerem, 69 tˇr´ıdˇen´ı pˇr´ım´ ym vkl´ ad´ an´ı, 73 tˇr´ıdˇen´ı pˇr´ım´ ym vkl´ ad´ an´ım, 66 tˇr´ıdˇen´ı pˇretˇra ´s´ an´ım, 72 tˇr´ıdˇen´ı pˇrihr´ adkov´e, 91, 93 tˇr´ıdˇen´ı pˇrirozen´ ym sluˇcov´ an´ım, 86 tˇr´ıdˇen´ı polyf´ azov´e sluˇcov´ an´ı, 90 tˇr´ıdˇen´ı rozdˇelov´ an´ım, 78 tˇr´ıdˇen´ı rychl´e, 78 tˇr´ıdˇen´ı se zmenˇsov´ an´ım kroku, 73 tˇr´ıdˇen´ı Shellovo, 73 tˇr´ıdˇen´ı stabiln´ı, 68 tˇr´ıdˇen´ı stromov´e, 74 tˇr´ıdˇen´ı topologick´e, 94 tˇr´ıdˇen´ı v´ıcecestn´e sluˇcov´ an´ı, 90 tˇr´ıdˇen´ı vnˇejˇs´ı, 65, 84 tˇr´ıdˇen´ı vnitˇrn´ı, 65 tabulka, 39 tabulka heˇsov´ a, 40 tabulka virtu´ aln´ıch metod, 19 transformace Fourierova, 136 typ double, 121 typ double - uloˇzen´ı v pamˇeti, 122 typ extended - uloˇzen´ı v pamˇeti, 122 typ float, 121 typ long double, 121 typ objektov´ y, 18 typ real - uloˇzen´ı v pamˇeti, 122 typ single - uloˇzen´ı v pamˇeti, 121 typ stromu, 25 unie, 18 uspoˇra ´d´ an´ı ˇca ´steˇcn´e, 94 uspoˇra ´d´ an´ı lexikografick´e, 93 uz´ avˇer tranzitivn´ı, 132, 133 uzel, 25 uzel grafu, 43 v´ ykonnost, 146 v´ ypoˇcet hodnoty polynomu, 135 vektor pˇr´ıstupov´ y, 17 vektor stavov´ y, 153 Visual Basic, 148 VMT, 19 vrchol stromu, 25 vrchol vnitˇrn´ı, 25 vrchol zvl´ aˇstn´ı, 26 vyhled´ av´ an´ı bin´ arn´ı, 45
179 Williams, J., 75 z´ aklad ˇc´ıseln´e soustavy, 115 z´ akon asociativn´ı, 126 z´ akon distributivn´ı, 127 z´ akon komutativn´ı, 126 z´ apis polsk´ y obr´ acen´ y, 112 z´ asobn´ık, 36, 60, 80, 112 z´ aznam, 18 z´ aznam variantn´ı, 18 zach´ azen´ı se zmˇenami, 144 zar´ aˇzka, 20, 27, 67, 73 zpracov´ an´ı stromu pˇr´ım´e, 27 zpracov´ an´ı stromu vnitˇrn´ı, 27 zpracov´ an´ı stromu zpˇetn´e, 27