快速斐波那契

对数阶斐波那契数列求值

本文介绍 SICP 练习1.19的 python 实现。

问题描述

斐波那契数列是按一定规律展开的一列数,其中任意一个数等于其前两个数之和。数列从0, 1开始,前18项为
0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 , 144 , 233 , 377 , 610 , 987 , 1597 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597
试编程求出数列中的第n项。

数学分析

要求数列的项,无非用通项公式或递推公式。存在斐波那契数列的通项公式和递推公式。

数学规律

直接利用递推公式迭代求解,这种解法的复杂度是线性的。但是,如果能用某种方法刻画递推的积累,将多次递推合并为一次递推,我们就可以少做计算。

这样的方法是存在的。为了表述方便,定义一些概念。首先,将斐波那契数列的递推过程看作变换 T T T应用到对偶 ( 1 , 0 ) (1,0) (1,0)的过程。注意,我们将数列递推看作对偶到对偶的递推,两两一组无重叠。然后,我们来考虑这递推过程能不能叠加积累。

直接观察 T T T的叠加,不易发现规律, T T T T 20 T^{20} T20作用在 ( a , b ) (a,b) (a,b)上的效果如下图:

在这里插入图片描述

查阅教材知,可以将 T T T看作变换族 T p q T_{pq} Tpq p = 0 p=0 p=0 q = 1 q=1 q=1的特殊情况,其中 T p q T_{pq} Tpq是对于对偶 ( a , b ) (a,b) (a,b)按照
a ← b q + a q + a p b ← b p + a q a\leftarrow bq+aq+ap\\b\leftarrow bp+aq abq+aq+apbbp+aq
规则的变换。应用变换 T p q T_{pq} Tpq两次,其效果等同于应用同样形式的一次变换 T p ′ q ′ T_{p'q'} Tpq,其中
p ′ = p 2 + q 2 q ′ = q 2 + 2 p q p'=p^2+q^2\\q'=q^2+2pq p=p2+q2q=q2+2pq
也就是说,
T p q ( T p q ( a , b ) ) = T p ′ q ′ ( a , b ) T_{pq}(T_{pq}(a,b))=T_{p'q'}(a,b) Tpq(Tpq(a,b))=Tpq(a,b)
这个规律从两个方面给了我们快速计算的支持:

  • 可以将两次变换合并为一次,且这两次变换本身可以是合并得来的
  • 公式的成立不依赖具体的 ( a , b ) (a,b) (a,b),即变换的起点任意

斐波那契递推公式可以看作 T 01 T_{01} T01,故我们的问题可以重新表述为
F i b ( n + 1 ) , F i b ( n ) = T 01 n ( 1 , 0 ) Fib(n+1),Fib(n)=T^n_{01}(1,0) Fib(n+1),Fib(n)=T01n(1,0)
为了尽可能少的应用变换,我们需要将 n n n次变换两两合并,并改变起点来处理奇数次变换的合并。具体来说,我们要应用
( T p q ) n ⇔ ( T p ′ q ′ ) n 2 (T_{pq})^n\Leftrightarrow (T_{p'q'})^{\frac{n}{2}} (Tpq)n(Tpq)2n
将变换次数减半。多次减半就可以降至对数级复杂度。

编程

scheme

#lang sicp

(define (fib n)
  (fib-iter 1 0 0 1 n))

(define (fib-iter a b p q count)
  (cond ((= count 0) b)
        ((even? count)
         (fib-iter a
                   b
                   (+ (square p) (square q))
                   (+ (square q) (* 2 p q))
                   (/ count 2)))
         (else (fib-iter (+ (* b q) (* a q) (* a p))
                         (+ (* b p) (* a q))
                         p
                         q
                         (- count 1)))))

(define (square x) (* x x))

python

def fast_fib(n):
    a, b, p, q = 1, 0, 0, 1
    while n != 0:
        if n % 2 == 0:
            p, q = p**2 + q**2, 2*p*q + q**2
            n //= 2
        else:
            a, b = b*q + a*(q + p), b*p + a*q
            n -= 1
    return b

偶数次更新变换,奇数次更新起点,次数耗尽结束,此时已将起点带到终点。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值