【问题标题】:inefficient recursive code to produce Fibonacci number in java [duplicate]在java中产生斐波那契数的低效递归代码[重复]
【发布时间】:2018-03-21 03:23:21
【问题描述】:

以下递归方法旨在为给定整数生成斐波那契数(用 Java 编写)

   public static long fib(int n)
   {
      if (n == 0)
         return (long)0;
      else if (n == 1)
         return (long)1;
      else
         return fib(n - 1) + fib(n - 2);
   }

但我发现,在第 48 位或更高的位置生成斐波那契数需要 20 多秒。你能帮忙解释一下为什么这个 Fib 生产者效率这么低吗?

例如,这里我附上一个简单的测试客户端:

  public static void main(String[] args)
   {
       int hi = 50;

       System.out.println("Sequance, elapsed time, number");

       for (int n = 0; n<= hi; n++)
       {
            long start = System.currentTimeMillis();
            long fib_num = fib(n);
            long end = System.currentTimeMillis();
            long elapse = (end-start)/1000;  
            System.out.printf("%d, %d, %d%n", n, elapse, fib_num);
       }
   }

它的输出是(在 i7、4 核 MacBook Pro 2017 型号上运行):

Sequance, elapsed time, number
0, 0, 0
1, 0, 1
2, 0, 1
3, 0, 2
4, 0, 3
5, 0, 5
6, 0, 8
7, 0, 13
8, 0, 21
9, 0, 34
10, 0, 55
11, 0, 89
12, 0, 144
13, 0, 233
14, 0, 377
15, 0, 610
16, 0, 987
17, 0, 1597
18, 0, 2584
19, 0, 4181
20, 0, 6765
21, 0, 10946
22, 0, 17711
23, 0, 28657
24, 0, 46368
25, 0, 75025
26, 0, 121393
27, 0, 196418
28, 0, 317811
29, 0, 514229
30, 0, 832040
31, 0, 1346269
32, 0, 2178309
33, 0, 3524578
34, 0, 5702887
35, 0, 9227465
36, 0, 14930352
37, 0, 24157817
38, 0, 39088169
39, 0, 63245986
40, 0, 102334155
41, 0, 165580141
42, 1, 267914296
43, 2, 433494437
44, 3, 701408733
45, 5, 1134903170
46, 8, 1836311903
47, 14, 2971215073
48, 22, 4807526976
49, 34, 7778742049
50, 58, 12586269025

【问题讨论】:

  • 因为递归斐波那契效率低下。做一些分析,比如fib(8),看看到底是什么原因。

标签: java recursion fibonacci


【解决方案1】:

这是一个经典的递归问题,动态编程可以解决这个问题。

实际发生的情况是您的代码执行了一些已经完成的重新计算,因此导致额外的机器周期和较长的处理时间。

上图描述了您的程序如何计算 5th Fibonacci Number 。 我们可以看到函数 f(3) 被调用了 2 次 fib(2) 3 次 fib(1) 5 次,而不是计算一次。

解决方案

如果我们存储了 f(3),f(2),f(1) 的值,那么我们可以重用旧存储的值,而不是再次计算它。 从这里您可以进行研究并可以阅读here

【讨论】:

    【解决方案2】:

    这主要是因为这种生成斐波那契数的实现效率极低。

    这个特定的算法呈指数增长而不是线性增长,因为斐波那契的每次调用都会分支成另外两个调用并继续在这个轨道上。因此,增加 N 的大小会大大增加完成所需的时间。

    更好的方法是跟踪先前的值以计算下一个值。

    long fibbonaci(int n) {
        long c = 0, k = 1;
        for (int i = 0; i < n; i++) {
            k += c;
            c = k - c;
        }
        return c;
    }
    

    【讨论】:

      【解决方案3】:

      这是一个典型的例子,你应该使用记忆。您需要记住已经计算出的数字。简而言之,您需要维护一个数据结构,在这种情况下可以是一个数组,它将保存该索引的斐波那契数。

      示例数组如下所示(从索引 0 开始)

      0,1,1,2,3,5,8,13

      因此,每当您需要计算斐波那契数时,首先检查数组是否已经计算了斐波那契数。如果是,则从数组中返回它。如果不是,则计算它并存储在数组中。

      【讨论】:

      • 您在每个步骤中花费多少时间搜索数组?您真的需要记住所有前面的数字还是只记住最后两个?
      • 访问数组元素所花费的时间是 O(1),因为它的索引将被使用,并且不会在整个数组中搜索。只保留最后 2 个将起作用,您将节省空间。
      • 啊,对……我想象你在数组中寻找13(这当然是愚蠢的)而不是array[n-1]
      猜你喜欢
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      • 1970-01-01
      • 2010-12-03
      • 1970-01-01
      • 2017-05-08
      • 1970-01-01
      相关资源
      最近更新 更多