递推算法是一种很常用的算法思想,在数学计算等场合有着广泛的应用。该算法适用于有明确公式的情况,通过已知条件,利用特定关系得出中间推论,逐步递推,直至得到结果为止。
不断利用已有的信息推导出新的东西。利用现有信息得到新信息,是递推算法的核心。
递推算法可分为顺推法和逆推法两种
- 1、顺推法
从已知条件出发,逐步推算出要解决问题的方法。例如,斐波那契数列就可以通过顺推法不断递推算出新的数据。 - 2、逆推法
从已知的结果出发,用迭代表达式逐步推算出问题开始的条件,即顺推法的逆过程。
例题一、顺推实例:斐波那契数列
13世纪初,欧洲数学家斐波那契在他的著作《算盘书》中出了一个有趣的题目:如果1对兔子每月能生1对小兔子,而每对小兔在它出生后的第3个月里,又能开始生1对小兔子,假定在不发生死亡的情况下,由1对初生的兔子开始,1年后能繁殖出多少对兔子?
解题思路:
(1)首先将兔子分为三种:大兔(已能生育小兔)、1个月大的小兔(当月出生的小兔)、2个月大的小兔(上月出生的小兔,还不能生育小兔),到第3个月时,2个月大的小兔就成大兔了。
(2)初始状态时,只有1对初生的小兔(1个月大的小兔),所以总数为1对。
(3)第1个月时,1个月大的小兔成长为2个月大的小兔,还是没有繁殖能力,所以总数仍然是1对。
(4)第2个月时,2个月大的小兔成长为大兔,将繁殖1对1个月大的小兔,因此总数将是2对。
(5)第3个月时,仍然只有1对大兔,将繁殖1对1个月大的小兔,同时上月的1对1个月大的小兔将成长为2个月大的小兔,因此总数将为3对。
(6)以此类推,得出规律,当前项数据(从第3个数开始)为前面相邻两项之和
特点:斐波那契数列有许多奇特的性质,例如,从第3个数起,相邻两个数的比值是随序号的增加而逐渐趋于黄金分割比的,即Fn-1/Fn→0.618…。由于斐波那契数都是整数,两个整数相除之商是有理数,所以只是逐渐逼近黄金分割比这个无理数。
从表的最后一列数据可以看出,斐波那契数列可使用递推算法来得到,由上两个月的兔子数量相加得到本月的兔子总数(对)
具体算法如下:
(1)设初始值为F0=1,第1个月兔子总数F1=1;
(2)第2个月兔子总数F2=F0+F1;
(3)第3个月兔子总数F3=F1+F2;
(4)第n个月兔子总数Fn=Fn-2+Fn-1。
详细C代码如下:
#include<stdio.h>
#define num 13
int main(){
int i;
long fib[num] = {1, 1};
for(i = 2; i < num; i++){
fib[i] = fib[i - 1] + fib[i - 2];
}
for(i = 0; i < num; i++){
printf("%d月兔子总数为%lld对\n", i, fib[i]);
}
return 0;
}
例题二、逆推实例:该存多少钱
父亲准备为小龙的4年大学生活一次性在银行储蓄一笔钱,使用整存零取的方式,控制小龙每月的月底只能提取1000元准备下月使用。假设银行一年整存零取的年利息为1.71%,请编程计算出父亲至少需要一次性存入多少钱才够小龙4年大学生活(4年的利息也应计算在内)
【解题思路】分析存钱和取钱的过程,可以采用逆推的方法。因为是按月取钱,因此需要将4年分为48个月,每个月分别进行计算。若在第48月小龙大学毕业时连本带息要取1000元,则要先求出第47个月时银行存款的具体数额:第47月月末存款=1000/(1+0.0171/12)(前面给出的是年利率,为简化程序,按年利率除以12得到月利率)
第46月月末存款=(第47月月末存款+1000)/(1+0.0171/12);
以此类推,可以求出第45月、第44月…月末存款的数值:
第45月月末存款=(第46月月末存款+1000)/(1+0.0171/12);
第44月月末存款=(第45月月末存款+1000)/(1+0.0171/12);
……
第2月月末存款=(第3月月末存款+1000)/(1+0.0171/12);
第1月月末存款=(第2月月末存款+1000)/(1+0.0171/12);
通过以上推理过程就可以很容易地求出最初要存入多少钱。
详细C代码如下:
#include<stdio.h>
int main(){
double fetch = 1000;
double rate = 0.0171;
double deposit[49];
deposit[48] = 1000;
int i;
for(i = 47; i > 0; i--){
deposit[i] = (deposit[i + 1] + 1000) / (1 + 0.0171 / 12);
}
for(i = 1; i <= 48; i++){
printf("第%d月的存款数为:%lf\n", i, deposit[i]);
}
return 0;
}
运行结果如下:
所以最初存入了46364.618566元(估计值不精确)
而有的人可能代码是这样写的:
#include<stdio.h>
int main(){
double sum = 1000 / (1 + 0.0171 / 12); /* 47月末的存款 */
printf("%.2f\n", sum);
int i;
for(i = 0; i < 47; i++){
sum = (sum + 1000) / (1 + 0.0171 / 12);
}
printf("%.2f\n", sum);
return 0;
}
乍一看感觉也是对的,但是其实第一个变量就错了,这样的逆推题最好就是用数组形式存储,按之前那个公式,
第n - 1月月末存款 = (第n月月末存款 + 1000)/ (1 + 月利率)
即,money[i] = (money[i + 1] + 1000) / (1 + 月利率),
47月末的存款 = (48月末存款 + 1000)/(1 + 月利率),而且这个代码运行出的答案46363.29也不对