我编写了一个小程序,使用动态编程技术计算数字的阶乘。
#include<stdio.h>
int fact(int n)
{
int f[n],i;
f[0] = 1;
for(i=1;i<=n;i++)
f[i] = i * f[i-1];
return f[n];
}
int main(void)
{
printf("\n Factorial of %d is %d ",5,fact(5));
return 0;
}
记忆的方法是否正确?因为,动态编程涉及递归。但我没有把它包括在内。所以我不确定我的方法。
答案 0 :(得分:4)
是的,您解决问题的方法是一个非常简单的动态编程案例,您可以在其中存储以前解决的子问题,以帮助您解决实际问题。虽然您提供的示例将被视为动态编程,但通常不会将其称为记事本
当有人说 Memoization 时,通常会采用自上而下的解决问题的方法,假设您已经通过构建程序解决了子问题以某种方式解决子问题递归。 您存储或 memoize 这些子问题的结果,以便不会多次计算它们。
让我通过一个例子说明 Memoization :
这是一个计算数字的第n个Fibonacci的简单例子:
int fib(int n)
{
if (n <= 1)
return n;
return fib(n-1) + fib(n-2);
}
上面的代码使用递归来解决子问题(fib(n-1)和fib(n-2)),以便最终可以求解fib(n)。它假设fib(n-1)和fib(n-2)已经以它的结构方式解决了。
虽然这段代码看起来很优雅,但运行时间是指数级的,因为你可以解决fib(i),其中i是一个小于n的数,多次。您可以查看此处显示的图表,以查看此问题生成的树:http://www.geeksforgeeks.org/program-for-nth-fibonacci-number。
为避免不必要的重新计算, Memoization 用于通过使用内存来优化运行时。
以下是使用 Memoization 计算第n个Fibonacci数的优化示例:
/*Global array initialized to 0*/
int a[100];
int fib(int n)
{
/*base case*/
if (n <= 1)
return n;
/*if fib(n) has not been computed, compute it*/
if (a[n] == 0) {
a[n] = fib(n - 1) + fib(n - 2);
}
*/Otherwise, simply get a[n] and return it*/
return a[n];
}
正如您所看到的,整体结构与递归解决方案没有太大差别,但它运行线性时间而不是指数时间,因为只有在我们尚未计算的情况下才会计算fib(i)。
如果我使用你的方法动态编程,对于Fibonacci问题,它看起来像这样:
int fib(int n)
{
/* just like the array you declared in your solution */
int f[n+1];
int i;
/* set up the base cases, just like how you set f[0] to 1*/
f[0] = 0;
f[1] = 1;
for (i = 2; i <= n; i++)
{
/* using previously solved problem to solve further problems*/
f[i] = f[i-1] + f[i-2];
}
/*return the final result*/
return f[n];
}
动态编程和Memoization之间存在更微妙的差异,权衡和影响。有些人认为Memoization是动态编程的一个子集。你可以在这里阅读更多有关差异的内容:
Dynamic programming and memoization: bottom-up vs top-down approaches
答案 1 :(得分:2)
是的,这是动态编程:从基础案例到最终案例。当然,你的例子(阶乘)太简单了,所以你可以自己简化许多事情:你消除了递归,从不在记忆中使用测试。但不管怎样,那就是它。
对于一般的记忆方案,请参阅http://en.wikipedia.org/wiki/Memoization。
有关动态编程的说明,请参阅http://en.wikipedia.org/wiki/Dynamic_programming,您将能够使用自下而上的方法阅读有关斐波纳契数列及其计算的部分。