2. Rekurze

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 f(N)=f(N2)+f(N1)f(N) = f(N-2) + f(N-1). Jejich složitost lze často vyjádřit pomocí vzorce

T(n)=aT(nb)+f(n)T(n) = a T\left(\frac{n}{b}\right) + f(n)

kde aa je počet rekurzivních zavolání z jednoho průchodu funkce, nb\frac{n}{b} je velikost vstupu pro dané podproblémy (vstup nn je rozdělen na bb stejných částí), a f(n)f(n) 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 f(n)O(nlogb(a)ϵ),ϵ>0f(n) \in \mathrm{O}\left(n^{\log_b(a) - \epsilon}\right), \epsilon > 0, pak T(n)Θ(nlogb(a))T(n) \in \Theta \left(n^{\log_b(a)}\right),

    • pokud f(n)Θ(nlogb(a))f(n) \in \Theta\left(n^{\log_b(a)}\right), pak T(n)Θ(nlogb(a)log(n))T(n) \in \Theta\left(n^{\log_b(a)}\log(n)\right),

    • pokud f(n)Ω(nlogb(a)+ϵ),ϵ>0f(n) \in \Omega\left(n^{\log_b(a) + \epsilon}\right), \epsilon > 0, a af(nb)cf(n),c<1a f\left(\frac{n}{b}\right) \leq c f(n), c < 1, pak T(n)Θ(f(n))T(n) \in \Theta\left(f(n)\right).

Jupyter notebook

Vybrané úlohy jsou připraveny v Jupyter notebooku.

Také na Google Colab.

Ř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í

Solution


Ř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í xyx \cdot y.


Řešená úloha 3: Napište rekurzivní funkci, která pro zadané číslo NN vypíše řetězec skládající se z NN jedniček následovaných 2N2N dvojkami. Pro dané NN bude funkce volat sama sebe právě NN-krát. Příklad: pro N=2N = 2 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 A\mathcal{A} dělí úlohu o velikosti nn 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ě nn. Jak lze matematicky vyjádřit rekurentní vztah popisující složitost algoritmu A\mathcal{A}?

Řešení

T(n)=4T(n2)+nT(n) = 4 T\left(\frac{n}{2}\right) + n


Počítání složitosti

Řešená úloha 5: Rekurzivní algoritmus A\mathcal{A} pracuje tak, že pro n>1n > 1 data rozdělí na 44 části stejné velikosti, zpracuje 55 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 n2nn^2–n.

Určete asymptotickou složitost pomocí

  • stromu rekurze,

  • Mistrovské věty,

  • a substituční metody.

Strom rekurze

Solution

  • V ii-té úrovni je 5i5^i uzlů, protože v každé úrovni se počet uzlů zvětší 55-krát.

  • V jednom uzlu v hloubce ii proběhne (n4i)2n4i\left(\frac{n}{4^i}\right)^2 - \frac{n}{4^i} operací, protože v ii-té úrovni jsme problém zmenšili 4i4^i-krát.

  • Hloubka stromu je log4(n)\log_4(n), protože v každé úrovni dělíme problém na čtvrtiny, a n4i=1\frac{n}{4^i} = 1 pro i=log4ni = \log_4 n.

  • Nejhlubší úroveň má konstantní složitost v každém uzlu, 5log4(n)=nlog4(5)5^{\log_4(n)} = n^{\log_4(5)} uzlů. Tedy Θ(nlog4(5))\Theta\left(n^{\log_4(5)}\right).

  • Hloubka ii vyžaduje 5i[(n4i)2n4i]=n2(516)in(54)i,0id15^i \left[ \left(\frac{n}{4^i} \right)^2 - \frac{n}{4^i}\right] = n^2 \left(\frac{5}{16}\right)^i - n \left(\frac{5}{4}\right)^i, 0 \leq i \leq d - 1 operací.

    • Celkem tedy:

      i=0log4n1n2(516)in(54)i=n2i=0log4n1(516)ini=0log4n1(54)i\sum_{i=0}^{\log_4 n-1} n^2 \left(\frac{5}{16}\right)^i - n \left(\frac{5}{4}\right)^i = n^2 \sum_{i=0}^{\log_4 n - 1} \left(\frac{5}{16}\right)^{i} - n \sum_{i=0}^{\log_4 n - 1}\left(\frac{5}{4}\right)^{i}

    • Což jsou součty 2 geometrických posloupností. Vzorec pro součet prvků geoemetrické posloupnosti je nám známý: i=0k1a1qi=a1qk1q1,q1\sum_{i=0}^{k-1} a_1q^{i} = a_1\frac{q^k - 1}{q - 1}, q \neq 1.

  • Celková složitost je

Θ(n21(516)log4n1516n1(54)log4n154+nlog45) =Θ(16n211(1nlog4516)+4n(1nlog454)+nlog45) =Θ(1611n21611nlog45+4n4nlog45+nlog45) =Θ(n2+nnlog45)=Θ(n2)\begin{align*} &\Theta\left(n^2 \frac{1-\left(\frac{5}{16}\right)^{\log_4 n}}{1 - \frac{5}{16}} - n \frac{1-\left(\frac{5}{4}\right)^{\log_4 n}}{1-\frac{5}{4}} + n^{\log_4 5}\right) \\\ = &\Theta\left(\frac{16n^2}{11}\left(1-n^{\log_4\frac{5}{16}}\right) + 4n \left(1-n^{\log_4\frac{5}{4}}\right) + n^{\log_4 5}\right) \\\ = &\Theta\left(\frac{16}{11}n^2 - \frac{16}{11}n^{\log_4 5} + 4n - 4n^{\log_4 5} + n^{\log_4 5}\right) \\\ = &\Theta\left(n^2 + n - n^{\log_4 5}\right) = \Theta\left( n^2\right) \end{align*}
Mistrovská věta

Ze zadání máme T(n)=5T(n4)+(n2n)T(n) = 5T\left(\frac{n}{4}\right) + (n^2 - n), tedy a=5a=5, b=4b=4 a f(n)=n2nf(n) = n^2 - n. Z hrubého porovnání f(n)f(n) a nlogb(a)n^{\log_b(a)}, tedy Θ(n2)\Theta(n^2) a nlog45n^{\log_4 5} odhadneme, že by mohlo jít o 3. možnost.

Existuje takové ϵ>0\epsilon > 0, aby (n2n)Ω(nlog4(5)+ϵ)(n^2-n) \in \Omega\left(n^{\log_4(5) + \epsilon}\right)? Pro ϵ=2log4(5)\epsilon = 2 - \log_4(5) dostáváme (n2n)Ω(n2)(n^2 - n) \in \Omega\left(n^2\right), což jistě platí, protože (n2n)Θ(n2)(n^2 - n) \in \Theta(n^2).

Existuje takové c<1c<1, aby 5(n216n4)c(n2n)5 \left(\frac{n^2}{16} - \frac{n}{4}\right) \leq c (n^2 - n)? Ano, například c=5/16c=5/16.

Takže T(n)Θ(n2n)=Θ(n2)T(n) \in \Theta(n^2 - n) = \Theta(n^2)

Substituční metoda

Chceme ukázat, že T(n)O(n2)T(n) \in O(n^2), kde T(n)=5T(n4)+(n2n)T(n) = 5T\left(\frac{n}{4}\right) + (n^2 - n). Tedy z definice: T(n)cn2T(n) \leq c n^2 pro nějaké c>0c > 0 a n0>0n_0 > 0.

Přecházíme k indukci:

  • Předpokládáme, že platí T(n4)c(n4)2T\left(\frac{n}{4}\right) \leq c \left(\frac{n}{4}\right)^2 a chceme ukázat T(n)cn2T(n) \leq c n^2

    • T(n)=5T(n4)+(n2n)T(n) = 5 T\left(\frac{n}{4}\right) + (n^2 - n)

    • T(n)5c(n4)2+(n2n)T(n) \leq 5 c\left( \frac{n}{4} \right)^2 + (n^2 - n) (z indukčního předpokladu)

    • T(n)n2(5c16+1)ncn2T(n) \leq n^2(\frac{5c}{16} + 1) - n \leq cn^2 což platí např. pro c=16c = 16.

Zbývá najít n0n_0:

T(n)=n2(51616+1)n16n2 6n2n16n2,\begin{align*} T(n) = n^2(\frac{5 \cdot 16}{16} + 1) - n &\leq 16 \cdot n^2 \\\ 6 n^2 - n &\leq 16 n^2, \end{align*}

což platí i pro n0=1n_0 = 1

Dokázali jsme T(n)O(n2)T(n) \in O(n^2). Pro T(n)Ω(n2)T(n) \in \Omega(n^2) bychom postupovali obdobně, a z toho že T(n)O(n2)Ω(n2)T(n) \in O(n^2) \cap \Omega(n^2) získáme T(n)Θ(n2)T(n) \in \Theta(n^2)


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 xyx^y.

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(xp, p);
  abc(x);
  if (x > 0) ff(xp, 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 A\mathcal{A} dělí úlohu o velikosti nn 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ě n2n^2. Zapište rekurentní vztah pro asymptotickou složitost algoritmu A\mathcal{A}.


Úloha 11: Daný rekurzivní algoritmus pracuje tak, že pro n>1n > 1 data rozdělí na 33 čá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ě n1/2log2(n)n^{1/2} \cdot \log_2(n). 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.

  1. Seřaď levou polovinu pole (rekurzivním voláním).

  2. Seřaď pravou polovinu pole (rekurzivním voláním).

  3. 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 Θ(n)\Theta(n)? A co v případě, že to zkombinování má složitost Θ(n2)\Theta(n^2)?


Úloha 13: Složitost rekurzivního algoritmu je dána rekurencí:

a) T(n)=3T(n/2)+nT(n) = 3 \cdot T(\lfloor n/2 \rfloor) + n
b) T(n)=T(n/2)+n2T(n) = T(n/2) + n^2
c) T(n)=4T(n/2+2)+nT(n) = 4 \cdot T(n/2 + 2) + n
Použijte metodu stromu rekurze k nalezení vhodného (těsného) horního asymmptotického odhadu funkce T(n)T(n).

Ověřte výsledek substituční metodou.


Úloha 14: Složitost rekurzivního algoritmu je dána rekurencí:

a) T(n)=T(n1)+T(n/2)+nT(n) = T(n−1) + T(n/2) + n
b) T(n)=T(na)+T(a)+cnT(n) = T(n−a) + T(a) + cn, kde a1,c0a \ge 1, c \> 0
c) T(n)=T(αn)+T((1α)n)+cnT(n) = T(\alpha n) + T((1 − \alpha)n) + cn, kde 0<α<1,c>00 < \alpha < 1, c > 0.
Použijte metodu stromu rekurze k nalezení vhodného horního odhadu funkce T(n)T(n).


Úloha 15: Pro složitost rekurzivního algoritmu platí T(1) = 1. Pro každé n > 1 je T(n) dána rekurencí:

a) T(n)=k=1n1T(k)+1T(n) = \sum_{k=1}^{n-1} T(k) + 1
b) T(n)=k=1n1T(k)+7T(n) = \sum_{k=1}^{n-1} T(k) + 7
c) T(n)=k=1n1T(k)+n2T(n) = \sum_{k=1}^{n-1} T(k) + n^2
d) T(n)=2k=1n1T(k)+1T(n) = 2\sum_{k=1}^{n-1} T(k) + 1

Určete řád růstu funkce T(n)T(n).


Úloha 16: Sierpińského koberce

Prvky pole PNP_N o velikosti 3N×3N3^N \times 3^N jsou pouze čísla 11 a 00. Obrázky níže znázorňují pole PNP_N pro několik hodnot NN, modrá barva představuje hodnotu 11, bílá 00. Napište kód, který pro dané NN vytvoří a vyplní pole PP podle daného vzoru.

02++_++koberec

Úloha 17: Seznamte se s Ackermanovou funkcí.

A(n,m)={m+1n=0 A(n1,1)n>0,m=0 A(n1,A(n,m1))n>0,m>0 \begin{equation*}A(n, m) = \begin{cases} m + 1 & n = 0 \\\ A(n-1, 1) & n > 0, m = 0 \\\ A(n-1, A(n, m-1)) & n > 0, m > 0 \\\ \end{cases} \end{equation*}

Zkuste spočítat hodnotu A(4,4)A(4, 4).


Úloha 18: Násobení matic

Nechť XX a YY jsou matice tvaru n×nn \times n, kde n je přirozené číslo. Uvažujme násobení matic Z=XYZ = X \cdot Y podle definice pro násobení matic, tedy: zij=k=1nxikykjz_{ij} = \sum_{k=1}^{n} x_{ik} \cdot y_{kj}.

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 XX a YY rozdělíme na 4 stejně velké podmatice:

X=[ABCD]X = \begin{bmatrix} A & B \\ C & D \end{bmatrix}

Y=[EFGH]Y = \begin{bmatrix} E & F \\ G & H \end{bmatrix}

Pak můžeme využít vztahu:

XY=[AE+BGAF+BHCE+DGCF+DH]X \cdot Y = \begin{bmatrix} {AE + BG} & {AF + BH} \\ {CE + DG} & {CF + DH} \end{bmatrix}.

Jaká je asymptotická složitost takového rekurzivního algoritmu?

Jaká je asymptotická složitost Strassenova algoritmu, pro který platí:

XY=[T5+T4T2+T6T1+T2T3+T4T1+T5T3T7]X \cdot Y = \begin{bmatrix} {T_5 + T_4 - T_2 + T_6} & {T_1 + T_2} \\ {T_3 + T_4} & {T_1 + T_5 - T_3 - T_7} \end{bmatrix},

kde:

T1=A(FH)T_1 = A(F - H)
T2=(A+B)HT_2 = (A + B)H
T3=(C+D)ET_3 = (C + D)E
T4=D(GE)T_4 = D(G - E)
T5=(A+D)(E+H)T_5 = (A + D)(E + H)
T6=(BD)(G+H)T_6 = (B - D)(G + H)
T7=(AC)(E+F)T_7 = (A - C)(E + F)?


Je na stránce chyba?

Nahlašte nám ji

Máte otázky nebo připomínky?

Edit Dejte nám vědět