A veces, al programar de forma recursiva, llamas a la función con los mismos parámetros múltiples veces, lo cual es innecesario.
El famoso ejemplo de los números Fibonacci:
índice: 1,2,3,4,5,6...
número Fibonacci: 1,1,2,3,5,8...
function F(n) {
if (n < 3)
return 1
else
return F(n-1) + F(n-2)
}
Vamos a ejecutar F(5):
F(5) = F(4) + F(3)
= {F(3) + F(2)} + {F(2) + F(1)}
= {[F(2) + F(1)] + 1} + {1 + 1}
= 1 + 1 + 1 + 1 + 1
Entonces hemos llamado: 1 vez a F(4) 2 veces a F(3) 3 veces a F(2) 2 veces a F(1)
Enfoque de Programación Dinámica: si llamas a una función con el mismo parámetro más de una vez, guarda el resultado en una variable para acceder directamente a él la próxima vez. La forma iterativa:
if (n==1 || n==2)
return 1
else
f1 = 1, f2 = 1
for i = 3 to n
f = f1 + f2
f1 = f2
f2 = f
Volviendo a llamar a F(5):
fibo1 = 1
fibo2 = 1
fibo3 = (fibo1 + fibo2) = 1 + 1 = 2
fibo4 = (fibo2 + fibo3) = 1 + 2 = 3
fibo5 = (fibo3 + fibo4) = 2 + 3 = 5
Como puedes ver, cuando necesitas hacer múltiples llamadas, simplemente accedes a la variable correspondiente para obtener el valor en lugar de recalcularlo.
Por cierto, programación dinámica no significa convertir un código recursivo en un código iterativo. También puedes guardar los subresultados en una variable si deseas un código recursivo. En este caso, la técnica se llama memoización. Para nuestro ejemplo, se vería así:
// declarar e inicializar un diccionario
var dict = new Dictionary();
for i = 1 to n
dict[i] = -1
function F(n) {
if (n < 3)
return 1
else
{
if (dict[n] == -1)
dict[n] = F(n-1) + F(n-2)
return dict[n]
}
}
Entonces la relación con Divide y Vencerás es que los algoritmos D&D se basan en la recursión. Y algunas versiones de ellos tienen este problema de "llamada múltiple a la función con el mismo parámetro". Busca ejemplos como "multiplicación de cadenas de matrices" y "subsecuencia común más larga" donde se necesita DP para mejorar el T(n) del algoritmo D&D.