Dynamické programování v teorii řízení a teorii počítačových systémů je způsob řešení složitých problémů jejich rozdělením na jednodušší dílčí úkoly. Je použitelný pro problémy s optimální podstrukturou, které vypadají jako soubor překrývajících se dílčích problémů, jejichž složitost je o něco menší než u původního. V tomto případě může být doba výpočtu ve srovnání s "naivními" metodami výrazně zkrácena.
Klíčová myšlenka dynamického programování je poměrně jednoduchá. K vyřešení problému je zpravidla nutné vyřešit jednotlivé části problému (podproblém) a následně sloučit řešení dílčích úkolů do jednoho společného řešení. Mnoho z těchto dílčích úkolů je často stejných. Přístup dynamického programování spočívá v řešení každého dílčího problému pouze jednou, čímž se sníží počet výpočtů. To je užitečné zejména v případech, kdy je počet opakujících se dílčích úkolů exponenciálně velký.
Metoda dynamického programování shora je jednoduchým zapamatováním výsledků řešení těch dílčích problémů, se kterými se může v budoucnu znovu setkat. Dynamické programování zdola zahrnuje přeformulování složitého problému jako rekurzivní sekvence jednodušších dílčích problémů.
Fráze „dynamické programování“ byla poprvé použita ve 40. letech 20. století Richardem Bellmanem k popisu procesu hledání řešení problému, kdy odpověď na jeden problém lze získat až po vyřešení problému, který mu „předcházel“. V roce 1953 tuto definici zdokonalil na moderní. Obor byl původně založen jako systémová analýza a inženýrství, což bylo uznáno IEEE . Bellmanův příspěvek k dynamickému programování byl zvěčněn ve jménu Bellmanovy rovnice , ústředního výsledku teorie dynamického programování, která přeformuluje optimalizační problém v rekurzivní formě.
Slovo „programování“ ve spojení „dynamické programování“ ve skutečnosti nemá téměř nic společného s „tradičním“ programováním (psáním kódu) a dává smysl jako ve spojení „ matematické programování “, které je synonymem slova „optimalizace“. Proto slovo „program“ v tomto kontextu spíše znamená optimální sled akcí k získání řešení problému. Například konkrétní plán akcí na výstavě je někdy označován jako program. Program je v tomto případě chápán jako platný sled událostí.
Optimální podstruktura v dynamickém programování znamená, že k vyřešení původního problému lze použít optimální řešení menších dílčích problémů. Například nejkratší cestu v grafu z jednoho vrcholu (označeného s) do druhého (označeného t) lze nalézt následovně: nejprve vezmeme v úvahu nejkratší cestu ze všech vrcholů sousedících s s do t a poté vezmeme s ohledem na váhy hran, které spojují s se sousedními vrcholy, zvolíme nejlepší cestu k t (kterým vrcholem je nejlepší projít). V obecném případě můžeme problém, který má optimální podstrukturu, vyřešit provedením následujících tří kroků.
Podproblémy se řeší tak, že se rozdělují na ještě menší podproblémy a tak dále, dokud nedojdou k triviálnímu případu problému, který lze vyřešit v konstantním čase (odpověď lze říci okamžitě). Pokud například potřebujeme najít n!, pak 1! = 1 (nebo 0! = 1).
Překrývající se podproblémy v dynamickém programování znamenají podproblémy, které se používají k řešení řady problémů (nejen jednoho) větší velikosti (to znamená, že totéž děláme několikrát). Pozoruhodným příkladem je výpočet Fibonacciho posloupnosti a - i v takovém triviálním případě jsme již dvakrát počítali výpočty pouze dvou Fibonacciho čísel. Pokud budete pokračovat dále a počítat , pak se to započítá ještě dvakrát, protože znovu a bude potřeba pro výpočet . Ukazuje se následující: jednoduchý rekurzivní přístup stráví čas výpočtem řešení problémů, které již vyřešil.
Abychom se takovému průběhu událostí vyhnuli, uložíme si řešení podproblémů, které jsme již vyřešili, a až budeme znovu potřebovat řešení podproblému, místo přepočítávání jej jednoduše získáme z paměti. Tento přístup se nazývá memoizace . Můžete také provádět další optimalizace - například pokud jsme si jisti, že již nepotřebujeme řešit dílčí úlohu, můžeme ji vyhodit z paměti a uvolnit ji pro jiné potřeby, nebo pokud je procesor nečinný a víme, že řešení některých dílčích úkolů, které ještě nebyly spočítány, potřebujeme v budoucnu, můžeme je vyřešit předem.
Shrneme-li výše uvedené, můžeme říci, že dynamické programování využívá následující vlastnosti problému:
Dynamické programování obecně sleduje dva přístupy k řešení problémů:
Programovací jazyky si mohou zapamatovat výsledek volání funkce s určitou sadou argumentů ( memoization ), aby se urychlil „výpočet podle jména“. Některé jazyky mají tuto schopnost zabudovanou (např . Scheme , Common Lisp , Clojure , Perl , D ), zatímco jiné vyžadují další rozšíření ( C++ ).
Známé jsou sériové dynamické programování, které je obsaženo ve všech učebnicích operačního výzkumu , a nesériové dynamické programování (NSDP), které je v současnosti málo známé, ačkoli bylo objeveno v 60. letech 20. století.
Konvenční dynamické programování je speciálním případem nesériového dynamického programování, kde je graf vztahu proměnných pouze cestou. NSDP, která je přirozenou a obecnou metodou pro zohlednění struktury optimalizačního problému, považuje množinu omezení a/nebo účelovou funkci za rekurzivně vypočítatelnou funkci. To umožňuje najít řešení krok za krokem, v každé fázi pomocí informací získaných v předchozích fázích, a účinnost tohoto algoritmu přímo závisí na struktuře grafu proměnných vztahů. Pokud je tento graf dostatečně řídký, pak lze množství výpočtů v každé fázi udržet v rozumných mezích.
Jednou z hlavních vlastností problémů řešených pomocí dynamického programování je aditivnost . Neaditivní problémy se řeší jinými metodami. Například mnoho úkolů optimalizace investic společnosti není aditivní a řeší se porovnáním hodnoty společnosti s investicemi a bez nich.
![]() | ||||
---|---|---|---|---|
|