斐波那契数列问题:如果一对兔子每月能生1对小兔子,而每对小兔在它出生后的第三个月里,又能开始生1对小兔子,假定在不发生死亡的情况下,由一对初生的兔子开始,1年后能繁殖出多少对兔子?

首先手工计算来总结规律,如下表

注意总数这一列

1+1=2

1+2=3

2+3=5

3+5=8

5+8=13

可以得出规律,第n个斐波那契数=第n-1个斐波那契数+第n-2个斐波那契数

为了计算n,必须计算n-1和n-2;为了计算n-1,必须计算n-2和n-3;直到n-x的值为1为止,这显示是递归大显身手的地方。来看代码

  1. public class Fibonacci {
  2. public static long calc(long n) {
  3. if(n < 0) {
  4. return 0;
  5. }
  6. if(n == 0 || n == 1) {
  7. return n;
  8. } else {
  9. return calc(n - 1) + calc(n - 2);
  10. }
  11. }
  12. }

这真是极短的,测试代码

  1. public static void main(String[] args) {
  2. long n = 50;
  3. long begin = System.nanoTime();
  4. long f = Fibonacci.calc(n);
  5. long end = System.nanoTime();
  6. System.out.println("第" + n + "个斐波那契数是" + f + ", 耗时" + TimeUnit.NANOSECONDS.toMillis(end - begin) + "毫秒");
  7. }

运行输出

  1. 第50个斐波那契数是12586269025, 耗时66024毫秒

注意看消耗的时间,在我的电脑上耗时66秒,真是个相当耗时的操作。既然整个过程都是在不断重复相同的计算规则,那我们可以采用分而治之的思想来优化代码。

  1. import java.util.concurrent.ForkJoinPool;
  2. import java.util.concurrent.RecursiveTask;
  3. import java.util.concurrent.TimeUnit;
  4. public class Fibonacci extends RecursiveTask<Long> {
  5. long n;
  6. public Fibonacci(long n) {
  7. this.n = n;
  8. }
  9. public Long compute() {
  10. if(n <= 10) {  //小于10不再分解
  11. return Fibonacci.calc(n);
  12. }
  13. Fibonacci f1 = new Fibonacci(n - 1);  //分解出计算n-1斐波那契数的子任务
  14. f1.fork();  //由ForkJoinPool分配线程执行子任务
  15. Fibonacci f2 = new Fibonacci(n - 2);  //分解出计算n-2斐波那契数的子任务
  16. return f2.compute() + f1.join();
  17. }
  18. public static long calc(long n) {
  19. if(n < 0) {
  20. return 0;
  21. }
  22. if(n == 0 || n == 1) {
  23. return n;
  24. } else {
  25. return calc(n - 1) + calc(n - 2);
  26. }
  27. }
  28. public static void main(String[] args) {
  29. long n = 50;
  30. long begin = System.nanoTime();
  31. Fibonacci fibonacci = new Fibonacci(n);
  32. ForkJoinPool pool = new ForkJoinPool();
  33. long f = pool.invoke(fibonacci);
  34. long end = System.nanoTime();
  35. System.out.println("第" + n + "个斐波那契数是" + f + ", 耗时" + TimeUnit.NANOSECONDS.toMillis(end - begin) + "毫秒");
  36. }
  37. }

运行输出

  1. 第50个斐波那契数是12586269025, 耗时20461毫秒

虽然时间缩短了2/3,但是仍然不理想。回头重新看计算方法,用递归方式虽然代码简短,但是存在很严重的重复计算,下面用非递归的方式改写,过程中每个数只计算一次。

  1. public static long calcWithoutRecursion(long n) {
  2. if(n < 0)
  3. return 0;
  4. if(n == 0 || n == 1) {
  5. return n;
  6. }
  7. long fib = 0;
  8. long fibOne = 1;
  9. long fibTwo = 1;
  10. for(long i = 2; i < n; i++) {
  11. fib = fibOne + fibTwo;
  12. fibTwo = fibOne;
  13. fibOne = fib;
  14. }
  15. return fib;
  16. }

测试

  1. 第50个斐波那契数是12586269025, 耗时0毫秒

斐波那契数的另一个经典题目是青蛙跳台阶问题:

一只青蛙一次可以条一级或两级台阶,求该青蛙跳上n级的台阶共有多少种跳法。

假设计算第n级台阶跳法的函数是f(n),当n>2时,第一步选择跳一级有X种跳法,第一步选择跳两级有Y种跳法,f(n)=X+Y。如何计算X呢,站在青蛙的位置考虑,面对的是一个全新的n-1级台阶,有f(n-1)种跳法,那么Y就是n-2级台阶的跳法,那么f(n)=f(n-1)+f(n-2),即斐波那契数列公式。

Java与算法之(3) - 斐波那契数列的更多相关文章

  1. Python开发【算法】:斐波那契数列两种时间复杂度

    斐波那契数列 概述: 斐波那契数列,又称黄金分割数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.34.……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1, ...

  2. 剑指offer编程题Java实现——面试题9斐波那契数列

    题目:写一个函数,输入n,求斐波那契数列的第n项. package Solution; /** * 剑指offer面试题9:斐波那契数列 * 题目:写一个函数,输入n,求斐波那契数列的第n项. * 0 ...

  3. 算法之路(三)----查找斐波纳契数列中第 N 个数

    算法题目 查找斐波纳契数列中第 N 个数. 所谓的斐波纳契数列是指: * 前2个数是 0 和 1 . * 第 i 个数是第 i-1 个数和第i-2 个数的和. 斐波纳契数列的前10个数字是: 0, 1 ...

  4. 算法导论-求(Fibonacci)斐波那契数列算法对比

    目录 1.斐波那契数列(Fibonacci)介绍 2.朴素递归算法(Naive recursive algorithm) 3.朴素递归平方算法(Naive recursive squaring) 4 ...

  5. java 递归及其经典应用--求阶乘、打印文件信息、计算斐波那契数列

    什么是递归 我先看下百度百科的解释: 一种计算过程,如果其中每一步都要用到前一步或前几步的结果,称为递归的.用递归过程定义的函数,称为递归函数,例如连加.连乘及阶乘等.凡是递归的函数,都是可计算的,即 ...

  6. Javascript数组求和的方法总结 以及由斐波那契数列得到的启发

    一次面试中,面试官要求用三种不同的Javascript方法进行一个数字数组的求和,当时思来想去只想到了使用循环这一种笨方法,因此面试比较失败,在这里总结了六种Javascript进行数组求和的方法,以 ...

  7. hdu 4549 M斐波那契数列 矩阵快速幂+欧拉定理

    M斐波那契数列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Problem ...

  8. java程序员到底该不该了解一点算法(一个简单的递归计算斐波那契数列的案例说明算法对程序的重要性)

    为什么说 “算法是程序的灵魂这句话一点也不为过”,递归计算斐波那契数列的第50项是多少? 方案一:只是单纯的使用递归,递归的那个方法被执行了250多亿次,耗时1分钟还要多. 方案二:用一个map去存储 ...

  9. 算法笔记_001:斐波那契数的多种解法(Java)

    本篇文章解决的问题来源于算法设计与分析课程的课堂作业,主要是运用多种方法来计算斐波那契数.具体问题及解法如下: 一.问题1: 问题描述:利用迭代算法寻找不超过编程环境能够支持的最大整数的斐波那契数是第 ...

随机推荐

  1. AsyncLocal的运作机制和陷阱

    这是今天帮柠檬分析一个AsyncLocal相关的问题时发现的. 试想这个代码输出的值是多少? using System; using System.Threading; using System.Th ...

  2. 《程序设计语言——实践之路》【PDF】下载

    程序设计语言--实践之路>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382240 内容简介 本书在美国大学已有使用了十余年,目前被欧 ...

  3. 《Spark大数据处理:技术、应用与性能优化》【PDF】

    内容简介 <Spark大数据处理:技术.应用与性能优化>根据最新技术版本,系统.全面.详细讲解Spark的各项功能使用.原理机制.技术细节.应用方法.性能优化,以及BDAS生态系统的相关技 ...

  4. ASP.NET Core MVC请求超时设置解决方案

    设置请求超时解决方案 当进行数据导入时,若导入数据比较大时此时在ASP.NET Core MVC会出现502 bad gateway请求超时情况(目前对于版本1.1有效,2.0未知),此时我们需要在项 ...

  5. 【liferay】3、liferay 添加spring支持

    1.添加对应的spring的jar 地址:https://spring.io/projects 选中springframework 进入git源码的地方,看简介 我们需要编译好的jar 当然也可以自己 ...

  6. IIS加载JSON文件 错误 404

    问题描述 在发布项目的时候,有一些文件是json文件,在网页中进行加载,但是在IIS7发布的时候,json文件居然是404,无法找到,在URL上输入地址也一样. 错误原因 IIS内部机制,不支持直接访 ...

  7. SQL Server授权购买简单介绍

    SQL Server授权购买简单介绍 之前有同事问我,使用盗版序列号的SQL Server到底有没有性能限制,之前本人一直没有深入研究过,后来经过一番资料搜集和查证,汇总成这篇文章 微软的SQL Se ...

  8. 区分javascript中的toString(),toLocaleString(),valueOf()方法

    首先我们随意创建一个对象,这很简单,打开FF浏览器的Firebug切换到控制台或者打开webkit浏览器的审查元素功能. 输入以下内容: var obj1=[1,2,3,4,5] var obj2=[ ...

  9. oracle 处理时间和金额大小写的相关函数集合

    CREATE OR REPLACE FUNCTION MONEY_TO_CHINESE(MONEY IN VARCHAR2) RETURN VARCHAR2 IS C_MONEY ); M_STRIN ...

  10. thinkinginjava学习笔记06_复用类

    MarsEdit粘代码好麻烦,所有代码交给github:https://github.com/lozybean/MyJavaLearning 复用一个类常用的两种方式:组合.继承: 组合 将对象引用置 ...