flyinghearts《编程之美》读书笔记连载(8)(转)

本文介绍了一种高效计算Fibonacci数列的方法,利用递推公式F(n)=F(k)*F(n+1-k)+F(k-1)*F(n-k)进行计算,通过优化时间复杂度至O(log n),实现快速准确地获取数列值。

原贴地址:http://blog.youkuaiyun.com/flyinghearts/archive/2010/05/18/5605933.aspx

2.9 Fibonacci序列

计算Fibonacci序列最直接的方法就是利用递推公式 F(n+2)=F(n+1)+F(n)。而用通项公式来求解是错误的,用浮点数表示无理数本来就有误差,经过n次方后,当n相当大时,误差能足够大到影响浮点数转为整数时的精度,得到的结果根本不准。

用矩阵来计算,虽然时间复杂度降到O(lg n),但要用到矩阵类,相当麻烦。观察:

F(n+2)=F(n)+F(n-1)2*F(n-1)+F(n-2)=3*F(n-2)+2*F(n-4)

用归纳法很容易证明 F(n) = F(k)*F(n+1-k) + F(k-1)*F(n-k),利用该递推公式和原递推公式,要计算F(n),只要计算F([n/2])F([n/2]+1),时间复杂度为 O(lg n)如:要计算F(58), 由 58 -> 29,30 -> 14,15 -> 7,8 -> 3,4 -> 1,2 可知只要算5次。可以用一个栈保存要计算的数,实际上,将n的最高位1(假设在第k位)左边的0去除掉后,第m次要计算的数就是:第k位到第k-m+1位这m个位组成的值为t(m),则第m-1次组成的值为t(m-1),则t(m)=2*t(m-1)+(k-m+1位是否为1)若第m-1次计算得到了 f(k)f(k+1),则第m次计算:

 

 

k-m+1

已计算

待计算

1

f(k)

f(k+1)

f(2*k+1),f(2*k+2)

0

f(2*k),f(2*k+1)

 

 

具体公式见下面代码。

下面是计算F(n)最后四位数(某道ACM题)的代码。

 

  1.    
  2. /*   Fibonacci 数列第N 个数的最后4 位数 
  3.      注意,当 N>93 时 第N 个数的值超过64 位无符号整数可表示的范围。 
  4. F(n+2)=F(n)+F(n-1) F(0)=0 F(1)=1  F(2)=1        ==> 
  5. F(n)=F(k)*F(n+1-k) + F(k-1)*F(n-k)               ==> 
  6. F(2*n)= F(n+1)*F(n)+F(n)*F(n-1)=(F(n+1)+F(n-1))*F(n)= (F(n+1)*2-F(n))*F(n) 
  7. F(2*n+1)=F(n+1)*F(n+1)+F(n)*F(n) 
  8. F(2*n+2)= F(n+2)*F(n+1)+F(n+1)*F(n)=(F(n+2)+F(n))*F(n+1)= (F(n+1)+F(n)*2)*F(n+1) 
  9.   */  
  10.    
  11. unsigned fib_last4 ( unsigned num )  
  12. {  
  13.   if ( num == 0 ) return 0 ;  
  14.   const unsigned M = 10000 ;  
  15.   unsigned ret = 1 , next = 1 , ret_ = ret ;  
  16.   unsigned mask = 1 , tt = num ;  
  17.   while ( tt >>= 1 ) mask <<= 1 ;  
  18.   while ( mask >>= 1 ){  
  19.     if ( num & mask ){  
  20.       ret_ = ret * ret + next * next ;  
  21.       next = ( ret + ret + next ) * next ;  
  22.     } else {  
  23.       // 多加一个M ,避免 2*next-ret 是负数,造成结果不对  
  24.        ret_ = ( next + next + M - ret ) * ret ;  
  25.       next = ret * ret + next * next ;  
  26.     }  
  27.     ret  = ret_ % M ;  
  28.     next = next % M ;  
  29.   }  
  30.   return ret ;  
  31. }  

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值