2 - Rekurze
Připomenutí teorie
Přednáška 2 v oddíle Přednášky
Rekurzivní funkce je ta, která volá sebe samu (přímo i nepřímo). Jednoduchým příkladem je například Fibonacciho posloupnost . Jejich složitost lze často vyjádřit pomocí vzorce
kde je počet rekurzivních zavolání z jednoho průchodu funkce, je velikost vstupu pro dané podproblémy (vstup je rozdělen na stejných částí), a je složitost jednoho průchodu funkce. Typicky složitost rozdělení dat a složení podvýsledků dohromady.
Složitost rekurzivních funkcí lze počítat různými způsoby.
-
Metodou rekurzivních stromů - rozkreselním stromu volání a sečtením složitosti.
-
Substituční metodou - Odhadem a následým ověřením skrze důkaz indukcí.
-
Mistrovskou větou (Master theorem) - Dosazením do vzorce a výpočtem.
-
pokud , pak ,
-
pokud , pak ,
-
pokud , a , pak .
-
Řešené úlohy
Úvod
Řešená úloha 1:
Kolikrát bude zavolána funkce xyz()
při zavolání rekur(5)
?
void rekur(int x) {
if(x < 1) return;
rekur(x-1);
xyz();
rekur(x-2);
}
Řešení
Řešená úloha 2:
Charakterizujte slovy, jakou hodnotu vrátí funkce f(x, y)
v závislosti na
hodnotách jejích vstupních parametrů.
int f(int x, int y) {
if(x > 0) return f(x - 1, y) + y;
return 0;
}
Řešení
Vrátí .
Řešená úloha 3:
Napište rekurzivní funkci, která pro zadané číslo vypíše řetězec skládající
se z jedniček následovaných dvojkami. Pro dané bude funkce volat
sama sebe právě -krát. Příklad: pro vypíše 112222
.
Řešení
def f(N):
if N > 0:
print(1)
f(N - 1)
print(2)
print(2)
Řešená úloha 4: Rekurzivní algoritmus dělí úlohu o velikosti na 2 stejné části. Každou část musí zpracovat dvakrát. Čas potřebný na rozdělení úlohy na části a na spojení dílčích řešení je úměrný hodnotě . Jak lze matematicky vyjádřit rekurentní vztah popisující složitost algoritmu ?
Řešení
Počítání složitosti
Řešená úloha 5: Rekurzivní algoritmus pracuje tak, že pro data rozdělí na části stejné velikosti, zpracuje těchto částí (tj. jednu dvakrát), a pak jejich řešení spojí. Na samotné rozdělení problému a spojení řešení menších částí potřebuje dobu úměrnou .
Určete asymptotickou složitost pomocí
-
stromu rekurze,
-
Mistrovské věty,
-
a substituční metody.
Strom rekurze
-
V -té úrovni je uzlů, protože v každé úrovni se počet uzlů zvětší -krát.
-
V jednom uzlu v hloubce proběhne operací, protože v -té úrovni jsme problém zmenšili -krát.
-
Hloubka stromu je , protože v každé úrovni dělíme problém na čtvrtiny, a pro .
-
Nejhlubší úroveň má konstantní složitost v každém uzlu, uzlů. Tedy .
-
Hloubka vyžaduje operací.
-
Celkem tedy:
-
Což jsou součty 2 geometrických posloupností. Vzorec pro součet prvků geoemetrické posloupnosti je nám známý: .
-
-
Celková složitost je
Mistrovská věta
Ze zadání máme , tedy , a . Z hrubého porovnání a , tedy a odhadneme, že by mohlo jít o 3. možnost.
Existuje takové , aby ? Pro dostáváme , což jistě platí, protože .
Existuje takové , aby ? Ano, například .
Takže
Substituční metoda
Chceme ukázat, že , kde . Tedy z definice: pro nějaké a .
Přecházíme k indukci:
-
Předpokládáme, že platí a chceme ukázat
-
-
(z indukčního předpokladu)
-
což platí např. pro .
-
Zbývá najít :
což platí i pro
Dokázali jsme . Pro bychom postupovali obdobně, a z toho že získáme
Další úlohy
Úloha 6:
Určete, jakou hodnotu vypíše program po vykonání
příkazu print(rekur(4));
int rekur(int x) {
if (x < 1) return 2;
return (rekur(x-1) + rekur(x-1));
}
Co tato funkce počítá?
Úloha 7: Uvažujte podobnou funkci jako ve cvičení 2. Doplňte prázdná místa tak, aby vracela .
int f(int x, int y) {
if(____) return ____;
return ____;
}
Úloha 8:
Daná funkce ff
je volána takto: ff(a, b);
, a
i b
jsou celá kladná čísla. Napište vztah, který
určí, kolikrát bude volána funkce abc(x)
, v závislosti na hodnotě parametrů a
, b
.
void ff(int x, int p) {
if (x > 0) ff(x–p, p);
abc(x);
if (x > 0) ff(x–p, p);
}
Úloha 9:
Pomocí rekurzivní funkce vypište pro zadané kladné číslo
N
posloupnost čísel:
1 2 … N−2 N−1 N N N−1 N−2 … 2 1
Úloha 10: Rekurzivní algoritmus dělí úlohu o velikosti na 3 stejné části a pro zisk výsledku stačí, když zpracuje pouze dvě z nich. Čas potřebný na rozdělení úlohy na části a na spojení dílčích řešení je úměrný hodnotě . Zapište rekurentní vztah pro asymptotickou složitost algoritmu .
⭐ Úloha 11: Daný rekurzivní algoritmus pracuje tak, že pro data rozdělí na části stejné velikosti, zpracuje každou tuto část dvakrát a pak jejich řešení spojí. Na samotné rozdělení problému a spojení řešení menších částí potřebuje dobu úměrnou hodnotě . Jakou má daný algoritmus asymptotickou složitost?
Řešte pomocí Vaší oblíbené metody.
⭐ Úloha 12: Uvažme následující algoritmus pro řazení pole délky n.
-
Seřaď levou polovinu pole (rekurzivním voláním).
-
Seřaď pravou polovinu pole (rekurzivním voláním).
-
Zkombinuj výsledky do seřazeného pole.
Jaká je asyptotická složitost tohoto algoritmu, pokud přepokládáme, že jsme schopni výsledky zkombinovat v ? A co v případě, že to zkombinování má složitost ?
Úloha 13: Složitost rekurzivního algoritmu je dána rekurencí:
a)
b)
c)
Použijte metodu stromu rekurze k nalezení vhodného (těsného)
horního asymmptotického odhadu funkce .
Ověřte výsledek substituční metodou.
⭐ Úloha 14: Složitost rekurzivního algoritmu je dána rekurencí:
a)
b) , kde
c) , kde .
Použijte metodu stromu rekurze k nalezení vhodného horního odhadu funkce .
Úloha 15: Pro složitost rekurzivního algoritmu platí T(1) = 1. Pro každé n > 1 je T(n) dána rekurencí:
a)
b)
c)
d)
Určete řád růstu funkce .
Úloha 16: Sierpińského koberce
Prvky pole o velikosti jsou pouze čísla a . Obrázky níže znázorňují pole pro několik hodnot , modrá barva představuje hodnotu , bílá . Napište kód, který pro dané vytvoří a vyplní pole podle daného vzoru.

Úloha 17: Seznamte se s Ackermanovou funkcí.
Zkuste spočítat hodnotu .
⭐ Úloha 18: Násobení matic
Nechť a jsou matice tvaru , kde n je přirozené číslo. Uvažujme násobení matic podle definice pro násobení matic, tedy: .
Jaká je složitost výpočtu podle této definice?
Nyní uvažujme násobení matic pomocí rekurze. Pro jednoduchost předpokládejme, že n je mocninou dvou. Pak můžeme matice násobit po blocích. Představme, si že matice a rozdělíme na 4 stejně velké podmatice:
Pak můžeme využít vztahu:
.
Jaká je asymptotická složitost takového rekurzivního algoritmu?
Jaká je asymptotická složitost Strassenova algoritmu, pro který platí:
,
kde:
?