斐波那契数列系列算法最优复杂度-------O(logN)

斐波那契数列系列算法最优复杂度——时间复杂度优化到O(LogN)

对于菲薄那契系列问题的探讨很多,下面就以两个例子来分析:

  • 案例 一:
    在迷迷糊糊的大草原上,小红捡到了n根木棍,第i根木棍的长度为i, 小红现在很开心。想选出其中的三根木棍组成美丽的三角形。 但是小明想捉弄小红,想去掉一些木棍,使得小红任意选三根木棍都不能组成 三角形。 请问小明最少去掉多少根木棍呢? 给定N,返回至少去掉多少根?
    比如N=14.那么木棍的长度分别是1 2 3 4 5 6 7 8 9 10 11 12 13 14
    由于三角形条件必须满足 两边之和大于第三边
    所以我们只要保留木棍长度为 1 2 3 5 8 13的木棍。这些木棍任意组合都无法构成三角形
    观察这个数列,就是满足F(n) = F(n-1)+F(n-2) 的公式 ,这就是纯粹的斐波那契数列问题

  • 案例二(斐波那契相关的探讨,也可以优化到O(logN):
    一个农场,有一个母牛,农场通过生育技术人工授精让母牛只能生母牛而不让生公牛。母牛从第二年开 始生母牛,对于新生的牛需要等过了三年才能生母牛
    假设牛不会死,问n年后农场有多少只母牛?

第一年 :母牛A 1只
第二年: 母牛A生B 2只
第三年: 母牛A生C,A,B 3只
第四年:母牛A生D,A,B,C 4只
第五年:母牛A生E,B生F,A,B,C,D 6只
第六年:母牛A生G,B生H,C生I,A,B,C,D,E,F 9只
上面的多少只的数量满足公式 F(N) = F(N-1)+F(N-3)

对于案例一无非就是求出斐波那契数列问题了:

相信大家都对斐波那契数列已经相当的熟悉了,最多两分钟就可以写出来以下时间复杂度为O(N)的代码:

//递归实现
long long fib(int n)
{
	if (n =1 || n== 2)
	{
		return 1;
	}
	return (fib(n - 2) + fib(n - 1));
}

但是上面的实现的时间复杂度是O(N)面试的时候是没有分的,而我想说的是优化到O(logN),那么下面就来探讨如何让时间复杂度到O(logN):
(由于编辑器实在难敲出数学特殊符号所以我拍成几张图片如下:)

在这里插入图片描述
在这里插入图片描述

下面就是实现代码:

    public static void main(String[] args) {
        System.out.println(fibonacci(5));
        System.out.println(fibonacci2(5));
    }

    /**
     * 普通解法时间复杂度O(N)
     * @param n
     * @return
     */
    public static int fibonacci(int n) {
        if(n<1){
            return 0;
        }
        if(n==1|| n==2){
            return 1;
        }else{
           return fibonacci(n-1)+fibonacci(n-2);
        }
    }

    /**
     * 整个调用时间复杂度O(logN)
     * @param n
     * @return
     */
    public static int fibonacci2(int n){
        if(n<1){
            return 0;
        }
        if(n==1 || n==2){
            return 1;
        }
        int[][] base = new int[][]{
            {1,1},
            {1,0}
        };
        int[][] res = marixPow(base,n-2);
        return res[0][0]+res[1][0];
    }

    private static int[][] marixPow(int[][] base, int n) {
        int[][] res = new int[base.length][base[0].length];
        for(int i=0;i<res.length;i++){
            res[i][i] = 1;//单位矩阵
        }
        int[][] t = base;
        for(;n!=0;n>>=1){
            if((n&1)!=0){//满足指数bit为为1情况
                res = multMaritx(res,t);
            }
            //基数矩阵在不断变化
            t = multMaritx(t,t);
        }
        return res;
    }

    /**
     * 虽然是三个for循环但是矩阵就是个2*2的大小有限。所以实际时间复杂度O(1)
     * @param t
     * @param t1
     * @return
     */
    private static int[][] multMaritx(int[][] t, int[][] t1) {
         int[][] res = new int[t.length][t1[0].length];
         for(int i=0;i<t.length;i++){
             for(int j=0;j<t1[0].length;j++){
                 for(int k=0;k<t1.length;k++){
                     res[i][j] += t[i][k]*t1[k][j];
                 }
             }
         }
         return res;
    }

下面来分析案例二:

由于案例二的公式是:F(N) = F(N-1)+F(N-3)
这是个3X3的矩阵,我们也可以通过带入前面几个值求出矩阵base来:
在这里插入图片描述
以下是案例二的实现代码最优解:O(logN):

/**
 * @author zhanglijie
 * @version 1.0
 * @since 1.1.0 2021/5/7 0007 20:54
 * 一个农场,有一个母牛,农场通过生育技术人工授精让母牛只能生母牛而不让生公牛。母牛从第二年开始生母牛,对于新生的牛需要等过了三年才能生母牛
 * 假设牛不会死,问n年后农场有多少只母牛?
 *
 * 第一年 :母牛A                                 1只
 * 第二年: 母牛A生B                              2只
 * 第三年: 母牛A生C,A,B                        3只
 * 第四年:母牛A生D,A,B,C                       4只
 * 第五年:母牛A生E,B生F,A,B,C,D                6只
 * 第六年:母牛A生G,B生H,C生I,A,B,C,D,E,F      9只
 *                                          13
 *                                          19
 *
 *
 * 公式:F(N) = F(N-1)+F(N-3)
 *
 */
public class CowHowMany {
    public static void main(String[] args) {
       int year =8;
       int sum = getHowManyCow(year);
       System.out.println(sum);
    }

    private static int getHowManyCow(int year) {
        if(year<=3){
            return  year;
        }
        int[][] base = new int[][]{
            {1,1,0},
            {0,0,1},
            {1,0,0}
        };
        int[][]res = martixPow(base,year-3);
        return 3*res[0][0]+2*res[1][0]+1*res[2][0];
    }

    private static int[][] martixPow(int[][] base, int n) {
        int[][] res = new int[base.length][base[0].length];
        for(int i=0;i<res.length;i++){
            res[i][i] = 1;//单位矩阵
        }
        int[][] t = base;
        for(;n!=0;n>>=1){
            if((n&1)!=0){
                res = multMaritx(res,t);
            }
            t = multMaritx(t,t);
        }
        return res;
    }

    private static int[][] multMaritx(int[][] t, int[][] t1) {
        int[][] res = new int[t.length][t1[0].length];
        for(int i=0;i<t.length;i++){
            for(int j=0;j<t1[0].length;j++){
                for(int k=0;k<t1.length;k++){
                    res[i][j] += t[i][k]*t1[k][j];
                }
            }
        }
        return res;
    }
}
 

有关斐波那契套路的算法很多很多,我们在刷leetcode的时候或者在面试笔试的时候都会见到,以上是我对其的总结。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值