斐波那契数列的研究

读昨天刚买的《Android应用性能优化》,第一章介绍了斐波那契数列的实现及优化,这是算法方面的问题。

斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、……在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)

书中介绍了4种实现,头两种是递归,后面两种是迭代。当然迭代更好,因为递归效率低下,可能导致栈溢出。


	public static long computeRecursively(int n) {
		if(n > 1) return computeRecursively(n-2) + computeRecursively(n-1);
		return n;
	}

第一种很好理解,不用多说,后面的三种,很费力的研究了许久。

第二种是对第一种的优化,见下,每次调用少递归调用一次:

	public static long computeRecursivelyWithLoop(int n) {
		if(n > 1){
			long result = 1;
			do{
				result += computeRecursivelyWithLoop(n-2);
				n--;
			}while(n>1);
			return result;
		}
		return n;
	}
分析:看起来就像是从第n-2个值开始累加到第一个,在额外加上个1,就是第n个值。

F(n) = F(n-2) +  F(n-1)每次都把上次分解得到的较大的一个(即每行的最后一个)进行再分解,得到F(n) = sum(F(n-2) + ... +F(0)) + 1
= F(n-2) + F(n-3) + F(n-2)
= F(n-2) + F(n-3) + F(n-4) + F(n-3)
= F(n-2) + F(n-3) + F(n-4) + (F(n-5)+F(n-4))
= F(n-2) + F(n-3) + F(n-4) + F(n-5) + F(n-6) + F(n-5)
= ...
= F(n-2) + F(n-3) + ... + F(2) + F(3)
= F(n-2) + F(n-3) + ... + F(2) + F(1) + F(2)
= F(n-2) + F(n-3) + ... + F(2) + F(1) + F(0) + F(1)
= sum(F(n-2) + ... + F(0)) + 1

剩下的两种迭代实现的方法也是这个求和的原理,方法3如下:

	public static long computeIteratively(int n){
		if(n > 1){
			long a = 0, b = 1;
			do {
				long tmp = b;
				b += a;
				a = tmp;
			}while(--n>1);
			return b;
		}
		return n;
	}
  	 0	1	2	3	4	5	6	7	8
	 0	1	1	2	3	5	8	13	21
0	 a	b
1	 	a	b
2	 		a	b
3	 			a	b
4	 				a	b
5	 					a	b
6	 						a	b
7								a	b
经历n-1次循环, F(n) = b


方法4每次迭代计算两项,迭代总数与方法3相比少了一半:

根据n奇偶判断初值,如果n是奇数,则a=0, b=1;如果n是偶数则a=1,b=1

	public static long computeIterativelyFaster(int n){
		if(n>1){
			long a, b = 1;
			n--;
			a = n&1;
			n /= 2;
			while(n-- > 0){
				a += b;
				b += a;
			}
			return b;
		}
		return n;
	}

自己根据方法4写了自以为更容易理解的:

 	 0	1	2	3	4	5	6	7	8
	 0	1	1	2	3	5	8	13	21
0	 a	b
1	 		a	b
2	 				a	b
3	 						a	b
4	 								a	b

经历n/2次循环, 如果n为奇数,F(n)=b; 如果n为偶数,F(n)=a

	public static long computeIterativelyFaster2(int n){
		if(n>1){
			long a=0, b = 1;
			int evenOdd = n%2;
			long tmp=1L<<63;
			long cnt=0;//累积器,2倍为最大值
			n /= 2;
			while(n-- > 0){
				a += b;
				b += a;
				++cnt;
				//溢出的时候检测到
				if((b&tmp)!=0){
					System.out.println("n:"+n+",a:"+a+",b:"+b+",cnn:"+cnt);
					break;
				}
				
			}
			if(evenOdd==0){
				return a;
			}else{
				return b;
			}
		}
		return n;
	}

关于溢出:

Long型有64位,除去第一位是符号位,最大值为9223372036854775807。

可容纳的最大的斐波那契数是7540113804746346429,即第92项。

写的方法里做了检测,当溢出的时候,跳出,查看当前的a、b值,及循环次数。

运行结果:

当n=30的时候,

Long_MAX:9223372036854775807
F[30]
832040
5ms
832040
4ms
832040
0ms
832040
0ms
832040
0ms


当n=100的时候(注释掉前两个递归的方法,费时太长,比较的都是迭代的方法)

Long_MAX:9223372036854775807
F[100]
3736710778780434371
0ms
3736710778780434371
0ms
n:4,a:7540113804746346429,b:-6246583658587674878,cnn:46
7540113804746346429
0ms

此时,迭代所用时间都在1ms之内。

自己写的方法里检测到b溢出了,此时循环次数是46次,意味着最大支持的是2倍的92项值7540113804746346429。

3736710778780434371是已经溢出了之后,丢失了符号位得到的数值。



溢出解决,见第二章BigInteger

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值