斐波那契数列数列相关简化1

斐波那契数列问题介绍:

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=1,F(1)=1, F(n)=F(n - 1)+F(n - 2)(≥ 2,∈ N*)在现代物理、准晶体结构、化学等领域,斐波那契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波那契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

第一、第二项都是1,从第三项开始F(n)=F(n - 1)+F(n - 2),这个使用递归很容易求解:

几行代码就能解决:

public static int fib(int n) {
        if( n == 0) return 0;
        if(n <= 2) return 1;
        return fib(n - 1) + fib(n-2);
    }

对斐波那契数列的求解我们有以下的思考:

1)斐波那契数列的线性求解(O(N))的方式非常好理解

2)同时利用线性代数,也可以改写出另一种表示

 | F(N) , F(N-1) | = | F(2), F(1) |  *  某个二阶矩阵的N-2次方

//|f(3) f(2)| = |f(2) f(1)| * base
//|f(4) f(3)| = |f(3) f(2)| * base
//|f(4) f(3)| = |f(2) f(1)| * base的平方

3)求出这个二阶矩阵,进而最快求出这个二阶矩阵的N-2次方

//因为f(n)肯定是一个整数,而原来的|f(2) f(1)|是一行两列,所以base是2行(要与|f(2) f(1)|的列数相同),有因为结果是一行两列
//所以base的列数也是2,我们假设矩阵是{{a,b},{c,d}}
//|2 1| = |1 1| * base得出 1*a + 1 * c = 2  1 * b+ 1 *d = 1
//|3 2| =  |2 1| * base得出 2*a+1*c=3 2*b+1*d=2,两个推出a = 1, b = 1, c =1 ,d=0
//所以求解出base矩阵是{{1,1},{1,0}}

怎么去求矩阵的n次方呢?我们先看某个数的n次方,比如10的75次方,但是10*10*10...10乘75次的话,那不还是O(N)的时间复杂度吗,这里有别的路径75=64+8+2+1, ,那怎么快速的得到1,2,8,64次方呢,10 本身是10的1次方 10*10(a)就是10的2次方,a*a(b)就是10的四次方, b*b(c)就是10的8次方,c*c(d)就是10的16次方,d*d(e)就是10的32, e*e(f)就是10的64次方,我们一共算了6次,加上10本身也就顶多7次,接近O(longN)。75对应的二进制数是 ....1001011只要把二进制中1,2,8,64所代表的1乘入当前结果就可以了

这种解法的时间复杂度就变成了O(logN)

下面上代码,依然有非常详细的注释:

package dataStructure.fibonacci;

import static dataStructure.danDiaoZhan.SumOfSubarrayMinimums.printArr;

public class FibonacciStandard {
    public static int fib(int n) {
        if( n == 0) return 0;
        if(n <= 2) return 1;
        return fib(n - 1) + fib(n-2);
    }

    public static int fib2(int n) {
        if(n == 0) return 0;
        if(n <= 2) return 1;
        //大于2的时候f(n) = f(n-1) + f(n-2)是稳定的递推结构,有稳定的位置依赖,可以使用矩阵求解
        //|f(3) f(2)| = |f(2) f(1)| * base
        //|f(4) f(3)| = |f(3) f(2)| * base
        //|f(4) f(3)| = |f(2) f(1)| * base的平方
        //
        //因为f(n)肯定是一个整数,而原来的|f(2) f(1)|是一行两列,所以base是2行(要与|f(2) f(1)|的列数相同),有因为结果是一行两列
        //所以base的列数也是2,我们假设矩阵是{{a,b},{c,d}}
        //|2 1| = |1 1| * base得出 1*a + 1 * c = 2  1 * b+ 1 *d = 1
        //|3 2| =  |2 1| * base得出 2*a+1*c=3 2*b+1*d=2,两个推出a = 1, b = 1, c =1 ,d=0
        //所以求解出base矩阵是{{1,1},{1,0}}
        int[][] base = {{1,1},{1,0}};
        //|f(n) f(n - 1)| = |f(2) f(1)| * base的n-2次方
        //先计算base的n-2次方
        int[][] matrix = matrixPower(base, n-2);
        //|f(2) f(1)|是{{1,1}}
        int[][] matrix21 = {{1,1}};
        //计算|f(2) f(1)|乘以base的n-2次方
        matrix = product(matrix21, matrix);
        //返回的结果只有一行{{f(n) f(n-1)}}
        //[0][0]就是f(n)
        return matrix[0][0];
    }

    public static int[][] matrixPower(int[][] m, int p) {
        int[][] res = new int[m.length][m.length];
        //先构建单位矩阵,也就是m的0次方
        for(int i = 0; i < res.length; i++) {
            res[i][i] = 1;
        }
        //矩阵的1次方,这里是为了在不改变m的情况下使用它的平方,4次方,8次方。。。64次方
        int[][] t = m;
        for(;p != 0; p >>= 1) {
            //最右侧是不是1,用完之后右移一位相当于比较上一位
            //拿个数字举例子,75=64+8+2+1,分别是从右往左第7,4,2,1位是1,也就是...1001011
            //分别是t * t^2 * t^8 * t^64
            //t是刚开始进循环的时候t^2是第二次进来的时候,t^8是第四次进来的时候,t^64是第7次进来的时候
            //也就是我们对于75只用了7次,基本是log(N)的级别
            if((p & 1) != 0) {
                res = product(res, t);
            }
            //每次自己变自己的平方依次求2,4,8,16,32,64次方
            t = product(t,t);
        }
        return res;
    }

    public static void main(String[] args) {
        int result = fib(7);
        System.out.println(result);

        int[][] matrix1 = {{2,1}};
        int[][] matrix2 = {{1,1}, {1,0}};
        int[][] matrix = product(matrix1, matrix2);
        if(matrix != null)
        printArr(matrix);

        int n = 7;
        int resultn = fib2(n);
        System.out.println(resultn);
    }

    public static int[][] product(int[][] matrix1, int[][] matrix2) {
        //第一个矩阵的行要和第二个矩阵的列相等
        if(matrix1 == null || matrix2 == null || matrix1[0] == null || matrix2[0] == null || matrix1[0].length != matrix2.length) {
            return null;
        }
        //第一个矩阵的行数
        int n = matrix1.length;
        //第二个矩阵的列数
        int m = matrix2[0].length;
        //第一个矩阵的列数也是第二个矩阵的行数,用于循环里面的计算项
        int k = matrix1[0].length;
        int[][] matrix = new int[n][m];
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                matrix[i][j] = 0;
                for(int k1 = 0; k1 < k; k1 ++) {
                    matrix[i][j] += matrix1[i][k1] * matrix2[k1][j];
                }
            }
        }
        return matrix;
    }
}

可能想理解这个你需要复习一下大学时候线性代数的东西:矩阵的乘法

 看不懂这个分析的话可以看一下百度百科里的解释

矩阵乘法_百度百科

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值