https://en.wikipedia.org/wiki/Dynamic_programming
https://medium.com/@codingfreak/top-50-dynamic-programming-practice-problems-4208fed71aa3
https://stackoverflow.com/questions/1065433/what-is-dynamic-programming
Programowanie dynamiczne jest techniką lub strategią projektowania algorytmów, stosowaną przeważnie do rozwiązywania zagadnień optymalizacyjnych. Jest alternatywą dla niektórych zagadnień rozwiązywanych za pomocą algorytmów zachłannych. Wynalazcą techniki jest amerykański matematyk Richard Bellman.
Programowanie dynamiczne ma na celu wykorzystanie zalet podejścia rekurencyjnego (prostota, przejrzystość) i eliminację jego wad (zasobożerność). Konstrukcja programu wykorzystującego zasadę programowania dynamicznego może być sformułowana w trzech punktach.
Programowanie dynamiczne przypomina metodę dziel i zwyciężaj, ponieważ w obu metodach wykorzystujemy rekurencję. Jednak w metodzie dziel i zwyciężaj podproblemy są rozłączne (np. przy sortowaniu są to różne fragmenty tablicy). W programowaniu dynamicznym wykorzystujemy powtarzanie się podproblemów na kolejnych etapach rekurencji. Liczba istotnie różnych podproblemów jest wielomianowa, co wykorzystuje metoda programowania dynamicznego. W metodzie siłowej wiele razy rozwiązuje się te same problemy, co zwykle prowadzi do ponadwielomianowej złożoności.
Problem można rozwiązać metodą programowania dynamicznego, jeżeli ma dwie kluczowe właściwości.
Technika programowania dynamicznego może być zastosowana do problemu obliczania wyrazów ciągu Fibonacciego.
# Programowanie dynamiczne wstępujące, wersja z listą. def fibonacci(n): F = [0] + n * [1] # trzymamy wszystkie wartości for i in range(2, n+1): F[i] = F[i-1] + F[i-2] return F[n]
# Programowanie dynamiczne wstępujące, wersja ze słownikiem. def fibonacci(n): F = {0:0, 1:1} # trzymamy wszystkie wartości for i in range(2, n+1): F[i] = F[i-1] + F[i-2] return F[n]
W technice programowania dynamicznego zstępującego wartości obliczne są tylko wtedy, gdy są potrzebne.
# Programowanie dynamiczne zstępujące. FIBONACCI = {0:0, 1:1} # globalny słownik def fibonacci(n): global FIBONACCI if n not in FIBONACCI: FIBONACCI[n] = fibonacci(n-1) + fibonacci(n-2) return FIBONACCI[n]
# Programowanie dynamiczne zstępujące. # Słownik z wynikami ukryty w instancji klasy. # Można wykorzystać wyniki z poprzednich wywołań funkcji. class FibonacciClass: def __init__(self): self.cache = {0:0, 1:1} def __call__(self, n): if n not in self.cache: self.cache[n] = self(n-1) + self(n-2) return self.cache[n] fibonacci = FibonacciClass() print ( fibonacci(10) ) print ( fibonacci(20) ) print ( fibonacci.cache ) # podgląd zapamiętanych wyników
Problem plecakowy (ang. knapsack problem) należy do problemów optymalizacyjnych. Wersja decyzyjna problemu plecakowego jest problemem NP-zupełnym i jest jednym z 21 NP-zupełnych problemów Karpa. Problem polega na znalezieniu takiego podzbioru elementów, aby suma wartości była jak największa, a suma wag nie była większa od danej pojemności plecaka. Nie można wybierać ułamkowch części elementów. Metody rozwiązywania problemu plecakowego.
Wikipedia, Knapsack problem, https://en.wikipedia.org/wiki/Knapsack_problem
Cormen s. 363.
Dynamic Programming — Rod Cutting Problem
http://algorithms.tutorialhorizon.com/dynamic-programming-rod-cutting-problem/
Dynamic Programming — Minimum Cost Path Problem
http://algorithms.tutorialhorizon.com/dynamic-programming-minimum-cost-path-problem/
Problem znajdowania podmacierzy o maksymalnej sumie elementów. Oryginalna wersja algorytmu dotyczy macierzy jednowymiarowej, ale istnieją uogólnienia na większą liczbę wymiarów.
https://en.wikipedia.org/wiki/Maximum_subarray_problem
W bioinformatyce programowanie dynamiczne pojawia się w algorytmie Needlemana-Wunscha i innych algorytmach.
https://en.wikipedia.org/wiki/Needleman%E2%80%93Wunsch_algorithm
Algorytm Dijkstry (najkrótsze ścieżki z jednego źródła, nieujemne wagi krawędzi). Jeżeli R znajduje się na najkrótszej ścieżce od P do Q, to znamy już najkrótsza ścieżkę od R do Q (optymalna podstruktura).
Algorytm Bellmana-Forda (najkrótsze ścieżki z jednego źródła, możliwe ujemne wagi krawędzi, brak ujemnych cykli).
Algorytm Floyda-Warshalla (najkrótsze ścieżki pomiędzy wszystkimi parami wierzchołków, możliwe ujemne wagi krawędzi, brak ujemnych cykli). Złożoność obliczeniowa to O(V^3). Wykorzystuje się relację rekurencyjną na najkrótsze ścieżki.
Algorytmy wykorzystujące drzewo dekompozycji grafu.