百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

求斐波那契数列(Fibonacci Numbers)算法居然有9种,你知道几种?

haoteby 2025-05-24 14:21 2 浏览

By LongLuo

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:

这个数列从第3项开始,每一项都等于前两项之和

斐波那契数的边界条件是。当时,每一项的和都等于前两项的和,因此有如下递推关系:


算法一: 递归(recursion)

显而易见斐波那契数列存在递归关系,很容易想到使用递归方法来求解:

public class Solution {

    public static int fib(int n) {
        if (n <= 1) {
            return n;
        }

        return fib(n - 1) + fib(n - 2);
    }

    public static void main(String[] args) {
        System.out.println("1 ?= " + fib(1));
        System.out.println("1 ?= " + fib(2));
        System.out.println("2 ?= " + fib(3));
    }
}

复杂度分析:

时间复杂度:,可见是指数级的。


我们可以写出其实现
递归树,如下所示:

                        fib(5)   
                     /                \
               fib(4)                fib(3)   
             /        \              /       \ 
         fib(3)      fib(2)         fib(2)   fib(1)
        /    \       /    \        /      \
  fib(2)   fib(1)  fib(1) fib(0) fib(1) fib(0)
  /     \
fib(1) fib(0)

可以看出其做了很多重复性的计算,因此对于数值比较大时,其性能是灾难性的。

空间复杂度:,函数递归栈。


算法二: 动态规划(dynamic programming)

因为斐波那契数列存在递推关系,因为也可以使用动态规划来实现。动态规划的状态转移方程即为上述递推关系,边界条件为


class Solution {
    public static int fib(int n) {
        /* Declare an array to store Fibonacci numbers. */
        int f[] = new int[n + 2]; // 1 extra to handle case, n = 0
        int i;

        /* 0th and 1st number of the series are 0 and 1*/
        f[0] = 0;
        f[1] = 1;

        for (i = 2; i <= n; i++) {
            /* Add the previous 2 numbers in the series and store it */
            f[i] = f[i - 1] + f[i - 2];
        }

        return f[n];
    }
}   

复杂度分析:

时间复杂度:
空间复杂度:


算法三:记录值的动态规划实现

针对算法二,我们可以将计算好的值存储起来以避免重复运算,如下所示:

    // Initialize array of dp
    public static int[] dp = new int[10];

    public static int fib(int n) {
        if (n <= 1) {
            return n;
        }

        // Temporary variables to store values of fib(n-1) & fib(n-2)
        int first, second;

        if (dp[n - 1] != -1) {
            first = dp[n - 1];
        } else {
            first = fib(n - 1);
        }

        if (dp[n - 2] != -1) {
            second = dp[n - 2];
        } else {
            second = fib(n - 2);
        }

        // Memoization
        return dp[n] = first + second;
    }

复杂度分析

时间复杂度:
空间复杂度:


算法四: 空间优化的动态规划(Space Optimized)

算法二时间复杂度和空间复杂度都是,但由于只和有关,因此可以使用滚动数组思想把空间复杂度优化成。代码如下所示:

class Solution {
    public int fib(int n) {
        if (n < 2) {
            return n;
        }
        int p = 0, q = 0, r = 1;
        for (int i = 2; i <= n; ++i) {
            p = q; 
            q = r; 
            r = p + q;
        }
        return r;
    }
}

复杂度分析:

时间复杂度:
空间复杂度:


算法五:矩阵幂

使用矩阵快速幂的方法可以降低时间复杂度。

首先我们可以构建这样一个递推关系:

因此:

令:

因此只要我们能快速计算矩阵次幂,就可以得到的值。如果直接求取,时间复杂度是

class Solution {
    public static int fib(int n) {
        int F[][] = new int[][]{{1, 1}, {1, 0}};
        if (n == 0) {
            return 0;
        }
        power(F, n - 1);
        return F[0][0];
    }

    /* Helper function that multiplies 2 matrices F and M of size 2*2, and
    puts the multiplication result back to F[][] */
    public static void multiply(int F[][], int M[][]) {
        int x = F[0][0] * M[0][0] + F[0][1] * M[1][0];
        int y = F[0][0] * M[0][1] + F[0][1] * M[1][1];
        int z = F[1][0] * M[0][0] + F[1][1] * M[1][0];
        int w = F[1][0] * M[0][1] + F[1][1] * M[1][1];

        F[0][0] = x;
        F[0][1] = y;
        F[1][0] = z;
        F[1][1] = w;
    }

    /* Helper function that calculates F[][] raise to the power n and puts the
    result in F[][]
    Note that this function is designed only for fib() and won't work as general
    power function */
    public static void power(int F[][], int n) {
        int i;
        int M[][] = new int[][]{{1, 1}, {1, 0}};

        // n - 1 times multiply the matrix to {{1,0},{0,1}}
        for (i = 2; i <= n; i++) {
            multiply(F, M);
        }
    }
}

复杂度分析

时间复杂度:,在于计算矩阵的次幂。
空间复杂度:


算法六:矩阵快速幂(分治快速幂运算)

算法五的时间复杂度是,但可以降低到,因为可以使用分治算法加快幂运算,加速这里的求取。如下所示:

class Solution {
    public int fib(int n) {
        if (n < 2) {
            return n;
        }
        int[][] q = {{1, 1}, {1, 0}};
        int[][] res = pow(q, n - 1);
        return res[0][0];
    }

    public int[][] pow(int[][] a, int n) {
        int[][] ret = {{1, 0}, {0, 1}};
        while (n > 0) {
            if ((n & 1) == 1) {
                ret = multiply(ret, a);
            }
            n >>= 1;
            a = multiply(a, a);
        }
        return ret;
    }

    public int[][] multiply(int[][] a, int[][] b) {
        int[][] c = new int[2][2];
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
            }
        }
        return c;
    }
}

复杂度分析

时间复杂度:
空间复杂度:
,如果认为函数栈也算空间的话。


算法七:斐波那契数新算法求解

这是另外一种求解斐波那契数的算法,证明如下:

1. 矩阵形式的通项

不妨令:

证明:

采用数学归纳法进行证明,

1. 当时:

显然成立!

2. 当时:

2. 偶数项和奇数项

因为,则有:


所以有:

3. 矩形形式求解Fib(n)

因为涉及到矩阵幂次,考虑到数的幂次的递归解法:

为奇数:

为偶数:

根据上述公式,我们可以写出如下代码:

    public static int MAX = 1000;
    public static int f[];

    // Returns n'th fibonacci number using
    // table f[]
    public static int fib(int n) {
        if (n == 0) {
            return 0;
        }

        if (n == 1 || n == 2) {
            return (f[n] = 1);
        }

        // If fib(n) is already computed
        if (f[n] != 0) {
            return f[n];
        }

        int k = (n & 1) == 1 ? (n + 1) / 2 : n / 2;

        // Applying above formula [Note value n&1 is 1 if n is odd, else 0.
        f[n] = (n & 1) == 1 ? (fib(k) * fib(k) + fib(k - 1) * fib(k - 1)) : (2 * fib(k - 1) + fib(k)) * fib(k);

        return f[n];
    }

复杂度分析

时间复杂度:
空间复杂度:


算法八:通项公式(Using Formula)

斐波那契数是齐次线性递推,根据递推方程,可以写出这样的特征方程:

求得

设通解为,代入初始条件,得

因此斐波那契数的通项公式如下:

得到通项公式之后,就可以通过公式直接求解第项。

class Solution {
    public int fib(int n) {
        double sqrt5 = Math.sqrt(5);
        double fibN = Math.pow((1 + sqrt5) / 2, n) - Math.pow((1 - sqrt5) / 2, n);
        return (int) Math.round(fibN / sqrt5);
    }
}

复杂度分析

时间复杂度:
空间复杂度:


算法九:暴力法

如果不需要求解特别大的

class Solution {
public:
    int fib(int n) {
        int nums[31]={0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040};
        return nums[n];
    }
};

复杂度分析

时间复杂度:
空间复杂度:


总结

通过上述,我们使用了9种算法来求解斐波那契数列,这9种方法综合了递归、迭代、数学等各方面知识,值得认真学习!

相关推荐

简单Labview实操案例

有几位条友私信我说Labview是怎么学的,怎么才能学好Labview,今天给大家简单介绍一下,如果想学上位机,Labview是相对来说比较容易上手的,而且开发速度也比较快,但是运行时候比较吃内存,...

关于LabVIEW用于仪器测控的自动测试程序的程序框架的选择问题!

有很长一段时间没有在公众号平台上输出、总结关于LabVIEW的知识文字内容了!主要是这段时间自己本职工作任务甚为繁重,加上各种家庭事宜的牵绊,耗费了过多的时间和精力,也就无力及时更新了。今天是端午节假...

LabVIEW编程基础:分割条控件的使用

1、分割条控件简介同其它高级编程语言类似,在LabVIEW中分割条控件也是界面设计中常用的一种控件元素,利用分割条控件可以将前面板划分为多个独立的区域,每个区域都是一个单独的窗格,这些窗格具有前面板的...

csgo一直显示正在连接到csgo网络怎么办?三招帮你解决

  CSGO是一款射击类的游戏,它的全名叫反恐精英:全球攻势,是一款由VALVE与HiddenPathEntertainment合作开发、ValveSoftware发行的第一人称射击游戏,相信很...

cs1.6没有bot怎么办

Hi~大家好啊,这里是聚合游戏,每天为你分享游戏相关的内容,喜欢的快来关注哟~...

《反恐精英:全球攻势2》 漏洞暴露玩家的IP地址

#文章首发挑战赛#据报道,在全球知名的电子游戏——CS2(《反恐精英:全球攻势2》)中存在一个HTML注入漏洞,这个漏洞被广泛利用来在游戏中注入图片并获取其他玩家的IP地址。...

《电子宠物》《007黄金眼》《雷神之锤》入选世界电子游戏名人堂

世界电子游戏名人堂5月8日公布了新的四位入选者《防卫者》《电子宠物》《007黄金眼》和《雷神之锤》,以向改变游戏行业规则的经典游戏致敬。世界电子游戏名人堂每年都会表彰那些具有持久热度并对视频游戏行业或...

V社修复《反恐精英2》游戏漏洞:可抓取玩家IP地址、发起XSS攻击

IT之家12月12日消息,Valve旗下《反恐精英2》游戏被曝光新的安全漏洞,攻击者通过注入恶意代码来抓取玩家的IP地址,并能对同一游戏大厅中的所有玩家发起跨站脚本攻击(XSS)。攻击...

粉丝自制《CS》1.6重制版将于2025年登陆Steam

基于Valve官方起源引擎SDK,由多位“CSPromod”粉丝项目前开发人员从头构建的《反恐精英》1.6版本重制版《CS:Legacy》日前宣布将于2025年在Steam发布。开发团...

知名网游源代码泄漏 ,外挂潮将来?

SteamDatabase近日发布消息称Valve旗下游戏《反恐精英:全球攻势》(CS:GO)与《军团要塞2》(TF2)的源代码疑遭泄露。据了解,游戏源代码如果泄露的话,黑客可以更为轻松地开发出外挂,...

Pandas每日函数学习之apply函数

...

求斐波那契数列(Fibonacci Numbers)算法居然有9种,你知道几种?

ByLongLuo斐波那契数列...

三维基因组:Loop结构 差异分析(2)

通过聚合峰分析进行可视化既然已经找出了“WT”和“FS”条件之间的差异loop结构,就可以利用聚合峰分析(APA)来直观地展示loop结构调用的质量。APA是一种以Hi-C数据中的中心loop像...

用Excel制作动态图表(动态名称法)

动态图表也称交互式图表,指图表的内容可以随用户的选择而变化,是图表分析中比较高级的形式。使用动态图表能够突出重点数据,避免被其他不需要的数据干扰,从而提高数据分析效率。一个好的动态图表,可以让人从大量...

Prometheus PromQL语法简介

...