Dynamické programování

Podobné dokumenty
2. úkol MI-PAA. Jan Jůna (junajan)

Prioritní fronta, halda

Programování: základní konstrukce, příklady, aplikace. IB111 Programování a algoritmizace

Časová a prostorová složitost algoritmů

Rekurze a rychlé třídění

Binární soubory (datové, typované)

Dynamické programování UIN009 Efektivní algoritmy 1

5. Dynamické programování

Dynamické programování

8. Rekurze. doc. Ing. Jiří Vokřínek, Ph.D. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze

Dynamické programování

Návrh Designu: Radek Mařík

Kombinatorika, výpočty

Algoritmizace Dynamické programování. Jiří Vyskočil, Marko Genyg-Berezovskyj 2010

Rekurze. Pavel Töpfer, 2017 Programování 1-8 1

Prohledávání do šířky = algoritmus vlny

Programy a algoritmy pracující s čísly. IB111 Úvod do programování skrze Python

Implementace LL(1) překladů

Časová složitost / Time complexity

IB111 Úvod do programování skrze Python

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

3. úloha - problém batohu metodami branch & bound, dynamické programování, heuristika s testem

Dynamické programování. Optimální binární vyhledávací strom

Třídění a vyhledávání Searching and sorting

Slepé prohledávání do šířky Algoritmus prohledávání do šířky Při tomto způsobu prohledávání máme jistotu, že vždy nalezneme koncový stav, musíme ale p

Spojový seznam. Jan Kybic.

10. Složitost a výkon

Test prvočíselnosti. Úkol: otestovat dané číslo N, zda je prvočíslem

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Rekurze. IB111 Úvod do programování skrze Python

Řešení: PŘENESVĚŽ (N, A, B, C) = přenes N disků z A na B pomocí C

Vzdálenost uzlů v neorientovaném grafu

Prohledávání stavového prostoru

IAJCE Přednáška č. 8. double tprumer = (t1 + t2 + t3 + t4 + t5 + t6 + t7) / 7; Console.Write("\nPrumerna teplota je {0}", tprumer);

Hanojská věž. T2: prohledávání stavového prostoru. zadání [1 1 1] řešení [3 3 3] dva možné první tahy: [1 1 2] [1 1 3]

DYNAMICKÉ PROGRAMOVÁNÍ A PROBLÉM BATOHU

Algoritmy I, složitost

Dynamické programování

Stromy. Strom: souvislý graf bez kružnic využití: počítačová grafika seznam objektů efektivní vyhledávání výpočetní stromy rozhodovací stromy

Třídy složitosti P a NP, NP-úplnost

IB111 Úvod do programování skrze Python

Grafové algoritmy. Programovací techniky

Algoritmizace řazení Bubble Sort

Rozklad problému na podproblémy

Zdůvodněte, proč funkce n lg(n) roste alespoň stejně rychle nebo rychleji než než funkce lg(n!). Symbolem lg značíme logaritmus o základu 2.

Konečný automat. Jan Kybic.

Obecná informatika. Matematicko-fyzikální fakulta Univerzity Karlovy v Praze. Podzim 2012

Dynamic programming. Historie. Dynamické programování je obsaženo v těchto programech: Příklad: chceme optimálně přiložit dvě sekvence

Stromy, haldy, prioritní fronty

VYŠŠÍ ODBORNÁ ŠKOLA a STŘEDNÍ PRŮMYSLOVÁ ŠKOLA Mariánská 1100, Varnsdorf PROGRAMOVÁNÍ FUNKCE, REKURZE, CYKLY

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

R zné algoritmy mají r znou složitost

1 Základní funkce pro zpracování obrazových dat

Stromy. Jan Kybic.

B3B33ALP - Algoritmy a programování - Zkouška z předmětu B3B33ALP. Marek Boháč bohacm11

Rekurzivní algoritmy

5 Rekurze a zásobník. Rekurzivní volání metody

Přednáška 3. Rekurze 1

Grafové algoritmy. Programovací techniky

Programy a algoritmy pracující s čísly. IB111 Úvod do programování skrze Python

Syntaktická analýza. Implementace LL(1) překladů. Šárka Vavrečková. Ústav informatiky, FPF SU Opava

Content Aware Image Resizing

Programování v C++, 2. cvičení

Rozptylovací tabulky

Programování v C++ 2, 8. cvičení

12. Globální metody MI-PAA

Použití dalších heuristik

MI-PAA. úkol č.3. Řešení problému batohu dynamickým programováním, metodou větví a hranic a aproximativním algoritmem

1. LINEÁRNÍ ALGEBRA Vektory Operace s vektory... 8 Úlohy k samostatnému řešení... 8

Dynamické datové struktury I.

PODOBÁ SE JAZYKU C S NĚKTERÝMI OMEZENÍMI GLOBÁLNÍ PROMĚNNÉ. NSWI162: Sémantika programů 2

Programování v Pythonu

Dynamické programování

Metody návrhu algoritmů, příklady. IB111 Programování a algoritmizace

68. ročník Matematické olympiády 2018/2019

Programy a algoritmy pracující s čísly. IB111 Úvod do programování

Úvod do programování 10. hodina

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

% vyhledání prvku s max. velikostí v jednotlivých sloupcích matice X

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

TGH06 - Hledání nejkratší cesty

Datové struktury. Obsah přednášky: Definice pojmů. Abstraktní datové typy a jejich implementace. Algoritmizace (Y36ALG), Šumperk - 12.

Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague

Zdroj:

B3B33ALP - Algoritmy a programování - Zkouška z předmětu B3B33ALP. Marek Boháč bohacm11

Základní datové struktury III: Stromy, haldy

V každém kroku se a + b zmenší o min(a, b), tedy vždy alespoň o 1. Jestliže jsme na začátku dostali 2

4EK201 Matematické modelování. 2. Lineární programování

bfs, dfs, fronta, zásobník, prioritní fronta, halda

bfs, dfs, fronta, zásobník, prioritní fronta, halda

Reprezentace aritmetického výrazu - binární strom reprezentující aritmetický výraz

Numerická stabilita algoritmů

Experimentální systém pro WEB IR

NEJKRATŠÍ CESTY I. Doc. RNDr. Josef Kolář, CSc. Katedra teoretické informatiky, FIT České vysoké učení technické v Praze

Příklad 1/23. Pro rostoucí spojité fukce f(x), g(x) platí f(x) Ω(g(x)). Z toho plyne, že: a) f(x) Ο(g(x)) b) f(x) Θ(g(x)) d) g(x) Ω(f(x))

PŘIJÍMACÍ TEST z informatiky a matematiky pro navazující magisterské studium Fakulta informatiky a managementu Univerzity Hradec Králové

Hledání kořenů rovnic jedné reálné proměnné metoda sečen Michal Čihák 23. října 2012

ČVUT FEL X36PAA - Problémy a algoritmy. 5. úloha - Seznámení se se zvolenou pokročilou iterativní metodou na problému batohu

Da D to t v o é v ty t py IB111: Datové typy

Abstraktní datové typy FRONTA

Transkript:

Dynamické programování Jan Kybic http://cmp.felk.cvut.cz/~kybic kybic@fel.cvut.cz 2016 1 / 26

Memoizace Dynamické programování 2 / 26

Memoizace (Memoization/caching) Pro dlouhotrvající funkce f (x) Jednou vypočtené f (x) si zapamatujeme Vyměníme prostor za čas (time-space trade-off) Funkce musí být čistá (pure) 3 / 26

Memoizace implementace Pro funkce jednoho argumentu. Vrátí funkci, která se chová jako f, jen jednou vypočítané hodnoty nejsou počítány znovu. def memoize(f): memo={} def g(x): if x not in memo: memo[x]=r=f(x) return r return memo[x] return g 4 / 26

Memoizace použití def is_prime(p): if p<2: return False n=2 while n*n<=p: if p % n == 0: return False n+=1 return True g=memoize(is_prime) Soubor memoize.py. 5 / 26

Memoizace se zapomínáním Last recently used (LRU) Nechceme si pamatovat vše Zapomeneme hodnotu použitou před nejdelší dobou (LRU last recently used) 6 / 26

Memoizace se zapomínáním Last recently used (LRU) Nechceme si pamatovat vše Zapomeneme hodnotu použitou před nejdelší dobou (LRU last recently used) V Pythonu (varianta bez zapomínání) h=functools.lru_cache(maxsize=none)(is_prime) 6 / 26

Memoizace měření času Otestuje čísla 1, 2,... 10 5 na prvočíselnost. >>> memoize.try_memoize_primes() Bez memoizace: 1.41s S memoizací poprvé: 1.51s S memoizací podruhé: 0.06s S lru_cache poprvé: 1.75s S lru_cache podruhé: 0.23s Soubor memoize.py. 7 / 26

Fibonacciho čísla F N = { 1 if N < 2 F N 1 + F N 2 if N 2 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,... Rekurzivní implementace: def fibonacci(n): if n <= 2: return 1 else: return fibonacci(n-1) + fibonacci(n-2) 8 / 26

Fibonacciho čísla F N = { 1 if N < 2 F N 1 + F N 2 if N 2 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,... Rekurzivní implementace: def fibonacci(n): if n <= 2: return 1 else: return fibonacci(n-1) + fibonacci(n-2) Časová složitost je F N φ N, kde φ 1.618. 8 / 26

Fibonacciho čísla F N = { 1 if N < 2 F N 1 + F N 2 if N 2 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,... Rekurzivní implementace: def fibonacci(n): if n <= 2: return 1 else: return fibonacci(n-1) + fibonacci(n-2) Časová složitost je F N φ N, kde φ 1.618. Memoizace odstraní rekurzivní volání. Soubor memoize.py. 8 / 26

Fibonacciho čísla a memoizace def fibonacci2(n): if n <= 2: return 1 else: return fibonacci3(n-1) + fibonacci3(n-2) fibonacci3=memoize(fibonacci2) 9 / 26

Fibonacciho čísla a memoizace def fibonacci2(n): if n <= 2: return 1 else: return fibonacci3(n-1) + fibonacci3(n-2) fibonacci3=memoize(fibonacci2) fibonacci=memoize(fibonacci) funguje také. 9 / 26

Fibonacciho čísla a memoizace def fibonacci2(n): if n <= 2: return 1 else: return fibonacci3(n-1) + fibonacci3(n-2) fibonacci3=memoize(fibonacci2) fibonacci=memoize(fibonacci) funguje také. Python má tzv. dekorátory (decorators), např. @memoize. 9 / 26

Fibonacciho čísla iterativně specializovaná memoizace pro F N budeme potřebovat F 1, F 2,..., F N 1 def fibonacci4(n): p=[ 1 for i in range(n+1) ] for i in range(3,n+1): p[i]=p[i-1]+p[i-2] return p[n] 10 / 26

Fibonacciho čísla iterativně specializovaná memoizace pro F N budeme potřebovat F 1, F 2,..., F N 1 def fibonacci4(n): p=[ 1 for i in range(n+1) ] for i in range(3,n+1): p[i]=p[i-1]+p[i-2] return p[n] Stačí si pamatovat dvě poslední hodnoty. Soubor memoize.py. 10 / 26

Experimentální vyhodnocení časové složitosti N 25 30 40 400 4000 40000 fibonacci rekurzivní 0.06 0.62 75.44 S S S fibonacci3 memoizace 0.00 0.00 0.00 0.00 R R fibonacci4 iterativní 0.00 0.00 0.00 0.00 0.00 0.16 Čas v sekundách. S neskončí v rozumném čase R překročená maximální hloubka rekurze Memoizace vyrobila z rekurzivního algoritmu nerekurzivní. 11 / 26

Memoizace Dynamické programování 12 / 26

Dynamické programování Dynamic programming Problém rozdělíme na několik menších. Menší problémy vyřešíme systematicky všechny a výsledky uložíme. Explicitní memoizace. Pro řešení většího problému využíváme dříve předpočítaných řešení. 13 / 26

Dynamické programování Dynamic programming Problém rozdělíme na několik menších. Menší problémy vyřešíme systematicky všechny a výsledky uložíme. Explicitní memoizace. Pro řešení většího problému využíváme dříve předpočítaných řešení. Příklad: Fibonacciho posloupnost fibonacci4 13 / 26

Dynamické programování Dynamic programming Problém rozdělíme na několik menších. Menší problémy vyřešíme systematicky všechny a výsledky uložíme. Explicitní memoizace. Pro řešení většího problému využíváme dříve předpočítaných řešení. Příklad: Fibonacciho posloupnost fibonacci4 programování = reformulace problému 13 / 26

Bellmanův princip optimality Ve stavovém prostoru hledejme cestu mezi stavy s a t. Nechť stav r leží na optimální cestě: p 1 = s,..., p M = r,..., p N = t. Pak i podcesty p 1 = s,..., p M = r a p M = r,..., p N = t jsou optimální. 14 / 26

Dynamické programování a stavový prostor Nechť kritérium optimality je součet cen akcí a/nebo stavů, které minimalizujeme. C = N i=1 N 1 C(p i ) + C(a i ), kde p i+1 = f (p i, a i ) i=1 Nechť délka řešení je maximálně N kroků. Nechť v každém kroku existuje množina R i stavů, kudy musí optimální cesta procházet. Nechť se do stavů R i+1 z R i mohu dostat maximálně k způsoby. 15 / 26

Dynamické programování a stavový prostor (2) Problém lze řešit dynamickým programováním takto: V každém kroku i si pamatuji, jaká je nejlepší cena D i (r) dosažení každého stavu r z R i, optimálního předchůdce a akci. Při výpočtu ceny stavů u R i+1 v dalším kroku vyberu nejlepší z možných akcí D i+1 (u) = min a A D i(r) + C(a) + C(u) kde u = f (r, a) Po dosažení cílového stavu zpětně rekonstruuji optimální cestu (je-li nutno). Složitost tohoto algoritmu je O(kN). 16 / 26

Dynamické programování a stavový prostor (2) Problém lze řešit dynamickým programováním takto: V každém kroku i si pamatuji, jaká je nejlepší cena D i (r) dosažení každého stavu r z R i, optimálního předchůdce a akci. Při výpočtu ceny stavů u R i+1 v dalším kroku vyberu nejlepší z možných akcí D i+1 (u) = min a A D i(r) + C(a) + C(u) kde u = f (r, a) Po dosažení cílového stavu zpětně rekonstruuji optimální cestu (je-li nutno). Složitost tohoto algoritmu je O(kN). Existuje mnoho variant tohoto základního postupu. 16 / 26

Příklad: Mince Řešili jsme, jak vypsat všechny způsoby, jak zaplatit x Kč mincemi v hodnotách h = {50, 20, 10, 5, 2, 1} Kč. Nyní najděme nejmenší počet mincí, kterými lze zaplatit x Kč. 17 / 26

Příklad: Mince Řešili jsme, jak vypsat všechny způsoby, jak zaplatit x Kč mincemi v hodnotách h = {50, 20, 10, 5, 2, 1} Kč. Nyní najděme nejmenší počet mincí, kterými lze zaplatit x Kč. Hladový (greedy) algoritmus nefunguje pro všechny h. 17 / 26

Mince rekurzivně Jak zaplatit částku x pomocí co nejmenšího počtu mincí o hodnotách h (pole). Vrátí pole počtů m i mincí h i. Myšlenka: Rekurzivně zkus zaplatit x h i a vyber nejlepší možnost. 18 / 26

Mince rekurzivně def zaplat_nejlepe(h,x): def zaplat_internal(x): if x in h: # existuje mince s hodnotou x? return [ int(x==v) for v in h ] else: nejlepsi=none # nejlepší pole počtů m for i in range(len(h)): # pro všechny mince if h[i]<=x: # které lze použít m=zaplat_internal(x-h[i]) m[i]+=1 if nejlepsi is None or sum(m)<sum(nejlepsi): nejlepsi=m.copy() return nejlepsi return zaplat_internal(x) Funguje, ale má exponenciální složitost, použitelné jen asi do x = 35 (40 s). Soubor dynamic.py. 18 / 26

Mince memoizace def zaplat_nejlepe2(h,x): def zaplat_internal(x): # existuje mince s hodnotou x? if x in h: return [ int(x==v) for v in h ] else: nejlepsi=none for i in range(len(h)): if h[i]<=x: m=zaplat_internal(x-h[i]) m[i]+=1 if nejlepsi is None or sum(m)<sum(nejlepsi): nejlepsi=m.copy() return nejlepsi zaplat_internal=memoize.memoize(zaplat_internal) return zaplat_internal(x) Funguje mnohem rychleji, použitelné asi do x = 24500 (0.42 s), pak dojde k překročení hloubky zásobníku. 19 / 26

Mince dynamické programování def zaplat_nejlepe3(h,x): reseni=[ [] ] * (x+1) # nej. řešení pro zapl. x reseni[0]=[0] * len(h) for y in range(1,x+1): # jak zaplatit y=1..x Kč nejlepsi_i=none # kterou minci je nejlépe použít nejlepsi_pocet=none # kolik mincí bude pak potřeba for i in range(len(h)): # zkus všechny mince if h[i]<=y: # lze ji použít? pocet=sum(reseni[y-h[i]])+1 # tolik mincí je potřeba if nejlepsi_i is None or pocet<nejlepsi_pocet: nejlepsi_i=i nejlepsi_pocet=pocet if nejlepsi_i is None: # pro jistotu return None reseni[y]=reseni[y-h[nejlepsi_i]].copy() reseni[y][nejlepsi_i]+=1 return reseni[x] Nejrychlejší řešení, složitost zhruba lineární, O(x) (0.02 s pro x = 2450, 0.24 s pro x = 24500, 2.7 s pro x = 245000) 20 / 26

Příklad: hledání lineárních struktur v obraze Rentgenový (CT) obraz dolních končetin. Najděte nejjasnější spojnici první a poslední řádky obrazu. Maximalizujte: n y J = f (i, p i ) i=0 aby p i p i 1 <= 1 f (y, x) = jas pixelu na souřadnicích (y, x) 21 / 26

Knihovna numpy Numeric Python >>> import numpy as np >>> a=np.zeros((3,4),dtype=np.uint8) >>> a[1,2]=3 >>> a array([[0, 0, 0, 0], [0, 0, 3, 0], [0, 0, 0, 0]], dtype=uint8) Typ numpy.array Vícerozměrná pole, vícerozměrné indexování. Definovatelný a homogenní typ elementů. Efektivnější využití paměti než nativní typ list. Interoperabilita s jinými programovacími jazyky. Nepodporuje dynamickou změnu rozměrů, vyhledávání atp. Podporuje řadu matematických vektorových a maticových operací (à la Matlab) 22 / 26

Načtení a zobrazení obrázku import scipy import scipy.misc import pylab import numpy as np # Scientific Python # pro čtení obrázků # pro zobrazování a grafy # numeric arrays def test_vessels(): # načti obrázek do numpy.array pole f=scipy.misc.imread( figs/limb_vessels2.jpg ) pylab.figure(1) # první okno na obrazovce pylab.imshow(f,cmap=pylab.cm.gray) # ukaž šedotónový obrázek p=find_vertical(f) # najdi spojnici, vrať x souřad. pylab.figure(2) # druhé okno na obrazovce pylab.imshow(f,cmap=pylab.cm.gray) # ukaž obrázek znovu pylab.plot(p,range(len(p)), r. ) # nakresli spojnici červeně pylab.show() # počkej, až uživatel okna zavře Možností načítání a zobrazování obrázků je více. Soubor vessels.py. 23 / 26

Hledání spojnice a dynamické programování def find_vertical(f): ny,nx=f.shape # velikost obrázku c=np.zeros(nx) # ceny sloupců pro aktuální řadu cnew=np.zeros(nx) # ceny sloupců pro novou řadu delta=np.zeros((ny,nx),dtype=np.int8) # posun p[i]-p[i-1] for iy in range(ny): # první průchod shora dolů for ix in range(nx): # pro všechny sloupce cnew[ix],delta[iy,ix]=max( # nejlepší cesta do (iy,ix) (c[ix+d]+f[iy,ix],d) for d in (-1,0,1) if ix+d<nx and ix-d>=0 ) cnew,c=c,cnew # vyměň odkazy na pracovní pole p=np.zeros(ny,dtype=np.int) p[ny-1]=np.argmax(c) for iy in range(ny-1,0,-1): p[iy-1]=p[iy]+delta[iy,p[iy]] return p # výsledná cesta # začni v nejlepším sloupci # zpětný průchod 24 / 26

Výsledek 25 / 26

Náměty na domácí práci Implementujte iterativní výpočet Fibonacciho čísel na základě dvou předchozích hodnot. Jak závisí zrychlení na velikosti LRU paměti v uvedených příkladech memoizace? Jaká je složitost algoritmu nalezení minimálního počtu mincí pro zaplacení dané částky vzhledem k počtu druhů mincí? Řešte úlohu minimálního počtu mincí pro zaplacení dané částky, pokud je počet mincí dané hodnoty omezen. Řešte úlohu batohu (dynamickým programováním): Je dáno N položek s váhou m i a hodnotou c i. Kolik kterých položek mám vybrat, aby váha nepřekročila M a hodnota byla maximální? Upravte řešení problému mincí dynamickým programováním, aby nebylo potřeba ukládat vždy celé řešení, ale jen naposledy přidanou minci. Upravte hledání jasné cesty v obrazu tak, aby cesta nemusela být pouze svislá. 26 / 26