php专区

 首页 > php专区 > PHP进阶 > 算法 > 趣味算法之兔子产子问题

趣味算法之兔子产子问题

分享到:
【字体:
导读:
         摘要:假定你有一雄一雌一对刚出生的兔子,它们在长到一个月大小时开始交配,在第二月结束时,雌兔子产下另一对兔子,过了一个月后它们也开始繁殖,如此这般持续下去。每只雌兔在开始繁殖时每月都产下一对兔子,假定没有兔子死亡,在一年后总共会有多少对兔子?在一月底,最初的一对兔子交配,但是还只有1对兔子。 ...

趣味算法之兔子产子问题

假定你有一雄一雌一对刚出生的兔子,它们在长到一个月大小时开始交配,在第二月结束时,雌兔子产下另一对兔子,过了一个月后它们也开始繁殖,如此这般持续下去。每只雌兔在开始繁殖时每月都产下一对兔子,假定没有兔子死亡,在一年后总共会有多少对兔子?

在一月底,最初的一对兔子交配,但是还只有1对兔子;在二月底,雌兔产下一对兔子,共有2对兔子;在三月底,最老的雌兔产下第二对兔子,共有3对兔子;在四月底,最老的雌兔产下第三对兔子,两个月前生的雌兔产下一对兔子,共有5对兔子;……如此这般计算下去,兔子对数分别是:1, 1, 2, 3, 5, 8, 13, 21, 34, 55,89, 144, ...看出规律了吗?从第3个数目开始,每个数目都是前面两个数目之和。这就是著名的斐波那契(Fibonacci)数列。

C 编程解决如下:

#include 
 
int Fib(int n)
{
	if (n == 1 || n == 2)
		return 1;
	else
		return Fib(n-1) + Fib(n-2);
}
int main()
{
	int wait;
	printf("一年可以有 %d 对兔子n", Fib(12));
	scanf("%d",&wait);
	return 0;
}

有许多相关问题:

有一段楼梯有10级台阶,规定每一步只能跨一级或两级,要登上第10级台阶有几种不同的走法?

答:这就是一个斐波那契数列:登上第一级台阶有一种登法;登上两级台阶,有两种登法;登上三级台阶,有三种登法;登上四级台阶,有五种方法……所以,1,2,3,5,8,13……登上十级,有89种。

数列中相邻两项的前项比后项的极限是多少,就是问,当n趋于无穷大时,F(n)/F(n+1)的极限是多少?

答:这个可由它的通项公式直接得到,极限是(-1+√5)/2,这个就是所谓的黄金分割点,也是代表大自然的和谐的一个数字。

Fibonacci数列的数学表达式就是:F(n) = F(n-1) + F(n-2),F(1) = 1,F(2) = 1。

Fibonacci数列可以用很直观的二叉递归程序来写,用C++语言的描述如下:

long fib1(int n)
{
	if (n 
  
  

看上去程序的递归使用很恰当,可是在用VC2005的环境下测试n=37的时候用了大约3s,而n=45的时候基本下楼打完饭也看不到结果……显然这种递归的效率太低了。

例如,用下面一个测试函数:

long fib1(int n, int* arr)
{
	arr[n]++;
	if (n 
  
  

这时,可以得到每个fib(i)被计算的次数:

fib(10) = 1     fib(9) = 1      fib(8) = 2      fib(7) = 3
fib(6) = 5      fib(5) = 8      fib(4) = 13    	fib(3) = 21
fib(2) = 34   	fib(1) = 55    	fib(0) = 34

可见,计算次数呈反向的Fibonacci数列,这显然造成了大量重复计算。

我们令T(N)为函数fib(n)的运行时间,当N>=2的时候我们分析可知:T(N) = T(N-1) + T(N-2) + 2

其实,这违反了递归的一个规则:合成效益法则。

合成效益法则(Compound interest rule):在求解一个问题的同一实例的时候,切勿在不同的递归调用中做重复性的工作。 所以在上面的代码中调用fib(N-1)的时候实际上同时计算了fib(N-2)。这种小的重复计算在递归过程中就会产生巨大的运行时间。

用一叉递归程序就可以得到近似线性的效率,用C++语言的描述如下:

long fib(int n, long a, long b, int count)
{
     if (count == n)
         return b;
     return fib(n, b, a+b, ++count);
}
 
long fib2(int n)
{
     return fib(n, 0, 1, 1);
}

这种方法虽然是递归了,但是并不直观,而且效率上相比下面的迭代循环并没有优势。

Fibonacci数列用迭代程序来写也很容易,用C++语言的描述如下:

//也可以用数组将每次计算的f(n)存储下来,用来下次计算用(空间换时间)
long fib3 (int n)
{
     long x = 0, y = 1;
     for (int j = 1; j 
  
  

这时程序的效率显然为O(N),N = 45的时候 <1s就能得到结果。

本文地址:http://www.nowamagic.net/librarys/veda/detail/601,欢迎访问原出处。

趣味算法之兔子产子问题
分享到:
趣味算法之猴子吃桃问题
趣味算法之猴子吃桃问题 猴子第一天摘下若干桃子,当即吃了一半,还不过瘾,又多吃了一个。第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘多少桃子。 C 程序: #include int main() { int sum...
利用数组的索引也可以排序
利用数组的索引也可以排序 看到一道算法面试题,比较有趣,我自己用C做了一下。 题目:随机生成10个100以内的整数,把数据从小到大排序,而且算法复杂度只能是1。 这个算法比较有意思的地方是,首先建立一个数组B,其元素个数为数组A的最大元素值,然后用A的元素作为B的数组下标,然后给存在的B元...
  •         php迷,一个php技术的分享社区,专属您自己的技术摘抄本、收藏夹。
  • 在这里……