斐波那契数列问题:如果一对兔子每月能生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. Javascript中的Microtask和Macrotask——从一道很少有人能答对的题目说起

    首先我们来看一道题目,如下javascript代码,执行后会在控制台打印出什么内容? async function async1() { console.log('async1 start'); aw ...

  2. C# 内存模型

    C# 内存模型 This is the first of a two-part series that will tell the long story of the C# memory model. ...

  3. spring boot + Thymeleaf开发web项目

    "Spring boot非常适合Web应用程序开发.您可以轻松创建自包含的HTTP应用.web服务器采用嵌入式Tomcat,或者Jetty等.大多数情况下Web应用程序将使用 spring- ...

  4. Python的HTTP服务实例

    1.前言 今天需要实现一个Pyhton的http服务,与Web的JS进行交换. 2.实例代码 支持HEAD.GET.POST方法,将参数转换为JSON格式,返回结果以JSON字符串返回. import ...

  5. JS画几何图形之五【过圆外一点作切线】

    样例:http://www.zhaojz.com.cn/demo/draw9.html 依赖:[点].[直线].[圆] //画切线 //point 圆外的一点 //dot 圆心 //r 半径 func ...

  6. SpringMVC底层数据传输校验的方案(修改版)

    团队的项目正常运行了很久,但近期偶尔会出现BUG.目前观察到的有两种场景:一是大批量提交业务请求,二是生成批量导出文件.出错后,再执行一次就又正常了. 经过跟踪日志,发现是在Server之间进行jso ...

  7. 编译Twitter的Heron时一直报错“heron/bazel_configure.py", line 25, in <module> import semver ImportError: No module named semver”如何处理。

    今天编译heron的时候,从官方git到的源码bazel_configure的时候一直报错如下: Traceback (most recent call last): File , in <mo ...

  8. C语言学生管理系统(增进版)

    在原版上进行改进,主要改进的功能有. 1.利用atof:将字符串转换为浮点型: 利用atoi:将字符串转换为整型: 原文地址:http://www.cnblogs.com/sddai/p/577412 ...

  9. mayavi安装

    Mayavi是python的一个包,提供方便的可视化方案.目前(20150809)Mayavi还没有py3的支持,以下安装环境在python 2.7.10下进行 安装Mayavi: 1. 通过pip ...

  10. Java实现简单工厂模式

    昨天看了一下设计模式,复习了一下简单工厂模式,做个笔记,浅淡一下我对简单工厂模式的理解.书上使用的是C#,因为我所学的是Java,所以本人就用Java实现了一遍.如果有讲的不对的地方,希望能够指出来. ...