More actions
Bez shrnutí editace |
Bez shrnutí editace |
||
(14 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
Riadok 1: | Riadok 1: | ||
Vyjadrenie funkcie <math>T(n)</math> analyticky (nerekurzívne), určovanie výpočtovej zložitosti v rekurzívnych funkciách. | Vyjadrenie funkcie <math>T(n)</math> analyticky (nerekurzívne), geometrická postupnosť, určovanie výpočtovej zložitosti v rekurzívnych funkciách. | ||
{{Pojmová mapa}} | {{Pojmová mapa}} | ||
Riadok 27: | Riadok 27: | ||
Našťastie, pre tento prípad vieme rekurzívne funkcie vyjadriť analyticky (teda, nerekurzívne) – to znamená že pre dané <math>n</math> vieme vypočítať konkrétnu hodnotu bez toho, aby sme volali funkciu rekurzívne. | Našťastie, pre tento prípad vieme rekurzívne funkcie vyjadriť analyticky (teda, nerekurzívne) – to znamená že pre dané <math>n</math> vieme vypočítať konkrétnu hodnotu bez toho, aby sme volali funkciu rekurzívne. | ||
Pri analýze funkcie musíme postupovať od čísla <math>n</math> a postupne sa vnárať hlbšie a hlbšie do rekurzie. Pre príklad vyššie by sme postupovali takto: | |||
<math display="block">\begin{align} | |||
T(n) &= {\color{hotpink}T(n - 1)} + 5 \\ | |||
{\color{hotpink}T(n - 1)} &= ? \\ | |||
\end{align}</math> | |||
Ako všeobecne vyjadríme <math>{\color{hotpink}T(n - 1)}</math>? Tak, že zoberieme všeobecnú definíciu funkcie <math>T(n)</math> a namiesto <math>n</math> dosadíme <math>n - 1</math>: | |||
<math display="block">\begin{align} | |||
T(n) &= T({\color{firebrick}n} - 1) + 5 \\ | |||
{\color{hotpink}T({\color{green}n - 1})} &= T(\underbrace{\color{green}n - 1}_{\color{firebrick}n} - 1) + 5 \\ | |||
T(n - 1) &= T(n - 2) + 5 \\ | |||
\end{align}</math> | |||
To dáva zmysel – ak chceme vyjadriť <math>T(n - 1)</math>, musíme k hodnote predtým (<math>T(n - 2)</math>) pripočítať 5. Inými slovami, vždy sa hodnota pre nejaké <math>n</math> vyjadrí tak, že sa k hodnote predtým (<math>n - 1</math>) pripočíta 5. '''Ale nesmieme zabudnúť, že <math>T(1) = 3</math>'''. | |||
Ak by sme pokračovali vo vyjadrovaní menších a menších <math>n</math>, vyzeralo by to nejako takto. <math>n</math> znižujeme preto, lebo sa chceme priblížiť ku triviálnemu prípadu: číslu 3 (predpokladá sa, že <math>3 < n</math>): | |||
<math>\begin{align} | |||
T(n - 1) &= {\color{orange}T(n - 2) + 5} \\ | |||
T(n - 2) &= {\color{crimsonred}T(n - 3) + 5} \\ | |||
T(n - 3) &= {\color{green}T(n - 4) + 5} \\ | |||
\end{align}</math> | |||
Teraz sme si vyjadrili pomocné funkcie a vnorili sa hlbšie do rekurzie. Tieto funkcie musíme dosadiť späť do pôvodného <math>T(n)</math>, teda: | |||
<math display="block">\begin{align} | |||
T(n) &= T(n - 1) + 5 \\ | |||
&= {\color{orange}T(n - 2) + 5} + 5 \\ | |||
&= {\color{crimsonred}T(n - 3) + 5}\ {\color{orange} +\ 5} \\ | |||
&= {\color{green}T(n - 4) + 5}\ {\color{crimsonred} +\ 5}\ {\color{orange} +\ 5} \\ | |||
\cdots \\ | |||
\end{align}</math> | |||
A takto to bude pokračovať donekonečna... Ale nie? Kedy sa rekurzia skončí? '''Skončí sa vtedy, keď nastane triviálny prípad'''. Triviálny prípad je v tomto prípade '''<math>T(1) = 3</math>'''. To znamená, že funkcia nebude volať samú seba, ak <math>n = 1</math> (pretože vráti iba číslo 3). | |||
To znamená, že do nášho rozpisu musíme namiesto posledného rekurzívneho volania dať triviálny prípad: | |||
<math display="block">T(n) = \overbrace{\color{green}T(n - 4)}^{\text{sem}} + 5 + 5 + 5</math> | |||
Vsunieme tam <math>T(1)</math> (čo je vlastne číslo 3) pretože to je jediný prípad ktorý neobsahuje rekurziu. Vďaka tomu bude funkcia vyjadrená nerekurzívne (analyticky): | |||
<math display="block">T(n) = \overbrace{T(1)}^{3} + 5 + 5 + 5 + \cdots</math> | |||
Posledná otázka je, že koľkokrát sa tam bude nachádzať sčítanie čísla 5. Ak sa pozrieme na pôvodné výpočty vyššie, vidíme že čísiel 5 je tam vždy o 1 menej ako <math>n</math>: | |||
<math display="block">\begin{align} | |||
T\overbrace{(1)}^{n = 1} &= 3 \\ | |||
T(2) &= T(\overbrace{n - 1}^{\color{red}n = 2}) + \overbrace{5}^{{\color{orange}n} - 1} \\ | |||
T(3) &= T(\overbrace{n - 2}^{\color{red}n = 3}) + \overbrace{5 + 5}^{{\color{orange}n} - 1} \\ | |||
T(4) &= T(\overbrace{n - 3}^{\color{red}n = 4}) + \overbrace{5 + 5 + 5}^{{\color{orange}n} - 1} \\ | |||
T(5) &= T(\overbrace{n - 4}^{\color{red}n = 5}) + \overbrace{5 + 5 + 5 + 5}^{{\color{orange}n} - 1} \\ | |||
\cdots \\ | |||
\end{align}</math> | |||
Funkciu <math>T(n)</math> môžeme teda zapísať ako: | |||
<math display="block">\begin{align} | |||
T(n) &= T(1) + 5 \times (n - 1) \\ | |||
&= 3 + 5 \times (n - 1) \\ | |||
&= 3 + 5n - 5 \\ | |||
T(n) &= {\color{green}5n - 2} \\ | |||
\end{align}</math> | |||
Bohužiaľ, nepomôže nič iné ako iba pozrieť sa na funkciu a všimnúť si vzťah medzi <math>n</math> a postupnosťou čísiel. Odporúčam pre rekurziu vypočítať viacero príkladov, keďže sú to veci nad ktorými sa treba zamyslieť logicky a pre netrénované oko to môže byť náročnejšie. | |||
Posledný krok kde sme určili že tam bude <math>5 \times (n - 1)</math> môže byť zatiaľ dosť ťažké na pochopenie. Poďme preto na ďalší príklad, kde si ukážeme '''ako sa nad tým dá uvažovať trochu inak''': | |||
<math display="block">\begin{align} | |||
T(1) &= 0 \\ | |||
T(n) &= T(n - 1) + n - 1 \\ | |||
\end{align}</math> | |||
Vidíme, že triviálny prípad je <math>T(1) = 0</math>. Poďme teda postupne vyjadrovať <math>T(n - 1)</math>, <math>T(n - 2)</math>, <math>T(n - 3)</math>... Vždy za <math>\color{green}n</math> v pôvodnom vzorci dosadíme aktuálny argument vo funkcii ktorú vyjadrujeme: | |||
<math display="block">\begin{align} | |||
T(n) &= T({\color{green}n} - 1) + {\color{green}n} - 1 \\ | |||
T({\color{red}n - 1}) &= T({\color{red}(n - 1)} - 1) + {\color{red}(n - 1)} - 1 \\ | |||
&= T(n - 2) + n - 2 \\ | |||
T({\color{hotpink}n - 2}) &= T({\color{hotpink}(n - 2)} - 1) + {\color{hotpink}(n - 2)} - 1 \\ | |||
&= T(n - 3) + n - 3 \\ | |||
\\ | |||
\\ | |||
\\ | |||
T(n - 3) &= T(n - 4) + n - 4 \\ | |||
T(n - 4) &= T(n - 5) + n - 5 \\ | |||
& \cdots | |||
\end{align}</math> | |||
Funkcie hore teraz dosádzame na miesto rekurzívnych funkcií v pôvodnom výraze <math>T(n)</math>. Inými slovami, teraz nebudeme nahrádzať jednotlivé <math>n</math>, ale rovno celé <math>T(...)</math> funkcie namiesto ekvivalentov vyššie: | |||
<math display="block">\begin{align} | |||
T(n) &= T(n - 1) + {\color{mediumslateblue}n - 1} \\ | |||
&= \overbrace{T(n - 2) + {\color{green}n - 2}}^{T({\color{red}n - 1})} + {\color{mediumslateblue}n - 1} \\ | |||
&= \overbrace{T(n - 3) + {\color{orchid}n - 3}}^{T({\color{hotpink}n - 2})} + {\color{green}n - 2} + {\color{mediumslateblue}n - 1} \\ | |||
&= \overbrace{T(n - 4) + n - 4}^{T({\color{orange}n - 3})} + {\color{orchid}n - 3} + {\color{green}n - 2} + {\color{mediumslateblue}n - 1} \\ | |||
& \cdots \\ | |||
\end{align}</math> | |||
...a takto budeme pokračovať až po triviálny prípad – <math>T(1) = 0</math>. Ale poďme skúsiť niečo iné, namiesto obyčajného nahradenia <math>T(n - 4)</math> za <math>T(1)</math>. | |||
Majme posledný riadok v našom výpočte vyššie: | |||
<math display="block">T(n) = T(n - 4) + n - 4 + n - 3 + n - 2 + n - 1</math> | |||
Je to skoro akoby sa nám tam rysovala naša známa aritmetická postupnosť. Posledný prvok bude určite <math>n - 1</math>, tak to je aj v pôvodnej funkcii <math>T(n) = T(n - 1) + n - 1</math>, za týmto už nič nie je takže <math>n - 1</math> musí byť posledný prvok. Ale ktorý prvok bude prvý? Teoreticky to je <math>T(n - 4)</math>, ale tu je problém – je to funkcia, nie konkrétny prvok! Ako sa bude táto funkcia rozbaľovať, tak v sebe môže mať ešte veľmi dlhú postupnosť, napríklad niečo takéto: | |||
<math display="block">T(n) = \cdots + n - 7 + n - 6 + n - 5 + n - 4 + n - 3 + n - 2 + n - 1</math> | |||
Táto postupnosť musí skončiť pri <math>T(1) = 0</math>. Takže, vyzerá nejako takto: | |||
<math display="block">T(n) = 0 + \cdots + n - 2 + n - 1</math> | |||
Hmm, to vyzerá jednoduchšie, ale ešte stále nemáme možnosť, ako môžeme nerekurzívne vyjadriť aká bude dlhá postupnosť. Vráťme sa teda o krok späť, kde sme ešte mali rekurzívne volanie: | |||
<math display="block">T(n) = T(n - 4) + n - 4 + n - 3 + n - 2 + n - 1</math> | |||
Otázka je: '''existuje možnosť ako dosadiť do <math>n - 4</math> aby výsledok tohto výrazu bol 1 (triviálny prípad)?''' Ak v matematike odčítame dve rovnaké čísla, výsledok bude 0 (<math>1 - 1 = 0</math>, <math>123 - 123 = 0</math>, <math>\pi - \pi = 0</math>...). Takže, toto musí platiť aj pre <math>n - n = 0</math>. Ako z nuly spravíme jednotku? No, tak že ju k nej pripočítame: <math>n - n + 1 = 1</math>. A jednotka je náš triviálny prípad! Ideme to dosadiť, aby bolo jasné čo si pod tým predstavujeme: | |||
<math display="block">T(n) = \overbrace{T(n - n + 1)}^{T(1) = 0} + \cdots + n - 4 + n - 3 + n - 2 + n - 1</math> | |||
Ach, zasa tie tri bodky! Ak by tam neboli, tak by sme predsa nevedeli že aká veľká postupnosť je za triviálnym prípadom. Pretože momentálne sme povedali iba to, čo už vieme – je nejaká nula a za ňou ide postupnosť <math>n</math> ktoré sa zakaždým znižujú menej a menej, až po <math>n - 1</math>. Počkať, ale veď v pôvodnom zápise sme <math>n - 4</math> nahradili <math>n - n + 1</math> aby sme dostali triviálny prípad. Mohli by sme to spraviť aj pre tú ďalšiu <math>n - 4</math>? Áno, v podstate sa jedná o dva rovnaké zápisy, takže aj výsledok musí byť v tomto prípade rovnaký. Čiže: | |||
<math display="block">T(n) = \overbrace{T(n - n + 1)}^{T(1)} + \overbrace{n - n + 1}^{\text{predtým:}\ n - 4} + \cdots + n - 3 + n - 2 + n - 1</math> | |||
Aha, takže teraz v podstate dopĺňame tú postupnosť z opačného konca! Takže by to pokračovalo nejako takto: | |||
<math display="block">T(n) = \overbrace{T(n - n + 1)}^{T(1)} + \overbrace{n - n + 1 }^{1} + \overbrace{n - n + 2 }^{2} + \overbrace{n - n + 3}^{3} + \cdots + n - 2 + n - 1</math> | |||
Poďme vyjadriť to čo vieme vyjadriť a dosadiť čo môžeme dosadiť, aby sme si to opäť zjednodušili: | |||
<math display="block">T(n) = \overbrace{0}^{T(1)} + \overbrace{{\color{orange}1} + 2 + 3 + \cdots + n - 2 + {\color{red}n - 1}}^{\text{aritmetická postupnosť}}</math> | |||
Ha! Je to postupnosť čísiel! Vidíme dokonca medzeru medzi nulou a jednotkou. To znamená, že aritmetická postupnosť je iba vyznačená časť a iba na tú môžeme aplikovať vzorec. Prvý prvok je <math>1</math>, posledný <math>n - 1</math>. To znamená, že výsledná nerekurzívna funkcia bude: | |||
<math display="block">T(n) = 0 + \frac{({\color{orange}1} + ({\color{red}n - 1})) \times {\color{green}n})}{2}</math> | |||
Hups, čo bude v tejto postupnosti počet prvkov (<math>\color{green}n</math>)? Nemôže to byť rovnaké <math>\color{green}n</math> ako z <math>T({\color{green}n})</math>, pretože to nevyjadruje počet prvkov našej aritmetickej postupnosti – tá postupnosť je iba pod-súčasťou celej funkcie. Teda naozajstná otázka je: koľko je čísiel od <math>\color{orange}1</math> po <math>\color{red}n - 1</math>? Odpoveď: <math>{\color{red}\text{horná hranica}} - {\color{orange}\text{dolná}} + 1</math>. Takže: <math>{\color{red} n - 1} - {\color{orange}1} + 1 = {\color{green}n - 1}</math>. Pokračujeme: | |||
<math display="block">\begin{align} | |||
T(n) &= \cancel{0 +}\ \frac{({\color{orange}1} + ({\color{red}n - 1})) \times {\color{green}(n - 1)})}{2} \\ | |||
&= \frac{n \times (n - 1)}{2} \\ | |||
T(n) &= \frac{n^2 - n}{2} \\ | |||
\end{align}</math> | |||
A to je aj naša odpoveď – tento vzorec môžeme teda použiť pre výpočet súčtu prvých <math>n</math> prvkov, nerekurzívne (kde prvým prvkom je nula, takže <math>0 + 1 + 2 + 3 + 4 + \cdots</math>). Toto riešenie je trochu zdĺhavejšie, ale zato veľmi detailné. Dokonca sme dokázali krok po kroku ako to celé funguje bez toho aby sme museli rátať prvých <math>n</math> konkrétnych hodnôt. | |||
Pre overenie či je naše riešenie naozaj správne, skúsme vypočítať nejakú hodnotu. Najskôr to vypočítame pomocou pôvodného rekurzívneho vzťahu a potom ich porovnáme – ak sa výsledky budú rovnať, naše riešenie je správne. Teda, povedzme že chceme vedieť hodnotu <math>T(5)</math> (teda, <math>n = 5</math>): | |||
<math display="block">\begin{align} | |||
T(1) &= 0 \\ | |||
T(n) &= T(n - 1) + n - 1 \\ | |||
T(2) &= T(2 - 1) + 2 - 1 = 0 + 1 = 1 \\ | |||
T(3) &= T(3 - 1) + 3 - 1 = 1 + 2 = 3 \\ | |||
T(4) &= T(4 - 1) + 4 - 1 = 3 + 3 = 6 \\ | |||
T(5) &= T(5 - 1) + 5 - 1 = 6 + 4 = {\color{green}10} \\ | |||
\end{align}</math> | |||
Súčet prvých 5 prvkov je teda 10 (<math>0 + 1 + 2 + 3 + 4 = 10</math>, takže je to v poriadku). Teraz to vypočítajme pomocou našej funkcie: | |||
<math display="block">\begin{align} | |||
T(n) &= \frac{n^2 - n}{2} \\ | |||
T(5) &= \frac{5^2 - 5}{2} \\ | |||
&= \frac{25 - 5}{2} \\ | |||
&= \frac{20}{2} \\ | |||
&= {\color{green}10} | |||
\end{align}</math> | |||
Výsledky sa zhodujú, takže to funguje a je to v poriadku. A určite je táto funkcia omnoho rýchlejšia ako jej pôvodná rekurzívna verzia. | |||
Postup vyššie môže niekomu pomôcť, ale druhému zasa veci zkomplikovať. Spôsobov riešenia je viacero a na konci dňa záleží iba na výsledku – treba si vybrať ten, ktorý je pre danú situáciu najvhodnejší. | |||
Poďme ďalej, späť na niečo menej zložité. Cieľom je opäť vyjadriť funkciu <math>T(n)</math> analyticky (inými slovami: nerekurzívne): | |||
<math display="block">\begin{align} | |||
T(1) &= 5 \\ | |||
T(n) &= T(n - 1) + 2 \\ | |||
\end{align}</math> | |||
Keď sa na to pozrieme logicky, v podstate je to funkcia ktorá začína od čísla 5, pričom k predošlému výsledku vždy pripočíta číslo 2. Postupnosť hodnôt teda vyzerá nasledovne: <math>5, 7, 9, 11, 13, 15, 17, \cdots</math>. Poďme sa ale postupne vnárať do rekurzie – pre stručnosť už nebudem uvádzať výpočet "krok za krokom", ale princíp zostáva rovnaký ako v predošlých príkladoch; výsledkom by malo byť: | |||
<math display="block">\begin{align} | |||
T(1) &= 5 \\ | |||
T(n) &= T(n - 1) + 2 \\ | |||
&= T(n - 2) + 2 + 2 \\ | |||
&= T(n - 3) + 2 + 2 + 2 \\ | |||
&= T(n - 4) + 2 + 2 + 2 + 2 \\ | |||
& \cdots \\ | |||
\end{align}</math> | |||
Už sa začína rysovať istá postupnosť čísiel, ktorá avšak nie je aritmetická – k číslu ktoré odčíta <math>n</math> v rekurzívnej funkcii naľavo sa vždy pripočíta rovnaký počet dvojok. Kedy sa to zastaví? Vždy, keď nastane triviálny prípad <math>T(1) = 5</math>. Otázka je jednoducho koľkokrát máme tie dvojky vynásobiť a na začiatku alebo konci k tomu pripočítame iba 5 a dostaneme našu funkciu. Ak na to ale máme ísť analyticky: | |||
<math display="block">\begin{align} | |||
T(1) &= 5 \\ | |||
T(n) &= T(n - 4) + 2 + 2 + 2 + 2 \\ | |||
&= T(n - 4) + 4 \times 2 \\ | |||
&= \overbrace{T(n - n + 1)}^{T(1)} + ? \times 2 | |||
\end{align}</math> | |||
Nastal ale problém – chceme 4 nahradiť <math>n + 1</math>, lenže nemôžme pretože sa to nespráva ako jeden prvok. Musíme okolo pridať zátvorky tak, aby výsledok zostal rovnaký a aby sa <math>n + 1</math> správalo ako jeden prvok: | |||
<math display="block">T(n) = \overbrace{T(n - (n - 1))}^{T(1)} + (n - 1) \times 2</math> | |||
Teraz je to už v poriadku. Pretože pred zátvorkou je mínus, výsledok bude rovnaký ako v predošlej verzii – toto sme preniesli aj na miesto druhej 4 ako jeden prvok. Ak by sme to preniesli ako <math>(n + 1) \times 2</math>, tak by to nevychádzalo – ak to nie je jasné, bohužiaľ pomôže opäť iba logické myslenie. Ale ako radu odporúčam pri "umelej tvorbe čísla 1" vždy používať <math>n - (n - 1)</math> namiesto <math>n - n + 1</math>, aby sme sa vyhli zbytočným problémom akým bol napríklad aj tento. | |||
Ale teraz už pokračujme: | |||
<math display="block">\begin{align} | |||
T(n) &= 5 + (n - 1) \times 2 \\ | |||
T(n) &= 5 + 2n - 2 \\ | |||
T(n) &= {\color{green}2n + 3} \\ | |||
\end{align}</math> | |||
Výsledok je teda <math>T(n) = 2n + 3</math>. Skúsme to porovnať s našou očakávanou postupnosťou čísel, ktorú sme odhadli úplne na začiatku. <math>5, 7, 9, 11, 13, 15, 17, \cdots</math>: | |||
<math display="block">\begin{align} | |||
T(n) &= 2n + 3 \\ | |||
T(1) &= 2 \times 1 + 3 = 5 \\ | |||
T(2) &= 2 \times 2 + 3 = 7 \\ | |||
T(3) &= 2 \times 3 + 3 = 9 \\ | |||
T(4) &= 2 \times 4 + 3 = 11 \\ | |||
T(5) &= 2 \times 5 + 3 = 13 \\ | |||
\end{align}</math> | |||
Výsledky sa zhodujú, takže to asi bude platiť aj pre všetky ostatné <math>n</math>. | |||
== Geometrická postupnosť == | |||
V tomto bode sa stretávame s '''geometrickou postupnosťou'''. Je to postupnosť, kde sa namiesto základu mení '''exponent''', respektíve prvky sa nemenia ''o nejaké číslo'', ale menia sa ''o niekoľko krát''. | |||
Príkladom geometrickej postupnosti môže byť takto definovaná rekurzívna funkcia: | |||
<math display="block">\begin{align} | |||
T(0) &= 0 \\ | |||
T(n) &= 2 \times T(n - 1) + 2 \\ | |||
\end{align}</math> | |||
Triviálny prípad je <math>T(0) = 0</math>, pričom vždy násobíme predošlý výsledok 2-krát a pripočítame k tomu dvojku. Ak rekurziu rozbalíme, bude vyzerať nejako takto: | |||
<math display="block">\begin{align} | |||
T(n) &= 2 \times {\color{green}T(n - 1)} + 2 \\ | |||
&= 2 \times (\overbrace{\color{green}2 \times {\color{red}T(n - 2)} + 2}^{\color{green}T(n - 1)}) + 2 \\ | |||
&= 2 \times (\underbrace{{\color{green}2 \times}\ {\color{red}\overbrace{(2 \times T(n - 3) + 2)}^{T(n - 2)}}\ {\color{green}+ 2}}_{\color{green}T(n - 1)}) + 2 \\ | |||
& \cdots \\ | |||
\end{align}</math> | |||
Pozor – keď násobíme dvojkou, násobíme celý predošlý výsledok! Takže nesmieme zabudnúť výrazy osádzať zátvorkami! | |||
Ak by sme pokračovali, vyzeralo by to nejako takto: | |||
<math display="block">\begin{align} | |||
&= 2 \times (2 \times (2 \times (2 \times T(n - 4) + 2) + 2) + 2) + 2 \\ | |||
&= 2 \times (2 \times (2 \times (2 \times (2 \times T(n - 5) + 2) + 2) + 2) + 2) + 2 \\ | |||
& \cdots \\ | |||
\end{align}</math> | |||
Máme tam veľa zátvoriek, poďme ich násobiť a celý výraz zjednodušiť: | |||
<math display="block">\begin{align} | |||
&= {\color{gray}2 \times (2 \times (2 \times} (2 \times {\color{orange}(2T(n - 5)} + 2)\ {\color{gray} +\ 2) + 2) + 2) + 2} \\ | |||
&= {\color{gray}2 \times (2 \times} (2 \times ({\color{orange}2^2T(n - 5) + 2^2} + 2) \ {\color{gray} +\ 2) + 2) + 2} \\ | |||
&= {\color{gray}2 \times} (2 \times ({\color{orange}2^3T(n - 5) + 2^3 + 2^2} + 2)\ {\color{gray} +\ 2) + 2} \\ | |||
&= 2 \times ({\color{orange}2^4T(n - 5) + 2^4 + 2^3 + 2^2} + 2)\ {\color{gray}+\ 2} \\ | |||
&= {\color{orange}2^5T(n - 5) + 2^5 + 2^4 + 2^3 + 2^2} + 2 \\ | |||
\end{align}</math> | |||
Vidíme, že dvojky na seba postupne nabaľujú mocniny (pretože <math>2 \times 2 = 2^2</math> a <math>2^2 \times 2 = 2^3</math> a tak ďalej), pričom zakaždým násobíme každý prvok v zátvorke. | |||
Rekurzia sa zastaví pri <math>T(0) = 0</math>. Inými slovami, v poslednom riadku vyššie musíme zahrnúť triviálny prípad: | |||
<math display="block">\begin{align} | |||
T(n) &= 2^{\color{green}n}T(\overbrace{n - {\color{green}n}}^{0}) + 2^{\color{green}n} + \cdots + 2^4 + 2^3 + 2^2 + 2^1 \\ | |||
&= \cancel{2^{\color{green}n}\times 0} + 2^{\color{green}n} + \cdots + 2^4 + 2^3 + 2^2 + 2^1 \\ | |||
&= 2^{\color{green}n} + \cdots + 2^4 + 2^3 + 2^2 + 2^1 \\ | |||
\end{align}</math> | |||
Hneď sme aj všetky čísla 5 nahradili <math>\color{green}n</math>. No a <math>2 = 2^1</math>. Výsledkom je geometrická postupnosť, kde prvým prvkom je <math>2^1</math> a posledným prvkom je <math>2^{\color{green}n}</math> (ak exponenty vnímame odzadu, zoradené vzostupne). | |||
Pri geometrickej postupnosti je dôležité určiť '''kvocient''' (označuje sa <math>q</math> a vyjadruje pomer medzi dvomi za sebou idúcimi prvkami v postupnosti). Ak sa pozrieme na postupnosť vyššie, tak v podstate akýkoľvek prvok v postupnosti vieme zapísať ako <math>n</math>-násobok prvého člena v postupnosti. Kvocient <math>q = 2</math> – pretože násobíme medzi sebou dvojky, teda každý prvok je v postupnosti vzdialený 2-'''krát'''. | |||
Všeobecný vzorec pre výpočet súčtu prvých <math>n</math> členov geometrickej postupnosti: | |||
<math display="block">S_n = a_1 \times \frac{q^n - 1}{q - 1}</math> | |||
Ako prvý prvok (<math>a_1</math>) sme si zvolili <math>2^1</math>. Počet prvkov v tejto postupnosti je <math>n</math> (pretože exponenty idú od 1 po <math>n</math>, a celá funkcia je iba táto geometrická postupnosť). Po dosadení dostávame funkciu: | |||
<math display="block">\begin{align} | |||
T({\color{green}n}) &= 2^1 \times \frac{2^{\color{green}n} - 1}{2 - 1} \\ | |||
&= 2 \times \frac{2^{n} - 1}{1} \\ | |||
&= 2 \times 2^{n} - 1 \\ | |||
T(n) &= {\color{hotpink}2^{(n + 1)} - 1} \\ | |||
\end{align}</math> | |||
Výsledok je teda <math>T(n) = 2^{(n + 1)} - 1</math>. | |||
Poďme na niečo iné (ale podobné): | |||
<math display="block">\begin{align} | |||
T(1) &= 2 \\ | |||
T(n) &= 5 \times T(n - 1) + 1 \\ | |||
\end{align}</math> | |||
Rozbalíme rekurziu: | |||
<math display="block">\begin{align} | |||
T(n) &= 5 \times {\color{green}T(n - 1)} + 1 \\ | |||
&= 5 \times (\overbrace{\color{green}5 \times {\color{red}T(n - 2)} + 1}^{\color{green}T(n - 1)}) + 1 \\ | |||
&= 5 \times (\underbrace{{\color{green}5 \times}\ {\color{red}\overbrace{(5 \times T(n - 3) + 1)}^{T(n - 2)}}\ {\color{green}+ 1}}_{\color{green}T(n - 1)}) + 1 \\ | |||
& \cdots \\ | |||
\end{align}</math> | |||
Podobne ako v predošlom prípade, je to postupnosť čísla 5, ktorá sa násobí: | |||
<math display="block">\begin{align} | |||
&= 5 \times (5 \times (5 \times (5 \times T(n - 4) + 1) + 1) + 1) + 1 \\ | |||
&= 5 \times (5 \times (5 \times (5 \times (5 \times T(n - 5) + 1) + 1) + 1) + 1) + 1 \\ | |||
& \cdots \\ | |||
\end{align}</math> | |||
Výsledná postupnosť je geometrická: | |||
<math>\begin{align} | |||
&= 5^5T(n - 5) + 5^4 + 5^3 + 5^2 + 5^1 + \overbrace{1}^{5^0 = 1} \\ | |||
\end{align}</math> | |||
Doplníme triviálny prípad: | |||
<math>\begin{align} | |||
T(n) &= 5^{\color{green}(n - 1)}T(\overbrace{n - {\color{green}(n - 1)}}^{T(1)}) + 5^{(n-2)} + 5^{(n-3)} + \cdots + 5^4 + 5^3 + 5^2 + 5^1 + \overbrace{1}^{5^0 = 1} \\ | |||
&= 5^{\color{green}(n - 1)}T(\overbrace{n - {\color{green}(n - 1)}}^{T(1)}) + 5^{(n-2)} + 5^{(n-3)} + \cdots + \overbrace{5^{(n - n)}}^{5^0 = 1} \\ | |||
&= 2 \times 5^{(n - 1)} + \underbrace{(5^{(n-2)} + 5^{(n-3)} + \cdots + 1)}_{\text{geometrická postupnosť:}\ \sum_{i=0}^{n - 2}5^i} \\ | |||
&= 2 \times 5^{(n - 1)} + \frac{5^{(n - 1)} - 1}{5 - 1} \\ | |||
&= \frac{\overbrace{8 \times 5^{(n - 1)} + 5^{n - 1}}^{9 \times 5^{(n - 1)}} - 1}{4} \\ | |||
T(n) &= \frac{9 \times 5^{(n - 1)} - 1}{4} \\ | |||
\end{align}</math> | |||
{{Téma|Oblast=Kategória:Algoritmy a výpočtová zložitosť|Poradie=25}} | {{Téma|Oblast=Kategória:Algoritmy a výpočtová zložitosť|Poradie=25}} | ||
[[Kategória:Algoritmy a výpočtová zložitosť]] | [[Kategória:Algoritmy a výpočtová zložitosť]] |
Aktuálna revízia z 16:29, 26. marec 2025
Vyjadrenie funkcie analyticky (nerekurzívne), geometrická postupnosť, určovanie výpočtovej zložitosti v rekurzívnych funkciách.
Určenie konkrétnej hodnoty rekurzívnej funkcie výpočtovej zložitosti
Už vieme určovať výpočtovú zložitosť v metódach, ktoré nie sú rekurzívne. Ale čo ak funkcia volá samú seba? Majme napríklad funkciu definovanú takto:
Funkcia odkazuje na samú seba, pričom vieme že pre je hodnota číslo 3. Toto sa nazýva triviálny prípad – je to bod, v ktorom sa rekurzívne volanie zastaví. Toto bude veľmi dôležité, pretože rekurzívne funkcie sa musia skôr či neskôr zastaviť (nemôžeme ich volať donekonečna).
Aby sme si vedeli lepšie predstaviť ako rekurzívne funkcie fungujú, poďme vypočítať, akú hodnotu bude mať :
Ako vidíme, . Ide iba o postupné nahrádzanie premennej , pričom ak chceme vypočítať určitú hodnotu, musíme v tomto prípade poznať aj všetky predošlé hodnoty. Avšak, čo by sa stalo ak by sme chceli vypočítať ? Ak by sme to počítali manuálne, trvalo by to strašne dlho. Veľmi veľké hodnoty by robili problém aj najrýchlejším počítačom, obzvlášť ak by rekurzívne volania boli komplikovanejšie.
Našťastie, pre tento prípad vieme rekurzívne funkcie vyjadriť analyticky (teda, nerekurzívne) – to znamená že pre dané vieme vypočítať konkrétnu hodnotu bez toho, aby sme volali funkciu rekurzívne.
Pri analýze funkcie musíme postupovať od čísla a postupne sa vnárať hlbšie a hlbšie do rekurzie. Pre príklad vyššie by sme postupovali takto:
Ako všeobecne vyjadríme ? Tak, že zoberieme všeobecnú definíciu funkcie a namiesto dosadíme :
To dáva zmysel – ak chceme vyjadriť , musíme k hodnote predtým () pripočítať 5. Inými slovami, vždy sa hodnota pre nejaké vyjadrí tak, že sa k hodnote predtým () pripočíta 5. Ale nesmieme zabudnúť, že .
Ak by sme pokračovali vo vyjadrovaní menších a menších , vyzeralo by to nejako takto. znižujeme preto, lebo sa chceme priblížiť ku triviálnemu prípadu: číslu 3 (predpokladá sa, že ):
Teraz sme si vyjadrili pomocné funkcie a vnorili sa hlbšie do rekurzie. Tieto funkcie musíme dosadiť späť do pôvodného , teda:
A takto to bude pokračovať donekonečna... Ale nie? Kedy sa rekurzia skončí? Skončí sa vtedy, keď nastane triviálny prípad. Triviálny prípad je v tomto prípade . To znamená, že funkcia nebude volať samú seba, ak (pretože vráti iba číslo 3).
To znamená, že do nášho rozpisu musíme namiesto posledného rekurzívneho volania dať triviálny prípad:
Vsunieme tam (čo je vlastne číslo 3) pretože to je jediný prípad ktorý neobsahuje rekurziu. Vďaka tomu bude funkcia vyjadrená nerekurzívne (analyticky):
Posledná otázka je, že koľkokrát sa tam bude nachádzať sčítanie čísla 5. Ak sa pozrieme na pôvodné výpočty vyššie, vidíme že čísiel 5 je tam vždy o 1 menej ako :
Funkciu môžeme teda zapísať ako:
Bohužiaľ, nepomôže nič iné ako iba pozrieť sa na funkciu a všimnúť si vzťah medzi a postupnosťou čísiel. Odporúčam pre rekurziu vypočítať viacero príkladov, keďže sú to veci nad ktorými sa treba zamyslieť logicky a pre netrénované oko to môže byť náročnejšie.
Posledný krok kde sme určili že tam bude môže byť zatiaľ dosť ťažké na pochopenie. Poďme preto na ďalší príklad, kde si ukážeme ako sa nad tým dá uvažovať trochu inak:
Vidíme, že triviálny prípad je . Poďme teda postupne vyjadrovať , , ... Vždy za v pôvodnom vzorci dosadíme aktuálny argument vo funkcii ktorú vyjadrujeme:
Funkcie hore teraz dosádzame na miesto rekurzívnych funkcií v pôvodnom výraze . Inými slovami, teraz nebudeme nahrádzať jednotlivé , ale rovno celé funkcie namiesto ekvivalentov vyššie:
...a takto budeme pokračovať až po triviálny prípad – . Ale poďme skúsiť niečo iné, namiesto obyčajného nahradenia za .
Majme posledný riadok v našom výpočte vyššie:
Je to skoro akoby sa nám tam rysovala naša známa aritmetická postupnosť. Posledný prvok bude určite , tak to je aj v pôvodnej funkcii , za týmto už nič nie je takže musí byť posledný prvok. Ale ktorý prvok bude prvý? Teoreticky to je , ale tu je problém – je to funkcia, nie konkrétny prvok! Ako sa bude táto funkcia rozbaľovať, tak v sebe môže mať ešte veľmi dlhú postupnosť, napríklad niečo takéto:
Táto postupnosť musí skončiť pri . Takže, vyzerá nejako takto:
Hmm, to vyzerá jednoduchšie, ale ešte stále nemáme možnosť, ako môžeme nerekurzívne vyjadriť aká bude dlhá postupnosť. Vráťme sa teda o krok späť, kde sme ešte mali rekurzívne volanie:
Otázka je: existuje možnosť ako dosadiť do aby výsledok tohto výrazu bol 1 (triviálny prípad)? Ak v matematike odčítame dve rovnaké čísla, výsledok bude 0 (, , ...). Takže, toto musí platiť aj pre . Ako z nuly spravíme jednotku? No, tak že ju k nej pripočítame: . A jednotka je náš triviálny prípad! Ideme to dosadiť, aby bolo jasné čo si pod tým predstavujeme:
Ach, zasa tie tri bodky! Ak by tam neboli, tak by sme predsa nevedeli že aká veľká postupnosť je za triviálnym prípadom. Pretože momentálne sme povedali iba to, čo už vieme – je nejaká nula a za ňou ide postupnosť ktoré sa zakaždým znižujú menej a menej, až po . Počkať, ale veď v pôvodnom zápise sme nahradili aby sme dostali triviálny prípad. Mohli by sme to spraviť aj pre tú ďalšiu ? Áno, v podstate sa jedná o dva rovnaké zápisy, takže aj výsledok musí byť v tomto prípade rovnaký. Čiže:
Aha, takže teraz v podstate dopĺňame tú postupnosť z opačného konca! Takže by to pokračovalo nejako takto:
Poďme vyjadriť to čo vieme vyjadriť a dosadiť čo môžeme dosadiť, aby sme si to opäť zjednodušili:
Ha! Je to postupnosť čísiel! Vidíme dokonca medzeru medzi nulou a jednotkou. To znamená, že aritmetická postupnosť je iba vyznačená časť a iba na tú môžeme aplikovať vzorec. Prvý prvok je , posledný . To znamená, že výsledná nerekurzívna funkcia bude:
Hups, čo bude v tejto postupnosti počet prvkov ()? Nemôže to byť rovnaké ako z , pretože to nevyjadruje počet prvkov našej aritmetickej postupnosti – tá postupnosť je iba pod-súčasťou celej funkcie. Teda naozajstná otázka je: koľko je čísiel od po ? Odpoveď: . Takže: . Pokračujeme:
A to je aj naša odpoveď – tento vzorec môžeme teda použiť pre výpočet súčtu prvých prvkov, nerekurzívne (kde prvým prvkom je nula, takže ). Toto riešenie je trochu zdĺhavejšie, ale zato veľmi detailné. Dokonca sme dokázali krok po kroku ako to celé funguje bez toho aby sme museli rátať prvých konkrétnych hodnôt.
Pre overenie či je naše riešenie naozaj správne, skúsme vypočítať nejakú hodnotu. Najskôr to vypočítame pomocou pôvodného rekurzívneho vzťahu a potom ich porovnáme – ak sa výsledky budú rovnať, naše riešenie je správne. Teda, povedzme že chceme vedieť hodnotu (teda, ):
Súčet prvých 5 prvkov je teda 10 (, takže je to v poriadku). Teraz to vypočítajme pomocou našej funkcie:
Výsledky sa zhodujú, takže to funguje a je to v poriadku. A určite je táto funkcia omnoho rýchlejšia ako jej pôvodná rekurzívna verzia.
Postup vyššie môže niekomu pomôcť, ale druhému zasa veci zkomplikovať. Spôsobov riešenia je viacero a na konci dňa záleží iba na výsledku – treba si vybrať ten, ktorý je pre danú situáciu najvhodnejší.
Poďme ďalej, späť na niečo menej zložité. Cieľom je opäť vyjadriť funkciu analyticky (inými slovami: nerekurzívne):
Keď sa na to pozrieme logicky, v podstate je to funkcia ktorá začína od čísla 5, pričom k predošlému výsledku vždy pripočíta číslo 2. Postupnosť hodnôt teda vyzerá nasledovne: . Poďme sa ale postupne vnárať do rekurzie – pre stručnosť už nebudem uvádzať výpočet "krok za krokom", ale princíp zostáva rovnaký ako v predošlých príkladoch; výsledkom by malo byť:
Už sa začína rysovať istá postupnosť čísiel, ktorá avšak nie je aritmetická – k číslu ktoré odčíta v rekurzívnej funkcii naľavo sa vždy pripočíta rovnaký počet dvojok. Kedy sa to zastaví? Vždy, keď nastane triviálny prípad . Otázka je jednoducho koľkokrát máme tie dvojky vynásobiť a na začiatku alebo konci k tomu pripočítame iba 5 a dostaneme našu funkciu. Ak na to ale máme ísť analyticky:
Nastal ale problém – chceme 4 nahradiť , lenže nemôžme pretože sa to nespráva ako jeden prvok. Musíme okolo pridať zátvorky tak, aby výsledok zostal rovnaký a aby sa správalo ako jeden prvok:
Teraz je to už v poriadku. Pretože pred zátvorkou je mínus, výsledok bude rovnaký ako v predošlej verzii – toto sme preniesli aj na miesto druhej 4 ako jeden prvok. Ak by sme to preniesli ako , tak by to nevychádzalo – ak to nie je jasné, bohužiaľ pomôže opäť iba logické myslenie. Ale ako radu odporúčam pri "umelej tvorbe čísla 1" vždy používať namiesto , aby sme sa vyhli zbytočným problémom akým bol napríklad aj tento.
Ale teraz už pokračujme:
Výsledok je teda . Skúsme to porovnať s našou očakávanou postupnosťou čísel, ktorú sme odhadli úplne na začiatku. :
Výsledky sa zhodujú, takže to asi bude platiť aj pre všetky ostatné .
Geometrická postupnosť
V tomto bode sa stretávame s geometrickou postupnosťou. Je to postupnosť, kde sa namiesto základu mení exponent, respektíve prvky sa nemenia o nejaké číslo, ale menia sa o niekoľko krát.
Príkladom geometrickej postupnosti môže byť takto definovaná rekurzívna funkcia:
Triviálny prípad je , pričom vždy násobíme predošlý výsledok 2-krát a pripočítame k tomu dvojku. Ak rekurziu rozbalíme, bude vyzerať nejako takto:
Pozor – keď násobíme dvojkou, násobíme celý predošlý výsledok! Takže nesmieme zabudnúť výrazy osádzať zátvorkami!
Ak by sme pokračovali, vyzeralo by to nejako takto:
Máme tam veľa zátvoriek, poďme ich násobiť a celý výraz zjednodušiť:
Vidíme, že dvojky na seba postupne nabaľujú mocniny (pretože a a tak ďalej), pričom zakaždým násobíme každý prvok v zátvorke.
Rekurzia sa zastaví pri . Inými slovami, v poslednom riadku vyššie musíme zahrnúť triviálny prípad:
Hneď sme aj všetky čísla 5 nahradili . No a . Výsledkom je geometrická postupnosť, kde prvým prvkom je a posledným prvkom je (ak exponenty vnímame odzadu, zoradené vzostupne).
Pri geometrickej postupnosti je dôležité určiť kvocient (označuje sa a vyjadruje pomer medzi dvomi za sebou idúcimi prvkami v postupnosti). Ak sa pozrieme na postupnosť vyššie, tak v podstate akýkoľvek prvok v postupnosti vieme zapísať ako -násobok prvého člena v postupnosti. Kvocient – pretože násobíme medzi sebou dvojky, teda každý prvok je v postupnosti vzdialený 2-krát.
Všeobecný vzorec pre výpočet súčtu prvých členov geometrickej postupnosti:
Ako prvý prvok () sme si zvolili . Počet prvkov v tejto postupnosti je (pretože exponenty idú od 1 po , a celá funkcia je iba táto geometrická postupnosť). Po dosadení dostávame funkciu:
Výsledok je teda .
Poďme na niečo iné (ale podobné):
Rozbalíme rekurziu:
Podobne ako v predošlom prípade, je to postupnosť čísla 5, ktorá sa násobí:
Výsledná postupnosť je geometrická:
Doplníme triviálny prípad: