回 帖 发 新 帖 刷新版面

主题:快速fibonacci变换

如何快速得到fibonnacci数列的某一项? 该帖改编自HAKMEM:
http://www.inwap.com/pdp10/hbaker/hakmem/recurrence.html#item12

定义一个二元数数乘法:
(A,B) (C,D) = (A C + A D + B C, A C + B D).
我们注意到 (A,B) (1,0) = (A + B, A), 这就是fibonacci的迭代公式.因此有 (1,0)^N = (FIB(N), FIB(N-1)). 这预示我们可以在logN的时间内得到数列的第N项.

例如:FIB(1) => (1,0)^1=(1,0);
FIB(2) => (1,0)^2=(1,1); 
FIB(4) => (1,0)^4=(3,2);
FIB(5) => (1,0)^1 * (1,0)^4=(1,0)*(3,2)=(5,3);
...

下面是实现代码:
#include <iostream>
#include <cstdlib>

using namespace std;

struct Pair {
    int a;
    int b;
};

Pair transform (const Pair &A, const Pair &B)
{
    if (A.a == 0)
        return B;
    Pair tmp;
    tmp.a = A.a*B.a + A.a*B.b + A.b*B.a;
    tmp.b = A.a*B.a + A.b*B.b;

    return tmp;
}

int fast_fibonacci_transform (int n)
{
    assert (n > 0);
    Pair now = {1, 0}, sum = {0, 0};

    if (n & 1) sum = now;
    n >>= 1;
    while (n) {
        now = transform (now, now);
        if (n & 1)
            sum = transform (sum, now);
        n >>= 1;
    }

    return sum.a;
}

int main ()
{
    int n = 47;
    for (int i = 1; i <= n; ++i)
        cout << fast_fibonacci_transform (i) << ' ';
    cout << endl;
    system ("pause");
    return 0;
}

输出:
1 1 2 3 5 8 13...

回复列表 (共17个回复)

11 楼

两个等比数列线性组合

12 楼

[quote]直接用通项应该最快了
嘿嘿[/quote]
不是,嘿嘿.还有更快的. 就是使用上面的矩阵方式(需要做些调整)也比它快.

13 楼

斐波那契列的矩阵是对称矩阵 所以满足交换律

14 楼

楼主介绍的二元数乘法并不是标准的矩阵形式,下面是一种矩阵形式的方法,其中
F(n)代表Fibonacci数列中的第n项,那么求Fibonacci数列的运算规则就是用向量(F(0) F(1))乘以下面的矩阵:
            | 0 1 |
            | 1 1 |
运算一次后得到向量(F(1) F(2)),以此类推。需要注意的是,由于上面的矩阵乘法不可交换,所以在向量中必须是F(0)在F(1)之前。如果用向量( F(1) F(0) )作为运算的初始向量,那么需要乘以下面的矩阵:
            | 1 0 |
            | 1 1 |

15 楼

| 0 1 |
| 1 1 | 是对称矩阵,(F(n) F(n+1))是(F(0) F(1))乘以这个矩阵n次
由于对称矩阵和对称矩阵相乘还是对称矩阵,而对称矩阵满足交换律,
所以要是用这个矩阵做的话乘法满足交换律

16 楼

对称矩阵乘以对称矩阵不一定会得到对称矩阵,例如
| 0 1 |   | 1 1 |   | 1 0 |
| 1 0 | * | 1 0 | = | 1 1 |
当然,如果是两个相同的对称矩阵相乘,得到的才是对称矩阵。同样,相同的矩阵之间相乘,肯定是可交换的。

17 楼

wu yu ,
严格证明这个不是容易的事,要用到子数列的极限{a2k}和{a2k+1},如果只是用推倒通项的话自要x^2=x+1的解

我来回复

您尚未登录,请登录后再回复。点此登录或注册