Commit 008bb2d3 authored by Martin Mareš's avatar Martin Mareš
Browse files

Dynamické programování

parent a1b6b935
#!/usr/bin/python3
# Počet dláždění obdélníka 1×n kostkami 1×1 a 1×2
# Rekurzivní funkce s exponenciální složitostí
def d(n):
if n <= 1:
return 1
return d(n-1) + d(n-2)
# Všimli jsme si, že d() počítá spoustu podproblémů opakovaně,
# tak jsme dodělali kešování známých mezivýsledků. Tím se časová
# složitost zlepšila na O(n), ale chvíli trvá to dokázat.
pamet = { 0: 1, 1: 1 }
def d2(n):
if n not in pamet:
pamet[n] = d2(n-1) + d2(n-2)
return pamet[n]
# Tady počítáme tytéž podproblémy od nejmenšího k největšímu.
# Rekurzi jsme nahradili obyčejným cyklem, složitost je evidentně O(n).
def d3(n):
D = [1, 1] + [0]*n
for i in range(2, n+1):
D[i] = D[i-1] + D[i-2]
return D[n]
# Konečně jsme si všímli, že není potřeba pamatovat si všechny
# mezivýsledky, ale stačí předchozí dva. Tím jsme snížili prostorovou
# složitost na konstantní.
def d4(n):
a = b = 1
for i in range(2, n+1):
a, b = a+b, a
return a
#!/usr/bin/python3
# Počet dláždění obdélníka 1×n kostkami 1×k (kde k je libovolné)
def d(n):
if n == 0:
return 1
return sum(d(i) for i in range(n))
#!/usr/bin/python3
# Rozklad řetězce na posloupnost slov ze slovníku.
# Načítání slovníku ze souboru
slovnik = set()
def nacti_slovnik():
with open('slovnik') as f:
for slovo in f:
slovnik.add(slovo.strip())
# První pokus: rekurzivní rozkládání. Zkoušíme všechny možnosti,
# jaké slovo může být první, a pro každou z nich rekurzivně zjistíme,
# zda se zbytek řetězce dá rozložit. V nejhorším případě to trvá
# exponenciálně dlouho (například pro slovník {"a","aa"} a řetězec
# ze samých a-ček je tato úloha ekvivalentní s rozkladem obdélníka,
# který jsme už zkoumali).
def rozloz(retezec):
if retezec == "":
return True
for i in range(len(retezec)+1):
if retezec[:i] in slovnik and rozloz(retezec[i:]):
return True
return False
# Všimli jsme si, že všechny podproblémy jsou suffixy původního řetězce,
# takže jako parametr místo řetězce předáváme pozici v původním řetězci
# (začátek suffixu). Také bychom si mohli všimnout (ale ještě jsme toho
# nevyužili), že možných podproblémů je málo a začít kešovat.
def rozloz2(retezec):
def roz(p):
"""Rozlož retezec[p:]"""
if p == len(retezec):
return True
return any(retezec[p:q] in slovnik and roz(q) for q in range(p+1, len(retezec)+1))
return roz(0)
# Podproblémy řešíme od nejkratšího suffixu k nejdelšímu, rekurzi jsme
# nahradili cyklem. Časová složitost činí O(n³).
def rozloz3(retezec):
n = len(retezec)
umim = [None]*(n+1)
umim[n] = True
for p in range(n-1, -1, -1):
umim[p] = any(retezec[p:q] in slovnik and umim[q] for q in range(p+1, n+1))
return umim[0]
# A teď nás zajímá nejen, jestli rozklad existuje, ale také jak nějaký konkrétní
# rozklad vypadá. Pro každý suffix si zapamatujeme, které slovo (stačí jeho délka)
# fungovalo jako první v suffixu. Podle toho pak rozklad sestrojíme.
def rozloz4(retezec):
n = len(retezec)
umim = [None]*(n+1)
umim[n] = 0
# Hlavní výpočet
for p in range(n-1, -1, -1):
for q in range(p+1, n+1):
if retezec[p:q] in slovnik and umim[q] is not None:
umim[p] = q-p
# Rekonstrukce rozkladu
if umim[0] is None:
return None
else:
rozklad = []
p = 0
while p < n:
rozklad.append(retezec[p:p+umim[p]])
p += umim[p]
return rozklad
return umim[0]
nacti_slovnik()
Markdown is supported
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