From f4fce2fda8cae3e040f4e86404f5a9c9f12bcc6e Mon Sep 17 00:00:00 2001 From: Martin Mares <mj@ucw.cz> Date: Wed, 15 Apr 2020 18:37:27 +0200 Subject: [PATCH] =?UTF-8?q?Rekurze:=20P=C5=99=C3=ADklady?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 07-rekurze/fib.py | 28 +++++++++++++++++++++++ 07-rekurze/gen-01.py | 44 ++++++++++++++++++++++++++++++++++++ 07-rekurze/gen-k1.py | 54 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100755 07-rekurze/fib.py create mode 100755 07-rekurze/gen-01.py create mode 100755 07-rekurze/gen-k1.py diff --git a/07-rekurze/fib.py b/07-rekurze/fib.py new file mode 100755 index 0000000..71d3597 --- /dev/null +++ b/07-rekurze/fib.py @@ -0,0 +1,28 @@ +#!/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] diff --git a/07-rekurze/gen-01.py b/07-rekurze/gen-01.py new file mode 100755 index 0000000..5a7d287 --- /dev/null +++ b/07-rekurze/gen-01.py @@ -0,0 +1,44 @@ +#!/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) diff --git a/07-rekurze/gen-k1.py b/07-rekurze/gen-k1.py new file mode 100755 index 0000000..61e7de6 --- /dev/null +++ b/07-rekurze/gen-k1.py @@ -0,0 +1,54 @@ +#!/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)] -- GitLab