La programación dinámica le brinda una forma de pensar sobre el diseño de algoritmos. Esto a menudo es muy útil.
Los métodos de memorización y ascendentes le brindan una regla / método para convertir las relaciones de recurrencia en código. La memorización es una idea relativamente simple, ¡pero las mejores ideas a menudo lo son!
La programación dinámica le brinda una forma estructurada de pensar sobre el tiempo de ejecución de su algoritmo. El tiempo de ejecución está determinado básicamente por dos números: el número de subproblemas que tiene que resolver y el tiempo que lleva resolver cada subproblema. Esto proporciona una manera conveniente y fácil de pensar sobre el problema de diseño del algoritmo. Cuando tiene una relación de recurrencia candidata, puede verla y tener una idea muy rápida de cuál podría ser el tiempo de ejecución (por ejemplo, a menudo puede decir muy rápidamente cuántos subproblemas habrá, que es un límite inferior en el tiempo de ejecución; si hay exponencialmente muchos subproblemas que tiene que resolver, entonces la recurrencia probablemente no será un buen enfoque). Esto también le ayuda a descartar descomposiciones de subproblemas candidatos. Por ejemplo, si tenemos una cadenaS [ 1 .. i ] S [ j . . n ] S [ i . . j ] n S nS[1..n], definir un subproblema con un prefijo o sufijo o una subcadena puede ser razonable (el número de subproblemas es polinomial en ), pero definir un subproblema por una subsecuencia de no es probable que sea un buen enfoque (el número de subproblemas es exponencial en ). Esto le permite podar el "espacio de búsqueda" de posibles recurrencias.S[1..i]S[j..n]S[i..j]nSn
La programación dinámica le brinda un enfoque estructurado para buscar relaciones de recurrencia de candidatos. Empíricamente, este enfoque suele ser eficaz. En particular, hay algunos patrones heurísticos / comunes que puede reconocer para formas comunes de definir subproblemas, dependiendo del tipo de entrada. Por ejemplo:
Si la entrada es un número entero positivo , una forma candidata de definir un subproblema es reemplazando por un número entero más pequeño (st ).n n ′ 0 ≤ n ′ ≤ nnnn′0≤n′≤n
Si la entrada es una cadena , algunas formas posibles de definir un subproblema incluyen: reemplazar con un prefijo ; reemplazar con un sufijo ; reemplace con una subcadena . (Aquí el subproblema está determinado por la elección de .)S[1..n]S[1..n]S[1..i]S[1..n]S[j..n]S[1..n]S[i..j]i,j
Si la entrada es una lista , haga lo mismo que haría para una cadena.
Si la entrada es un árbol , una forma candidata de definir un subproblema es reemplazar con cualquier subárbol de (es decir, elegir un nodo y reemplazar con el subárbol enraizado en ; el subproblema se determina por la elección de )TTTxTxx
Si la entrada es un par , mire recursivamente el tipo de el tipo de para identificar una forma de elegir un subproblema para cada uno. En otras palabras, una forma candidata para definir un subproblema es reemplazar por donde es un subproblema para e es un subproblema para . (También puede considerar subproblemas de la forma o .)x y ( x , y ) ( x ′ , y ′ ) x ′ x y ′ y ( x , y ′ ) ( x ′ , y )(x,y)xy(x,y)(x′,y′)x′xy′y(x,y′)(x′,y)
Y así. Esto le proporciona una heurística muy útil: con solo mirar la firma de tipo del método, puede encontrar una lista de formas candidatas para definir subproblemas. En otras palabras, solo observando el enunciado del problema, observando solo los tipos de entradas, puede encontrar un puñado de formas candidatas para definir un subproblema.
Esto a menudo es muy útil. No le dice cuál es la relación de recurrencia, pero cuando tiene una opción particular sobre cómo definir el subproblema, a menudo no es demasiado difícil determinar una relación de recurrencia correspondiente. Por lo tanto, a menudo convierte el diseño de un algoritmo de programación dinámico en una experiencia estructurada. Anote en papel de desecho una lista de formas candidatas para definir subproblemas (usando la heurística anterior). Luego, para cada candidato, intente escribir una relación de recurrencia y evaluar su tiempo de ejecución contando el número de subproblemas y el tiempo empleado por subproblema. Después de probar a cada candidato, conserva el mejor que pudo encontrar. Proporcionar cierta estructura al proceso de diseño del algoritmo es una gran ayuda, ya que de lo contrario el diseño del algoritmo puede ser intimidante (hay '