2021/10/21 斐波那契数列与矩阵快速幂 C++实现

斐波那契是一个神奇的东西,0,1,1,2,3,5,8,13,21等等,有一个递推公式:

f(N+1)=f(N)+f(N-1)

所以要实现一个取第一百万的斐波那契数字,最简单的方法是不停的加。

这其中有两个问题:1 效率,2 超大整数。

时间效率自不必说 O(N) 跑不了了,而另一个棘手的问题是超大整数。这个超大整数变量的运算是不能忽略效能的。其时间效能绝对不是O(1)。

所以需要优化算法,同时,需要找一个极致优化的大整数库轮子。

优化算法是百度来的,有无数人实现过的矩阵快速幂法。

已知如下方程成立,其中N>=1,如何实现求 f(N):

[ f ( N + 1 ) f ( N ) f ( N ) f ( N − 1 ) ] = [ 1 1 1 0 ] N \begin{bmatrix} f(N+1) & f(N) \\ f(N) & f(N-1) \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} ^N [f(N+1)f(N)f(N)f(N1)]=[1110]N

使用快速幂法:
[ 1 1 1 0 ] N = ( [ 1 1 1 0 ] N / 2 ) 2 \begin{bmatrix} 1&1\\1&0\end{bmatrix} ^N =\left ( \begin{bmatrix} 1&1\\1&0\end{bmatrix} ^ {N / 2} \right )^2 [1110]N=([1110]N/2)2

比如原N为8,需要进行32次相乘运算,可以变为 ( ( ( X 2 ) 2 ) 2 ) (((X^2 ) ^2 )^2) (((X2)2)2)三次运算即可完成,时间复杂度变为O(logN)。当然,由于N不等于2的整数次幂的情况比如 9 ,可以变成 8 + 1 相乘的形式, ( ( ( X 2 ) 2 ) 2 ) × X (((X^2 ) ^2 )^2) \times X (((X2)2)2)×X 所以时间复杂度会稍微变大一点点。

我当初看到这种算法时,真是佩服的不要不要的,算法思路清奇,由递推公式的计算和矩阵联系在一起,作为没有选修过线代的人,无法想象,两个字,神奇,太TM神奇了。

嘎吹完毕,接着解决第二个问题,C++超大数整数库。对于这种常规的问题,我总是第一个求助于boost,事实上boost确实由cpp_int类,也确实可以完成任务。

但是,效率,还是效率问题。boost明显不是一个专门的数学库,所以其效率可能会打折扣。不卖关子了,咱们继续。

通过各种百度,了解到GMP库,#include <gmpxx>,编译参数加上 -lgmp -lgmpxx,可以有令人惊叹的效率提升。

以下代码:

    //矩阵快速幂生成超大数斐波那契数列
    mpz_class bigFibonacciFast(int N)
    {
        assert(N >= 0);
        if (N == 0)
        {
            return 0;
        }
        Liner::Mat<mpz_class> basic(2, 2);
        basic(0, 0) = 1;
        basic(0, 1) = 1;
        basic(1, 0) = 1;
        basic(1, 1) = 0;
        //矩阵中的1就是[1,0][0,1],任何矩阵乘以它都是矩阵自身。
        Liner::Mat<mpz_class> rest(2, 2);
        rest(0, 0) = 1;
        rest(0, 1) = 0;
        rest(1, 0) = 0;
        rest(1, 1) = 1;
        while (N)
        {
            //当N为奇数,则有(basic)^(N-1)*basic, 将basic项乘给rest
            //其余项为(basic^2)^((N-1)/2)*rest
            //依次递推,如幂为奇数则将当时的basic^x拿出乘以rest,剩下的为偶数幂
            //再将偶数幂除以2,依次类推,直至幂为1,将此时的basic^x乘以rest即可
            if (1 & N) //位操作,判断是否为偶数
            {
                //初始rest相当于 “ 1 ”
                rest = basic * rest;
            }
            basic = basic * basic;
            //位操作,向右移动1位,相当于除以2
            N >>= 1;
        }

        return rest(0, 1);
    }

当然,上述代码缺了一个矩阵库,我是自己实现的,其实效率不高,就不贴出来献丑了,需要高效率的,请搜索专用的线性代数库,比如eigen3,openblas(我没用过,但我下过库),等等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值