Commit f4fce2fd authored by Martin Mareš's avatar Martin Mareš
Browse files

Rekurze: Příklady

parent 97d5a67e
#!/usr/bin/python3
# Různé způsoby výpočtu Fibonacciho čísel
### Čistá rekurzivní verze, exponenciálně pomalá
def fib(n):
if n < 2:
return n
else:
return fib(n-1) + fib(n-2)
### Rekurze s pamětí na už spočítané výsledky, složitost O(n)
pamet = { 0: 0, 1: 1 }
def fib1(n):
if n not in pamet:
pamet[n] = fib(n-1) + fib(n-2)
return pamet[n]
### Iterativní řešení
def fib2(n):
p = [0] * (n+1)
p[1] = 1
for i in range(2, n+1):
p[i] = p[i-1] + p[i-2]
return p[n]
#!/usr/bin/python3
# Generování všech posloupností 0 a 1 délky n
# Posloupnosti vypisujeme v lexikografickém pořadí
### První řešení: rekurzivní funkce, která vygeneruje všechny posloupnosti
### začínající zadaným prefixem. Parametr "p" je prefix, "n" říká, kolik ještě
### potřebujeme přidat prvků.
def gen01(n, p=[]):
if n == 0:
print("".join(map(str, p)))
else:
gen01(n-1, p + [0])
gen01(n-1, p + [1])
### V předchozím řešení se v každém volání funkce kopíruje seznam až n prvků,
### takže i vnitřní vrcholy stromu rekurze mají linearní složitost. Toho se můžeme
### zbavit, když prefix místo v argumentu funkce předáváme v proměnné sdílené
### všemi instancemi funkce.
def gen01b(n):
def g(n):
if n == 0:
print("".join(map(str, p)))
else:
for i in [0, 1]:
p.append(i)
g(n-1)
p.pop()
p = []
g(n)
### Funkci gen01 můžeme také upravit, aby místo vygenerování všech posloupností
### jenom spočítala, kolik jich je. Na prefixu nezáleží, tak ho nemusíme předávat.
### Díky tomu stačí jen jedno rekurzivní volání a výsledek znásobit dvěma,
### takže funkce má celkově složitost O(n).
def count01(n):
if n == 0:
return 1
else:
return 2 * count01(n-1)
#!/usr/bin/python3
# Generování všech posloupností 0 a 1 délky n, ve kterých je právě k jedniček
# Posloupnosti vypisujeme v lexikografickém pořadí
### Rekurzivní řešení: generujeme všechny posloupnosti délky s prefixem p,
### do kterých potřebujeme přidat n cifer, z nichž k jsou jedničky.
def genk1(n, k, p=[]):
if n == 0:
print("".join(map(str, p)))
else:
# 0 můžeme přidat, pokud zbude dost pozic na to, aby se do nich
# vešly všechny zbývající 1.
if k < n:
genk1(n-1, k, p + [0])
# 1 můžeme přidat, pokud jsme ještě všechny nespotřebovali.
if k > 0:
genk1(n-1, k-1, p + [1])
### A opět můžeme podobným způsobem počítat, kolik takových posloupností existuje:
def countk1(n, k):
if n == 0:
return 1
else:
result = 0
if k < n:
result += countk1(n-1, k)
if k > 0:
result += countk1(n-1, k-1)
return result
### Počítačí funkce je ovšem pomalá: výsledek je kombinační číslo (poznáváte
### v naší funkci součtový vzorec z Pascalova trojúhelníku?) a nasčítáme ho
### postupně z jedniček. Přitom "n nad n/2" je řádově 2^n / √n. Stejně jako
### u příkladu s Fibonacciho čísly si můžeme mezivýsledky pamatovat a tím
### výpočet zrychlit na O(n^2). Mimochodem, uměli byste to rychleji, aniž
### byste potřebovali mezivýsledky řádově větší než výsledek?
mem = {} # Slovník, kde klíče jsou tuply (n,k)
def countk1b(n, k):
if (n,k) not in mem:
if n == 0:
result = 1
else:
result = 0
if k < n:
result += countk1b(n-1, k)
if k > 0:
result += countk1b(n-1, k-1)
mem[(n,k)] = result
return mem[(n,k)]
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment