斐波那契数列问题:如果一对兔子每月能生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. docker学习笔记(一)

    docker是一种容器技术,现在火的一塌糊涂,最近公司打算用docker统一开发.测试.预上线.上线环境,所以花了时间研究一下. docker是一种容器技术,之前是基于LXC容器,现在已经改成基于li ...

  2. 什么是JWT(JSON WEB TOKEN)

    转自于:http://www.jianshu.com/p/576dbf44b2ae 什么是JWT Json web token(JWT)是为了网络应用环境间传递声明而执行的一种基于JSON的开发标准( ...

  3. OC学习14——谓词

    一.谓词的基本概念与使用 1.谓词(NSPredicate)用于定义一个逻辑条件,通过该条件可执行搜索或内存中的过滤操作.上一篇文章中介绍的集合都提供了使用谓词对集合进行过滤的方法.OC中的谓词操作是 ...

  4. iOS App3D Touch快捷键的静态以及动态设置详细使用

    1. 功能支持 3D-Touch 只在 iOS 9 及以上版本得到支持,之前版本的 iOS 并不支持该功能:3D-Touch 只在 iPhone 6s 及以后型号的 iPhone 或 iPad Pro ...

  5. Git安装和使用(谨记)

    刚开始用git的小白适用,,转自http://www.cnblogs.com/qijunjun/p/7137207.html 实际项目开发中,我们经常会用一些版本控制器来托管自己的代码,今天就来总结下 ...

  6. bzoj 4538: [Hnoi2016]网络

    Description 一个简单的网络系统可以被描述成一棵无根树.每个节点为一个服务器.连接服务器与服务器的数据线则看做一条树边.两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有服 ...

  7. [C#]使用Redis来存储键值对(Key-Value Pair)

    本文为原创文章.源代码为原创代码,如转载/复制,请在网页/代码处明显位置标明原文名称.作者及网址,谢谢! 开发工具:VS2017 语言:C# DotNet版本:.Net FrameWork 4.5及以 ...

  8. Docker入门书籍

    https://yuedu.baidu.com/ebook/d817967416fc700abb68fca1 精细讲解,入门使用极佳.

  9. ValueError: too many values to unpack (expected 2)

    记录下总是码错的地方 for key,value in final_table:#final_table is a dict 然后报错 File "./count_co_Mty_read_n ...

  10. [编织消息框架][网络IO模型]NIO(select and poll)

    上面测试论证系统内核在read data时会阻塞,如果我们在把第一个阶段解决掉那么性能就会提高 NIO 编程 JDK 1.4中的java.nio.*包中引入新的Java I/O库,其目的是提高速度.实 ...