Динамичко програмирањеДинамичко програмирање је метод којим се смањује време извршавања оних проблема у којима се захтева тражење оптималне подструктуре и који имају потпроблеме који се понављају, као што ће бити описано у наставку. Овај појам је увео математичар Ричард Белман 1953. године. ПрегледПојам оптимална подструктура означава тражење оптималних решења потпроблема и њихово коришћење у циљу тражења оптималног решења целокупног проблема. На пример, најкраћи пут до датог чвора (означимо га као крајњи) од произвољног чвора у ацикличном графу, може се израчунати тако што се најпре израчуна најкраћи пут од крајњег чвора до његових суседних, затим исти принцип применимо на његове суседе, итд. Дакле, проблем који има оптималну подструктуру се може решити у следећа три корака:
Потпроблеме је потребно делити на пот-проблеме, све док се не дође до једноставних случаја које је једноставно решити. За разлику од неких алгоритама у којима се појављују потпроблеми који се понављају, код динамичког програмирања, један потпроблем се решава само једном. На пример, у Фибоначијевом низу F3 = F1 + F2 и F4 = F2 + F3 - рачунање сваког броја захтева налажење F2. Како су F3 и F4 потребни за налажење F5, наивним приступом би за рачунање F5 било потребно налажење F2 неколико пута. Када се потпроблеми понављају више пута, наивним приступом се често изгуби доста времена на тражење њихових оптималних решења, који су већ решени. Како би се ово избегло, потребно је сачувати решења оних проблема који су већ решени, како би се могли касније искористити. Ово се још назива и мемоизација. Уколико је очигледно да решени потпроблеми нису више потребни, могу се одбацити како би се сачувао меморијски простор. Динамичко програмирање се користи код:
Овај алгоритам користи најчешће један од следећа два приступа:
ПримериФибоначијев низНаивна имплементација функције за тражење n-тог члана Фибоначијевог низа се базира директно на математичкој дефиницији: function fib(n) if n = 0 or n = 1 return n else return fib(n − 1) + fib(n − 2) Треба приметити да, ако се позове функција
Ако се искористи приступ одозго надоле, тј. примени мемоизација, тада се потпроблеми решавају тачно једном, јер се њихове вредности памте: var m := map(0 → 1, 1 → 1) function fib(n) if n not in keys(m) m[n] := fib(n − 1) + fib(n − 2) return m[n] Међутим, користећи приступ одоздо нагоре, линеарна просторна сложеност се може преобразити у константну: function fib(n) var previousFib := 1, currentFib := 1 repeat n − 1 times var newFib := previousFib + currentFib previousFib := currentFib currentFib := newFib return currentFib У оба последња случаја, само се једном рачуна Шаховска таблаНека је дата шаховска табла димензија n × n и функција c(i, j) која враћа одређену вредност за дато поље i,j (i означава ред, а j колону). Узмимо, на пример, да је n = 5. +---+---+---+---+---+ 5 | 6 | 7 | 4 | 7 | 8 | +---|---|---|---|---+ 4 | 7 | 6 | 1 | 1 | 4 | +---|---|---|---|---+ 3 | 3 | 5 | 7 | 8 | 2 | +---|---|---|---|---+ 2 | 2 | 6 | 7 | 0 | 2 | +---|---|---|---|---+ 1 | 7 | 3 | 5 | 6 | 1 | +---+---+---+---+---+ 1 2 3 4 5 Дакле, c(1, 3) = 5. Нека се у доњој колони налази краљ који треба да стигне до горње колоне, тако што ће прећи најкраћи пут (сума квадрата на путу до горње колоне треба да буде најмања). Под претпоставком да се краљ може померати само напред, дијагонално лево или дијагонално десно, он из поља (1,3) може прећи на (2,2), (2,3) или (2,4). +---+---+---+---+---+ 5 | | | | | | +---|---|---|---|---+ 4 | | | | | | +---|---|---|---|---+ 3 | | | | | | +---|---|---|---|---+ 2 | | x | x | x | | +---|---|---|---|---+ 1 | | | O | | | +---+---+---+---+---+ 1 2 3 4 5 Овај проблем захтева тражење оптималне подструктуре, односно решавање целокупног проблема се заснива на тражењу његових потпроблема. Нека је функција q(i, j) дефинисана на следећи начин:
Лако се уочава да је вредност функције q(i, j) једнака минималној вредности од три поља испод датог (то су поља са којих се једино до њега може и стићи) плус c(i, j). На пример: +---+---+---+---+---+ 5 | | | | | | +---|---|---|---|---+ 4 | | | A | | | +---|---|---|---|---+ 3 | | B | C | D | | +---|---|---|---|---+ 2 | | | | | | +---|---|---|---|---+ 1 | | | | | | +---+---+---+---+---+ 1 2 3 4 5 Сада, q(i, j) се може дефинисати као: Ово се може представити псеудокодом: function minCost(i, j) if j < 1 or j > n return infinity else if i = 1 return c(i, j) else return min(minCost(i-1, j-1), minCost(i-1, j), minCost(i-1, j+1)) + c(i, j) Лако се уочава да наведена функција не одређује најкраћи пут, већ само његову вредност. Најпре, може се применити приступ одоздо нагоре и искористити дводимензионални низ function computeShortestPathArrays() for x from 1 to n q[1, x] := c(1, x) for y from 1 to n q[y, 0] := infinity q[y, n + 1] := infinity for y from 2 to n for x from 1 to n m := min(q[y-1, x-1], q[y-1, x], q[y-1, x+1]) q[y, x] := m + c(y, x) if m = q[y-1, x-1] p[y, x] := -1 else if m = q[y-1, x] p[y, x] := 0 else p[y, x] := 1 Сада је лако наћи минимум и исписати најкраћи пут. function computeShortestPath() computeShortestPathArrays() minIndex := 1 min := q[n, 1] for i from 2 to n if q[n, i] < min minIndex := i min := q[n, i] printPath(n, minIndex) function printPath(y, x) print(x) print("<-") if y = 2 print(x + p[y, x]) else printPath(y-1, x + p[y, x]) Алгоритми који користе динамичко програмирање
Литература
Спољашње везе
|
Portal di Ensiklopedia Dunia