简单介绍下:一个数列是这样的:1 1 2 3 5 8 13 21 34 55,很明显看得出来它的每一项都是前两个数字之和,而且很有意思的是从第三项开始,没隔两项之后的一项必是偶数。
1:采用递归的方式
C++代码实现:
using namespace std;
#include <iostream>
int f(int n)
{
//分解为最开始的项数,此函数的作用就是将n这一项拆分为前两项相加
//然后前面的项继续拆分,直到回到最初定义的第一项和第二项
//程序的思维是从后往前找规律,后项总是前两项相加,而最初的两项的数值可以自定义
if (n == 1)return 1;
if (n == 2)return 1;
return f(n - 1) + f(n - 2);
}
void main()
{
cout << f(7) << endl;
system("pause");
}
分析时间效率:
发现重复计算F(2),F(3)降低了效率。
因此采用递归会导致大量的重复计算,同时,由于计算机本身的性质,递归次数不能过多,否则会导致栈溢出,导致程序崩溃。即在前面我们讲过,函数是靠栈实现的,过多的函数调用必然造成栈溢出。因此这里采用递归算法不仅造成时间复杂度很大,同时还造成栈溢出的可能。
递归算法是从上到下的计算,即将大问题分别分解成若干个小问题,然后递归求解小问题,直到达到原子问题,然后开始逐步返回,计算每个大的问题。即存在某些问题会反复计算多次。
2:采用动态规划和递归的方法(减少运算次数)
而从底向上的解法,则是从小问题开始,计算完成后就记录下小问题的解,然后通过小问题的解,构造出大问题的解,如此重复。即每个问题只需要计算一次即可。
C++代码实现:
using namespace std;
#include <iostream>
int f(int n1, int n2)
{
//采用之下往上的方法,减小计算次数
int x1 = n1 + n2;
int x2 = n1;
if (x2 < 10000 && x1 < 10000)
{
cout << x1 << " " << x2 << endl;
return f(x1, x2);
}
}
void main()
{
f(1, 1);
system("pause");
}
这种是从下往上计算:即 F(1)+F(1) =F(2) F(2)+F(1)=F(3) F(3)+F(2),时间复杂度度。
关于递归法和动态规划的讨论:
动态规划和分治法是有着相同的思想。分治法的思想是将大问题分解为小问题。而动态规划思想是取最优子结构。但是从计算方法上来说,分治法是自顶向下的分解问题,然后自底向上返回问题的解。而动态规划一开始就直接从底向上的将小问题的解综合成大问题的解。动态规划相对分治法中的递归算法时间上是采用了以空间换时间的方式。即在计算小问题的过程中记录下小问题的值。而递归是不会记录小问题的值,每次都是重复计算。
而采用两种方法相结合的方式也同样可以提高运行速度。其内在原因就是是否重复计算的问题。