整理一下经典算法用C/C++实现,并思考总结
题目:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
常规解法
读题之后,我直接着手进行解题:
思路:设置2个变量分别保存成年兔子数,幼年兔子数量,设置一个计数器分组分别计算每对小兔子还有几个月成年,成年后加入成年兔子。成年兔子每月产下幼年兔子加入幼年兔子。
void rabitNormal(int month){
const int MAX_SHALL_RABIT = 1000;//最大小兔子数量(这个算法用数组做有局限性,最大只能MAX_SMALL_RABIT个兔子)
int big_rabits = 0;//大兔子数量(初始都是小兔子)
int small_r = 1;//小兔子数量
short small_rabits[MAX_SHALL_RABIT];
small_rabits[0]=2;//初始化第一对小兔子计数器
int idx = 1;
for (int i=0; i<month; i++) {//每个月更新
for (int j=0; j<idx; j++) {
if(small_rabits[j]>0){
small_rabits[j]--;
if(small_rabits[j]==0){//第三个月
big_rabits ++;//大兔子增加
small_r--;//小兔子减少
}
}
}
for(int j=0;j<big_rabits;j++){
small_rabits[idx++] = 2;//每对大兔子生一对小兔子
small_r++;//小兔子数量增加
}
}
cout<<big_rabits+small_r<<endl;//打印大兔子小兔子总和
}
这个方法直接从实现角度来解决这个问题,就事论事,不太优雅。
递归算法
因为每对兔子从出生到生产的过程是一致的,所以我考虑是不是封装一个方法来表示兔子的这一过程,然后用递归实现
//递归实现 返回第一对兔子和之后所有小兔子生下的总兔子对数
int rabits(int month){
int r = 0;
for(int i=2;i<=month;i++){
r+=rabits(month-i);//从第3个月起每个月生一堆兔子
}
return 1 + r;//这只兔子升的小兔子的总对数
}
这个方法我认为从解决问题的角度不失为一个好方法。但对规律问题考虑不深入,也属于就事论事。
斐波那契数列
网上看了正规解法后发现,这道题表现的本质就是斐波那契数列 f(0)=0,f(1)=1...f(n)=f(n-1)+f(n-2) (当前项等于前两项之和)
。这一数列强大之处我还无法深入体会,我就整理下如何让兔子生产和斐波那契数列联系起来:
思考过程
1.第一个月1对幼年兔子,0对成年兔子
2.第二个月把幼年兔子加入成年兔子(因为第3个月生产出的幼年兔子等于当月的成年兔子),0对幼年,1对成年
3.第三个月幼年兔子对数 = 前月成年兔子对数1,成年兔对数 = 前月幼兔对数+前月成年对数=0+1=1
总结公式:
兔子总对数 = 成年兔子对数+幼年兔子对数
成年兔子对数 = 前月成年兔子对数+前月幼年兔子对数 = 前月总对数
幼年兔子对数 = 前月成年兔子对数 = 大前月兔子总对数(由上条公式得到)
最终推导公式:兔子总对数 = 前月兔子总对数+大前月兔子总对数! 这个公式正好符合Fibonacci数列
void rabitFibonacci(){
long f1,f2;
int i;
f1=f2=1;
for(i=1;i<=20;i++)
{
printf("%12ld %12ld",f1,f2);
if(i%2==0) printf("\n");/*控制输出,每行四个*/
f1=f1+f2;/*前两个月加起来赋值给第三个月*/
f2=f1+f2;/*前两个月加起来赋值给第三个月*/
}
}
整个推导过程为了让幼年兔子对数更纯净(保证都是上月生产的兔子),次月我们就把前一个月的幼年兔子并入成年了,这是由于从客观上来说
- 当月成年对数决定次月幼年对数同时和当月幼年对数一起决定当月总对数
- 当月幼年对数和当月成年对数决定次月成年对数
所以以上对幼年对数当月的“纯净”处理有利于对幼年对数的直接使用(试想一下如果幼年对数中混有新生的和上个月生的,你该如何处理它)。
从这推导过程的构架和实际推导过程中,我认为变量定义功能的明确性和纯净性非常重要。我要的不是这个数列和公式的直接应用,而是这个推导过程。想想当三个月变成四个月,变成五个月该怎么来推导?
小结:3个算法前2个算法是我自己写,第3个方法是参考网上的,推导过程是自己模拟的,虽然花了不少时间做推导,但我觉得还是值得的,因为我现在最缺的就是一颗万事静心思考本质的心,推导的方式固然重要,但这颗探究本质的心更重要