Java与算法之(3) - 斐波那契数列

原创 2016年08月30日 17:19:14

斐波那契数列问题:如果一对兔子每月能生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为止,这显示是递归大显身手的地方。来看代码

public class Fibonacci {
    public static long calc(long n) {
        if(n < 0) {
            return 0;
        }
        if(n == 0 || n == 1) {
            return n;
        } else {
            return calc(n - 1) + calc(n - 2);
        }
    }
}
这真是极短的,测试代码
    public static void main(String[] args) {
        long n = 50;
        long begin = System.nanoTime();
        long f = Fibonacci.calc(n);
        long end = System.nanoTime();
        System.out.println("第" + n + "个斐波那契数是" + f + ", 耗时" + TimeUnit.NANOSECONDS.toMillis(end - begin) + "毫秒");
    }
运行输出
第50个斐波那契数是12586269025, 耗时66024毫秒
注意看消耗的时间,在我的电脑上耗时66秒,真是个相当耗时的操作。既然整个过程都是在不断重复相同的计算规则,那我们可以采用分而治之的思想来优化代码。
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;

public class Fibonacci extends RecursiveTask<Long> {
	
    long n;
    public Fibonacci(long n) {
        this.n = n;
    }

    public Long compute() {
        if(n <= 10) {  //小于10不再分解
            return Fibonacci.calc(n);
        }
        Fibonacci f1 = new Fibonacci(n - 1);  //分解出计算n-1斐波那契数的子任务
        f1.fork();  //由ForkJoinPool分配线程执行子任务
        Fibonacci f2 = new Fibonacci(n - 2);  //分解出计算n-2斐波那契数的子任务
        return f2.compute() + f1.join();
    }

    public static long calc(long n) {
        if(n < 0) {
            return 0;
        }
        if(n == 0 || n == 1) {
            return n;
        } else {
            return calc(n - 1) + calc(n - 2);
        }
    }
	
    public static void main(String[] args) {
        long n = 50;
        long begin = System.nanoTime();
        Fibonacci fibonacci = new Fibonacci(n);
        ForkJoinPool pool = new ForkJoinPool();
        long f = pool.invoke(fibonacci);
        long end = System.nanoTime();
        System.out.println("第" + n + "个斐波那契数是" + f + ", 耗时" + TimeUnit.NANOSECONDS.toMillis(end - begin) + "毫秒");
    }
}
运行输出
第50个斐波那契数是12586269025, 耗时20461毫秒

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

    public static long calcWithoutRecursion(long n) {
        if(n < 0)
            return 0;
        if(n == 0 || n == 1) {
            return n;
        }
        long fib = 0;
        long fibOne = 1;
        long fibTwo = 1;
        for(long i = 2; i < n; i++) {
            fib = fibOne + fibTwo;
            fibTwo = fibOne;
            fibOne = fib;
        }
        return fib;
    }
测试
第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),即斐波那契数列公式。

版权声明:欢迎转载, 转载请保留原文链接。

Fibonacci(斐波那契)数列的递归与非递归实现 python

关于递归算法,可以参考这篇文章 汉诺塔 经典递归算法 in python Fibonacci数列为:0、1、1、2、3、5、8、13、21...... 数列第一项为0,第二项为1,从第三项开始,每...
  • u014496330
  • u014496330
  • 2015年03月26日 15:48
  • 3958

斐波那契数列累加求前n项和

斐波那契累加求前n项和 写一个斐波那契求和的算法,第一反应太简单了,递归一下就求出来了 常规的方法:...
  • diaoaa
  • diaoaa
  • 2014年05月18日 20:13
  • 3169

利用斐波那契数列解决兔子数的算法(java)

今天看到一道算法题,算了半天没算出来,后来查了资料,原来这是一道关于斐波那契数列题目,题目是: 有 一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不...
  • qq_31756531
  • qq_31756531
  • 2016年03月09日 15:56
  • 968

Java多重循环算法问题:(九九乘法表、打印平行四边形、打印空心菱形、打印菱形、搬砖问题、斐波那契数列、猴子吃桃问题、回文数、1!+2!+...+20!)

package jereh1; import java.util.Scanner; public class Class4XunHuan { public static void main(St...
  • j_a_v_a_guan
  • j_a_v_a_guan
  • 2015年08月16日 21:03
  • 1751

Java基础算法——斐波那契数列的多种实现方式!

为什么突然想写这个,只因读了下边某位大佬的一段话! 如果你应聘的是“Web前端开发”岗位,那么你只懂jQuery也可以胜任,那么,“软件工程师”是不是就意味着会HTML、JavaScript和CS...
  • qq_29694039
  • qq_29694039
  • 2017年08月03日 14:54
  • 222

[算法]java实现 二分查找 斐波那契数列 静静思考

问题提出Binary search with addition and subtraction (probleme@ldc.ro, 2004)[1] You are given a sorted a...
  • u014786849
  • u014786849
  • 2015年06月16日 20:55
  • 751

4阶斐波那契数列算法(使用循环队列实现)

  • 2017年04月26日 15:52
  • 16KB
  • 下载

递归算法算斐波那契数列

  • 2008年11月01日 21:28
  • 297B
  • 下载

斐波那契数列用Java实现

  • 2016年01月06日 16:12
  • 1KB
  • 下载

java斐波那契数列编程

  • 2015年11月05日 18:21
  • 381B
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java与算法之(3) - 斐波那契数列
举报原因:
原因补充:

(最多只允许输入30个字)