大家好啊,我们又见面了。听说有人想学数据结构与算法却不知道从何下手?那你就认真看完本篇文章,或许能从中找到方法与技巧。

    本期我们就从斐波那契数列的几种解法入手,感受算法的强大与奥妙吧。

原文链接:原文来自个人公众号:C you again,欢迎关注

斐波那契数列

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

    斐波那契数列指的是这样一个数列:

0、1、1、2、3、5、8、13、21、34......

    有一组数列,它的第一项为1,第二项为1,从第三项开始,每一项为前两项之和。

    斐波那契数列的第n项Fn可以通过如下的递归公式定义:

F(1)=1,F(2)=1,

F(n)=F(n-1)+F(n-2)(n ≥ 3,n ∈ N*)

通项公式



    如上,又称为“比内公式”,是用无理数表示有理数的一个范例。

注:此时a1=1,a2=1,a(n)=a(n-1)+a(n-2),(n ≥ 3,n ∈ N*)

求第n项斐波那契数

    现在写一个函数int fib(int n) 返回第n项Fn。例如,若n=0,则函数fib(0)应该返回0,若n=1, 则函数fib(1)应返回1,若 n > 1,返回 F(n-1)+F(n-2)。

若n = 9

输出:34

    下面是返回斐波那契数列第n项Fn的不同方法:

方法1 (使用递归)

    一个简捷的方法是直接使用递归定义关系式写出递归实现的代码,C/C++代码如下:

  1. //Fibonacci Series using Recursion
  2. #include<stdio.h>
  3. int fib(int n) {
  4. if (n <= 1)
  5. return n;
  6. return fib(n - 1) + fib(n - 2);
  7. }
  8. int main() {
  9. int n = 9;
  10. printf("%d", fib(n));
  11. return 0;
  12. }

输出:34

时间复杂度:T(n) = T(n-1) + T(n-2),该时间复杂度是指数级别的

空间复杂度:如果考虑递归调用时栈的大小,则为O(n) ;如果不考虑调用栈的话,则为O(1)

    通过观察,我们可以发现递归求解时做了很多重复的工作(见下面的递归调用树)。因此采用递归方式求解斐波那契数列的第n项Fn不是一种好的方法。



方法2 (使用动态规划Dynamic Programming:DP)

    如果你还不了解动态规划,请看以下两篇文章:

    《深入浅出理解动态规划(一) | 交叠子问题》

    《深入浅出理解动态规划(二) | 最优子结构》

    在方法1中,在求解某项时,如果我们把计算结果存储起来,则后续的计算就可以使用前面的计算结果,从而可以避免很多重复的计算,C/C++代码如下:

  1. //Fibonacci Series using Dynamic Programming
  2. #include<stdio.h>
  3. int fib(int n) {
  4. /* Declare an array to store Fibonacci numbers. */
  5. int f[n + 1];
  6. int i;
  7. /* 0th and 1st number of the series are 0 and 1*/
  8. f[0] = 0;
  9. f[1] = 1;
  10. for (i = 2; i <= n; i++) {
  11. /* Add the previous 2 numbers in the series
  12. and store it */
  13. f[i] = f[i - 1] + f[i - 2];
  14. }
  15. return f[n];
  16. }
  17. int main() {
  18. int n = 9;
  19. printf("%d", fib(n));
  20. return 0;
  21. }

输出:34

时间复杂度:O(n)

空间复杂度: O(n)

方法3 (对方法2进行空间上的优化)

    由于在计算某项时只需其前面相邻的两项,因此可以对方法2中的空间进行优化,C/C++代码如下:

  1. // Fibonacci Series using Space Optimized Method
  2. #include<stdio.h>
  3. int fib(int n) {
  4. int a = 0, b = 1, c, i;
  5. if (n == 0)
  6. return a;
  7. for (i = 2; i <= n; i++) {
  8. c = a + b;
  9. a = b;
  10. b = c;
  11. }
  12. return b;
  13. }
  14. int main() {
  15. int n = 9;
  16. printf("%d", fib(n));
  17. return 0;
  18. }

输出:34

时间复杂度: O(n)

空间复杂度: O(1)

    当然,也可以使用滚动数组。滚动数组不是什么高大上的技术,我们在计算斐波那契数列的过程中,始终使用相邻的前两项,加上正在计算的项,总共就三项,因此可以定义一个长度只有3的数组,可以滚动地使用0、1、2这三个下标。代码如下:

  1. //Fibonacci Series using Dynamic Programming
  2. #include<stdio.h>
  3. int fib(int n) {
  4. /* Declare an array to store Fibonacci numbers. */
  5. int f[3]; /* 只需定义一个长度为3的数组 */
  6. int i;
  7. /* 0th and 1st number of the series are 0 and 1*/
  8. f[0] = 0;
  9. f[1] = 1;
  10. for (i = 2; i <= n; i++) {
  11. /* Add the previous 2 numbers in the series
  12. and store it:注意下标要对3取模 */
  13. f[i % 3] = f[(i - 1) % 3] + f[(i - 2) % 3];
  14. }
  15. return f[n % 3]; /* 这里要注意下标对3取模 */
  16. }
  17. int main() {
  18. int n = 9;
  19. printf("%d", fib(n));
  20. return 0;
  21. }

方法4 (使用矩阵{{1,1},{1,0}}的幂)

    另外一种复杂度为O(n)的方法是对矩阵M={{1,1},{1,0}}自乘n次(换句话说,就是计算矩阵M的n次幂:power(M,n)), 这样就可以在结果矩阵下标为(0, 0)的地方得到斐波那契数列的第(n+1)项,如下所示:

  1. #include <stdio.h>
  2. /* Helper function that multiplies 2 matrices F and M of size 2*2, and puts the multiplication result back to F[][] */
  3. void multiply(int F[2][2], int M[2][2]);
  4. /* 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 */
  5. void power(int F[2][2], int n);
  6. int fib(int n) {
  7. int F[2][2] = { { 1, 1 }, { 1, 0 } };
  8. if (n == 0)
  9. return 0;
  10. power(F, n - 1);
  11. return F[0][0];
  12. }
  13. void multiply(int F[2][2], int M[2][2]) {
  14. int x = F[0][0] * M[0][0] + F[0][1] * M[1][0];
  15. int y = F[0][0] * M[0][1] + F[0][1] * M[1][1];
  16. int z = F[1][0] * M[0][0] + F[1][1] * M[1][0];
  17. int w = F[1][0] * M[0][1] + F[1][1] * M[1][1];
  18. F[0][0] = x;
  19. F[0][1] = y;
  20. F[1][0] = z;
  21. F[1][1] = w;
  22. }
  23. void power(int F[2][2], int n) {
  24. int i;
  25. int M[2][2] = { { 1, 1 }, { 1, 0 } };
  26. // n - 1 times multiply the matrix to {{1,0},{0,1}}
  27. for (i = 2; i <= n; i++)
  28. multiply(F, M);
  29. }
  30. /* Driver program to test above function */
  31. int main() {
  32. int n = 9;
  33. printf("%d", fib(n));
  34. return 0;
  35. }

输出:34

时间复杂度: O(n)

空间复杂度: O(1)

方法 5 (对方法4进行优化 )

    上面的方法4可以优化到)的时间复杂度。我们可以像计算x^n那样,采用递归的方式来计算power(M, n) ,C/C++代码如下:

  1. #include <stdio.h>
  2. void multiply(int F[2][2], int M[2][2]);
  3. void power(int F[2][2], int n);
  4. /* function that returns nth Fibonacci number */
  5. int fib(int n) {
  6. int F[2][2] = { { 1, 1 }, { 1, 0 } };
  7. if (n == 0)
  8. return 0;
  9. power(F, n - 1);
  10. return F[0][0];
  11. }
  12. /* Optimized version of power() in method 4 */
  13. void power(int F[2][2], int n) {
  14. if (n == 0 || n == 1)
  15. return;
  16. int M[2][2] = { { 1, 1 }, { 1, 0 } };
  17. power(F, n / 2);
  18. multiply(F, F);
  19. if (n % 2 != 0)
  20. multiply(F, M);
  21. }
  22. void multiply(int F[2][2], int M[2][2]) {
  23. int x = F[0][0] * M[0][0] + F[0][1] * M[1][0];
  24. int y = F[0][0] * M[0][1] + F[0][1] * M[1][1];
  25. int z = F[1][0] * M[0][0] + F[1][1] * M[1][0];
  26. int w = F[1][0] * M[0][1] + F[1][1] * M[1][1];
  27. F[0][0] = x;
  28. F[0][1] = y;
  29. F[1][0] = z;
  30. F[1][1] = w;
  31. }
  32. /* Driver program to test above function */
  33. int main() {
  34. int n = 9;
  35. printf("%d", fib(n));
  36. return 0;
  37. }

输出:34

时间复杂度: O(Logn)

空间复杂度: 如果考虑递归调用时栈的大小,则为O(n) ;如果不考虑调用栈的话,则为O(1)

方法 6 (O(Log n) 的时间复杂度)

    下面是一个很有趣的计算斐波那契数列第n项的递归公式,该公式的时间复杂度为O(Log n)。

如果n是偶数, 则k=n/2,

F(n)=[2F(k-1)+F(k)]F(k)

如果n是奇数,则 k=(n+1)/2

F(n)=F(k)F(k)+F(k-1)F(k-1)

原文链接:原文来自个人公众号:C you again,欢迎关注

    该公式是如何计算的?上面的公式可以从前面的矩阵幂推算出来:





要证明上面的公式成立,只需做下面的工作即可:

如果n是偶数, 令 k = n/2

如果n是奇数, 令 k = (n+1)/2

    下面是上述过程的C++ 实现:

  1. // C++ Program to find n'th fibonacci Number in
  2. // with O(Log n) arithmatic operations
  3. #include <bits/stdc++.h>
  4. using namespace std;
  5. const int MAX = 1000;
  6. // Create an array for memoization
  7. int f[MAX] = { 0 };
  8. // Returns n'th fuibonacci number using table f[]
  9. int fib(int n) {
  10. // Base cases
  11. if (n == 0)
  12. return 0;
  13. if (n == 1 || n == 2)
  14. return (f[n] = 1);
  15. // If fib(n) is already computed
  16. if (f[n])
  17. return f[n];
  18. int k = (n & 1) ? (n + 1) / 2 : n / 2;
  19. // Applyting above formula [Note value n&1 is 1
  20. // if n is odd, else 0.
  21. f[n] = (n & 1) ?
  22. (fib(k) * fib(k) + fib(k - 1) * fib(k - 1)) :
  23. (2 * fib(k - 1) + fib(k)) * fib(k);
  24. return f[n];
  25. }
  26. /* Driver program to test above function */
  27. int main() {
  28. int n = 9;
  29. printf("%d ", fib(n));
  30. return 0;
  31. }

输出:34

时间复杂度为:O(Log n) ,因为每次递归调用时都将问题规模降了一半

方法 7 (使用Java提供的BigInteger类)

     Java提供了BigInteger类,可以很轻易地算出当n很大时的斐波那契数。

  1. // Java program to compute n-th Fibonacci number where n may be large.
  2. import java.math.*;
  3. public class Fibonacci {
  4. // Returns n-th Fibonacci number
  5. static BigInteger fib(int n) {
  6. BigInteger a = BigInteger.valueOf(0);
  7. BigInteger b = BigInteger.valueOf(1);
  8. BigInteger c = BigInteger.valueOf(1);
  9. for (int j = 2; j <= n; j++) {
  10. c = a.add(b);
  11. a = b;
  12. b = c;
  13. }
  14. return (a);
  15. }
  16. public static void main(String[] args) {
  17. int n = 1000;
  18. System.out.println("Fibonacci of " + n + "th term" + " " + "is" + " " + fib(n));
  19. }
  20. }

当n=1000时,输入结果如下:

原文链接:原文来自个人公众号:C you again,欢迎关注

公众号推荐(资源加油站)

了解更多资源请关注个人公众号:C you again,你将收获以下资源

1、PPT模板免费下载,简历模板免费下载

2、基于web的机票预订系统基于web的图书管理系统

3、贪吃蛇小游戏源码

4、各类IT技术分享

文章推荐

推荐一:计算机网络中这些高频考题,你还在死记硬背吗?(一),讲述内容:IP地址及其分类,子网掩码的概念,网络号、主机号、直接广播地址计算方法等。

推荐二:计算机网络中这些高频考题,你还在死记硬背吗?(二),讲述内容:局域网接口配置、路由器的静态路由配置、OSPF动态路由协议配置和DHCP服务器配置。

    以上就是本期的所有内容了,是否对你有帮助呢?了解更多算法请关注公众号“C you again”。

用x种方式求第n项斐波那契数,99%的人只会第一种的更多相关文章

  1. 非递归和递归分别实现求第n个斐波那契数。

    菲波那切数列为:0 1 1 2 3 5 8 13 21 34... 规律:从第三个数字起后面的每一个数字都是前两个数字的和. 非递归算法: #include<stdio.h> int ma ...

  2. 【C++】【斐波那契】求第几个斐波那契数字。

    首先在头文件 whichfibonaccinumber.h 中写了一个使用加法的解法.没有验证输入数字是否小于0. #ifndef WHICHFIBONACCINUMBER_H_ #define WH ...

  3. POJ 3070(求斐波那契数 矩阵快速幂)

    题意就是求第 n 个斐波那契数. 由于时间和内存限制,显然不能直接暴力解或者打表,想到用矩阵快速幂的做法. 代码如下: #include <cstdio> using namespace ...

  4. 数学算法(一):快速求斐波那契数第n项通过黄金分割率公式

    有一个固定的数学公式= =,不知道的话显然没法应用 首先黄金分割率接近于这个公式, (以下为黄金分割率与斐波那契的关系,可跳过) 通过斐波那契数列公式 两边同时除以 得: (1) 注意后一项比前一项接 ...

  5. C++求斐波那契数

    题目内容:斐波那契数定义为:f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2)(n>1且n为整数) 如果写出菲氏数列,则应该是: 0 1 1 2 3 5 8 13 21 34 …… ...

  6. python递归方式和普通方式实现输出和查询斐波那契数列

    ●斐波那契数列 斐波那契数列(Fibonacci sequence),是从1,1开始,后面每一项等于前面两项之和. 如果为了方便可以用递归实现,要是为了性能更好就用循环. ◆递归方式实现生成前30个斐 ...

  7. hdu1568&&hdu3117 求斐波那契数前四位和后四位

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1568 题意:如标题所示,求斐波那契数前四位,不足四位直接输出答案 斐波那契数列通式: 当n<=2 ...

  8. HDU 1568 Fibonacci【求斐波那契数的前4位/递推式】

    Fibonacci Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Proble ...

  9. Java算法求最大最小值,冒泡排序,斐波纳契数列一些经典算法<不断更新中>

    清明在家,无聊,把一些经典的算法总结了一下. 一.求最大,最小值 Scanner input=new Scanner(System.in); int[] a={21,31,4,2,766,345,2, ...

随机推荐

  1. js事件入门(4)

    4.表单事件 表单事件处理主要用来验证表单,可以处理用户在表单上所做的任何操作. 4.1.onsubmit事件 当用户点击submit按钮来提交表单时,就会触发onsubmit事件,如果事件处理程序返 ...

  2. SpringBoot之入门教程-SpringBoot项目搭建

    SpringBoot大大的简化了Spring的配置,把Spring从配置炼狱中解救出来了,以前天天配置Spring和Mybatis,Springmvc,Hibernate等整合在一起,感觉用起来还是挺 ...

  3. 【总结-前台发送后台接收表单】MVC提交表单的四种方式

    https://www.cnblogs.com/chenwolong/p/Form.html#commentform 后台控制器接收前台表单参数三种方法: 一.普通参数 HTML标签name 和参数名 ...

  4. 请写出在ASP.NET中常用的几种页面间传值的方法,并说出它们的特点。

    QueryString 传递一个或多个安全性要求不高或是结构简单的数值.但是对于传递数组或对象的话,就不能用这个方法了 session(viewstate) 简单,但易丢失 作用于用户个人,过量的存储 ...

  5. 一.2.序列化使用之用户资源功能app

    1.环境准备:(1).新建功能app: (python36env) [vagrant@CentOS7 devops]$ python manage.py startapp users ---建议以后用 ...

  6. 使用Tensorflow对模型进行量化

    本文旨在将迁移学习训练好的模型基于tensorflow工具进行量化. 环境配置及迁移学习部分可参考博文[https://www.cnblogs.com/hayley111/p/12887853.htm ...

  7. MACOS使用VScode进行C语言编程

    [B站有同步视频教程] 安装VScode 从官网下载vscode安装https://code.visualstudio.com/ 安装code runner插件 配置code runner从终端输出 ...

  8. CSS如何将图像转换为模糊图像?

    在CSS中,可以使用filter属性来模糊处理图像:filter属性用于将图像转换为模糊图像.该属性主要用于设置图像的视觉效果. 语法: filter: blur() 属性值: ● blur():给图 ...

  9. RESTful API 规范(一)

    一,简介 DRF 即Django rest framework 二,rest 规范 1 协议 API 与用户通信,总是使用https协议 2 域名 1) 应尽量将API 部署在域名下(这种情况会存在跨 ...

  10. Python——查看目录下所有的目录和文件

    写程序我们经常会遇到需要遍历某一个目录下的所有文件这个操作,然而python有现成的库,只需要2个循环就可以搞定. import os def all_path(dirname): result = ...