From 008bb2d36f5310b09fc3f699f1b2d1a2c862898e Mon Sep 17 00:00:00 2001
From: Martin Mares <mj@ucw.cz>
Date: Wed, 20 May 2020 11:37:45 +0200
Subject: [PATCH] =?UTF-8?q?Dynamick=C3=A9=20programov=C3=A1n=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
12-dp/obdelnik-12.py | 34 ++++++++++++++++
12-dp/obdelnik-1k.py | 7 ++++
12-dp/rozklad-na-slova.py | 82 +++++++++++++++++++++++++++++++++++++++
3 files changed, 123 insertions(+)
create mode 100644 12-dp/obdelnik-12.py
create mode 100644 12-dp/obdelnik-1k.py
create mode 100644 12-dp/rozklad-na-slova.py
diff --git a/12-dp/obdelnik-12.py b/12-dp/obdelnik-12.py
new file mode 100644
index 0000000..dab4351
--- /dev/null
+++ b/12-dp/obdelnik-12.py
@@ -0,0 +1,34 @@
+#!/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
diff --git a/12-dp/obdelnik-1k.py b/12-dp/obdelnik-1k.py
new file mode 100644
index 0000000..20322a1
--- /dev/null
+++ b/12-dp/obdelnik-1k.py
@@ -0,0 +1,7 @@
+#!/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))
diff --git a/12-dp/rozklad-na-slova.py b/12-dp/rozklad-na-slova.py
new file mode 100644
index 0000000..b4fd1ab
--- /dev/null
+++ b/12-dp/rozklad-na-slova.py
@@ -0,0 +1,82 @@
+#!/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()
--
GitLab