【问题标题】:Why it takes a long time to compute Fibonacci number?为什么计算斐波那契数需要很长时间?
【发布时间】:2014-05-13 05:59:52
【问题描述】:

我几天前开始学习 Ocaml。我试图制作一个斐波那契数的程序:

  let rec fib a=
      if a=1||a=2 then 1 else fib(a-1)+fib(a-2);;

这段代码不是最优的,因为我不知道如何处理异常情况。但是现在,如果我尝试计算 fib 50 或 fib 100,那么计算机需要很长时间来评估。我想知道为什么,因为 Ocaml 应该非常快,而将数字相加显然是一个线性时间任务。如果我将此代码粘贴到“Try Ocaml”(http://try.ocamlpro.com/)中,那么当我执行 fib 50 时,整个网站都会冻结。

如果问题的级别太低,请见谅。

【问题讨论】:

  • 我认为这是一个算法问题而不是 ocaml 问题
  • “整个网站冻结”:大声笑:D

标签: ocaml


【解决方案1】:

因为这是错误递归使用的海报示例。应该禁止将其用作递归优雅的示例,因为它确实完全不优雅 w.r.t.机器。

每次调用fib,您都会获得另外两个fib 调用:

depth | call tree
------+------------------------------------------------------------------------
1     | fib-----+
      | |        \
2     | fib      fib
      | |   \    |   \
3     | fib  fib fib  fib
      | ....
4     | fib fib  fib  fib fib fib  fib  fib
      | ....
5     | fib fib  fib  fib fib fib  fib  fib 
      | fib fib  fib  fib fib fib  fib  fib
      | ....
6     | fib fib  fib  fib fib fib  fib  fib  
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib  
      | fib fib  fib  fib fib fib  fib  fib
      | ....
7     | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | ....
8     | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib          
      | ....
9     | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib
      | fib fib  fib  fib fib fib  fib  fib

...对于深度 50,...

 [562,949,950,000,000] × fib

...全部并行存在,大约有 5.6 亿个数据包,每百万个 fib

您所拥有的不是 OCaml 问题,而是计算/算法问题。对于不需要“简单”递归的情况,斐波那契数是一个很好的例子。看似简单优雅,实则否定现实。

因此,将其实现为循环,您的问题就会消失。

复杂性

这是一个指出计算复杂性的好问题 (Big O Notation)。您的实现具有指数复杂度 O(2n),即运行算法所需的时间随时间呈指数增长。到输入。求和循环将在 O(n) 中运行,即解决问题所需的时间随时间线性增长。到输入。

另见Algorithm function for fibonacci series

【讨论】:

  • 换句话说,您的代码具有指数级的复杂性。每种语言都会很慢。 (令我印象深刻的是,phresnel 可以在短短几分钟内绘制出如此出色的画面。)但是,我不同意循环与递归的说法。如果你递归地写tail,它们是一样的。
  • @JeffreyScofield:好点。我试图稍微完善一下声明。
  • 这个exponential complexity O(n²)应该是exponential complexity O(2^n)
【解决方案2】:

phresnel 是绝对正确的,斐波那契函数的通常递归定义是一种非常糟糕的实际计算方法。很容易看出,每次您想将 1 加到结果中时,至少有一个递归调用。由于斐波那契函数是指数函数(大约 1.6 ^ n),因此您必须进行指数数量的递归调用。

这是一个相当快的递归斐波那契函数:

let fib n =
    let rec ifib i a b =
        if i = n then b
        else ifib (i + 1) b (a + b)
    in
    ifib 0 0 1

【讨论】:

  • 谢谢。当我自己思考完后,我会看看你的。
  • 虽然不错,但这应该是评论或扩展,因为它不能回答问题。
  • 好点,这确实是对您的回答的回应。我可以扩大一点,但它必须是明天。或者我可以删除并对您的答案发表评论。 (但是,在 cmets 中很难显示代码。)
  • 迟到 +1 :) 1.6^n 的好加法。
【解决方案3】:

您已经编写了臭名昭著的双递归斐波那契函数。因为每次调用都有两个递归调用,所以调用的总数随着递归级别的数量呈指数增长。

处理这个问题的常用方法是重写函数以不需要递归的方式进行计算(对于斐波那契,您可以从 1 开始向上,而不是向下到 a-1 和 @987654324 @,以便您在需要时已经拥有系列中较早的值),或者使用 memoization 缓存值,这样您就不需要多次计算相同的值。一旦 fib 23 被计算出来,任何后续的调用都只是从缓存中拉取它而不是再次计算它。

然而,斐波那契数列也有一个closed form(在该链接中搜索“封闭形式”),它与黄金比例 Φ 有关:

let round f =
    int_of_float (f +. 0.5)

let phi = (1. +. (sqrt 5.)) /. 2.

let fibf n =
    (phi ** n) /. (sqrt 5.)

let fib n =
    round (fibf n)

此实现直接计算斐波那契数列的值,无需迭代或递归。但是,它受到浮点精度的限制。例如,它不适合替代旨在继续进入 bignums 领域的迭代解决方案。

【讨论】:

  • +1:Wolfram 越来越棒了。我的语言显然不是。
【解决方案4】:

为了完整起见,这里是斐波那契的对数版本:

(* exponentiation by squaring *)
let rec pow e ( * ) x = function
| 0 -> e
| 1 -> x
| n ->
  let r = pow e ( * ) x (n/2) in
  if n mod 2 = 0 then r * r else r * r * x

(* matrix product *)
let ( ** ) a b = 
  assert
    (Array.length a = Array.length a.(0)
  && Array.length b = Array.length b.(0)
  && Array.length a = Array.length b);
  let n = Array.length a in
  let c = Array.make_matrix n n 0 in
  for i = 0 to n-1 do
    for j = 0 to n-1 do
      for k = 0 to n-1 do
        c.(i).(j) <- c.(i).(j) + a.(i).(k) * b.(k).(j)
      done;
    done;
  done;
  c

let id = [|[|1; 0|]; [|0; 1|]|]

(* companion matrix for fibonacci *)
let f0 = [|[|0; 1|]; [|1; 1|]|]

let fibo n = (pow id ( ** ) f0 n).(1).(1)

它通过获取companion matrix 并计算其能力来工作。

【讨论】:

  • +1:一方面,这不是一个答案。另一方面,这看起来是一个有价值的补充。
猜你喜欢
  • 2014-12-16
  • 2011-06-02
  • 1970-01-01
  • 2013-03-31
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 2016-08-04
  • 2020-01-18
相关资源
最近更新 更多