斐波那契数列

本文主旨

旨在分享斐波那契数列数列的多种解法。

主要内容

先开始bb一下。首先为什么要写这篇文章呐?小学生么,不是一个简单的递归就能解决的事情么,对呀,博主在这里主要是为了开拓思维。呸,是某些编程网站上面时间复杂度达不到要求好吧,可怜我脆弱的内心,好不容易知道了递归的解法,却告诉我不行?噢,好吧,您是老大,为了求您赏碗饭吃,我改好吧,直到让你满意为止(心里...),好了,开始正文的内容吧。

首先看一下斐波那契数列需要解决的问题:斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........这个数列从第3项开始,每一项都等于前两项之和。

  1. 你知道递归解法么?它的时间复杂度为O(2^n)

看题目都说了,这个数列从第三项开始,每一项都等于前两项之和,那么可以列出表达式,当n>2时,fn=fn-1+f(n-2),so easy ,题目直接给出了公式,那么写的代码也是一目了然:

public int Fibonacci(int n){
    if(n==1)
        return 1;
    else if(n==2)
        return 1;
    else
        return Fibonacci(n-1)+Fibonacci(n-2);
}

这么简答的代码,怎么不符合时间复杂度的要求呢?那么我们来分析一下:

在程序的运行过程中,我们可以画出递归树:

原来在递归调用过程中,同一个数据会被我们的计算机多次计算,导致时间复杂度过高,同时还可能因为n取的过大,导致递归栈overflow,这就是很严重的事故了,于是想破脑袋来改进:既然同一个数据会被计算多次,且题目已经给出,从第三项开始,后面的每一项都等于前面两项的和,这就是说,我们可以先保存前面的两个数,直接计算第三个数,这样即得到了循环的解法

    2.你知道DP解法么?它的时间复杂度为O(n)

通过上面的分析,DP解法使得算法的性能得到了极大的提升,运行起来也是飕飕的,在这里只是使用了有限个辅助变量,空间复杂度为O(1),棒极了,那么接下来看一下我们的code:

public int Fibonacci2(int n) {
    if(n<=0) return 0;
    if(n==1||n==2) return 1;
    int one = 1;
    int two = 1;
    int result = 0;
    for (int i = 2; i < n; i++) {
        result = one + two;
        one = two;
        two = result;
    }
    return result;
}

正准备收手,出去上个厕所的时候,大佬还是不放过我:

大佬言:“这是个数学题,斐波那契是个数学家,难道会没有数学的解法么?再来思考一下。”

我:“……”

  3.你知道矩阵快速幂的解法么?它的时间复杂度为O(logn)

于是继续苦逼的思考着,细想一下,大佬说的也对,斐波那契是个数学家,他肯定会想到更好的方法去解决这个问题的,嗯,我去找找资料了…

一圈资料找下来,确实哦,这里还有一个矩阵的解法,称为矩阵的快速幂,它的效率更高了,只要O(logn),于是我惊呼:大佬就是大佬,思维都比我发散的多。

那么什么是矩阵快速幂呢?

数列的递推公式为:f(1)=1,f(2)=1,f(3)=2 , f(n)=f(n-1)+f(n-2)(n>=3)用矩阵表示为:

进一步,可以得出直接推导公式:

接下来就是实现Code的时刻了:

 public int[][] matrixPower(int[][] m, int p) {

        int[][] res = new int[m.length][m[0].length];

        //先把res设为单位矩阵

        for (int i = 0; i < res.length; i++) {

            res[i][i] = 1;

        } //单位矩阵乘任意矩阵都为原来的矩阵

        //用来保存每次的平方

        int[][] tmp = m;

        //p每循环一次右移一位

        for ( ; p != 0; p >>= 1) {

            //如果该位不为零,应该乘

            if ((p&1) != 0) {

                res = multiMatrix(res, tmp);

            }

            //每次保存一下平方的结果

            tmp = multiMatrix(tmp, tmp);

        }

        return res;

    }

解释:m是底数,p是幂,相当于计算m^p

为什么把p一位一位移动?

把p转换成2进制数,然后把为1的位提取出来

保存平方结果目的是什么呢?

后面平方的方法就是计算两个矩阵相乘,.就是说把幂转成2进制,从右往左开始累乘, 然后高一位的数字比低一位的数字权值大两倍,反应到矩阵上就是平方,当前位代表累乘的阶数(如第3位,则,当前位是m^(2^3),到了第四位,则是m^(2^4),实际上就是第三位的平方),所以在计算完当前位之后,都平方来保存一下,方便以后的计算。

计算出来需要返回的数据即f(n)=res[0][1];

总结

总体来说,前面两种算法比较容易掌握,矩阵的快速幂是难点,如果博主以后碰到类似的题,会加到里面来。

总而言之,为么要这么多的解法呢?第一是为了达到要求,第二,现在就是拓展自己的思维的时刻,多积累一点总是好的,fighting!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值