Skip to content
Snippets Groups Projects
Commit 008bb2d3 authored by Martin Mareš's avatar Martin Mareš
Browse files

Dynamické programování

parent a1b6b935
No related branches found
No related tags found
No related merge requests found
#!/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()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment