Programación dinámica (computación)Na Informática, a programación dinámica é un método para reducir o tempo de execución dun algoritmo mediante a utilización de subproblemas superpostos e subestruturas óptimas, como se describe a continuación. O matemático Richard Bellman inventou a programación dinámica en 1953. IntroduciónUnha subestructura óptima significa que solucións óptimas de subproblemas poden ser usadas para atopar as solucións óptimas do problema no seu conxunto. Por exemplo, o camiño máis curto entre dous vértices dun grafo pódese atopar calculando primeiro o camiño máis curto ao obxectivo desde todos os vértices adxacentes ao de partida, e despois usando estas solucións para elixir o mellor camiño de todos eles. En xeral, pódense resolver problemas con subestructuras óptimas seguindo estes tres pasos:
Os subproblemas resólvense á súa vez dividíndoos en subproblemas máis pequenos ata que se alcance o caso fácil, onde a solución ao problema é trivial. Dicir que un problema ten subproblemas superpostos é dicir que un mesmo subproblema é usado para resolver diferentes problemas maiores. Por exemplo, na sucesión de Fibonacci, F3 = F1 + F2 e F4 = F2 + F3 — calcular cada termo supón calcular F2. Como ambos os F3 e F4 fan falta para calcular F5, unha mala implementación para calcular F5 acabará calculando F2 dúas ou máis veces. Isto ocorre sempre que haxa subproblemas superpostos: unha mala implementación pode acabar desperdiciando tempo recalculando as solucións óptimas a subproblemas que xa foron resoltos anteriormente. Isto pódese evitar gardando as solucións que xa calculamos. Entón, se necesitamos resolver o mesmo problema máis tarde, podemos obter a solución da lista de solucións calculadas e reutilizala. Este achegamento ao problema chámase memoización (que a pesar de non estar na lingua galega, o seu similar en inglés tampouco está e "é memoizing"). Se estamos seguros de que non volveremos necesitar unha solución en concreto, podémola descartar para aforrar espazo. Nalgúns casos, podemos calcular as solucións a problemas que sabemos que imos necesitar de antemán. En resumo, a programación dinámica fai uso de: A programación dinámica toma normalmente un dos dous seguintes enfoques:
Orixinalmente, o termo de programación dinámica designaba unicamente á resolución de certos problemas operacións fose do ámbito da Enxeñería Informática, do mesmo xeito que o facía programación lineal. Neste contexto non ten ningunha relación coa programación en absoluto; o nome é unha coincidencia. O termo tamén se usaba nos anos 40 por Richard Bellman, un matemático americano, para describir o proceso de resolver problemas onde fai falta calcular a mellor solución consecutivamente. Algúns linguaxes de programación funcionais, sobre todo Haskell, poden usar a memoización automaticamente sobre funcións cun conxunto concreto de argumentos, para acelerar o seu proceso de avaliación. Isto só é posible en funcións que non teñan efectos secundarios, algo que ocorre en Haskell pero non tanto noutras linguaxes. Principio de optimalidadCando falamos de optimizar referímonos a buscar a mellor solución de entre moitas alternativas posibles. Devandito proceso de optimización pode ser visto como unha secuencia de decisións que nos proporcionan a solución correcta. Se, dada unha subsecuencia de decisións, sempre se coñece cal é a decisión que debe tomarse a continuación para obter a secuencia óptima, o problema é elemental e resólvese trivialmente tomando unha decisión detrás doutra, o que se coñece como estratexia voraz. A miúdo, aínda que non sexa posible aplicar a estratexia voraz, cúmprese o principio de optimalidad de Bellman que dita que «dada unha secuencia óptima de decisións, toda subsecuencia dela é, á súa vez, óptima». Neste caso segue sendo posible o ir tomando decisións elementais, na confianza de que a combinación delas seguirá sendo óptima, pero será entón necesario explorar moitas secuencias de decisións para dar coa correcta, sendo aquí onde intervén a programación dinámica. Contemplar un problema como unha secuencia de decisións equivale a dividilo en subproblemas máis pequenos e polo tanto máis fáciles de resolver como facemos en Divide e Vencerás, técnica similar á de Programación Dinámica. A programación dinámica aplícase cando a subdivisión dun problema conduce a:
ExemploSucesión de FibonacciUnha implementación dunha función que atope o n-ésimo termo da sucesión de Fibonacci baseada directamente na definición matemática da sucesión realiza moito traballo redundante: function fib(n) if n =0 or n= 1 return n else return fib(n − 1) + fib(n − 2) Se chamamos, por exemplo, a code
En particular, Agora supoñemos que temos un simple obxecto mapa, m, que garda cada valor de 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] Esta técnica de gardar os valores que xa foron calculados chámase memorización; isto é unha implementación top-down, posto que primeiro dividimos o problema noutros máis pequenos e despois calculamos e almacenamos os valores. Neste caso, tamén podemos evitar que a función use unha cantidade de espazo lineal (Ou(n)) e use unha cantidade constante no seu lugar cambiando a definición da nosa función e usando unha implementación bottom-up que calculará valores pequenos de code function fib(n) var currentFib := 0, nextFib := 1 repeat n times var newFib := currentFib + nextFib currentFib := nextFib nextFib := newFib return currentFib En ámbolos dous exemplos, Véxase taménBibliografía
|
Portal di Ensiklopedia Dunia