斐波那契的表现

时间:2010-11-09 03:07:28

标签: performance wolfram-mathematica fibonacci

f[0] = 0;
f[1] = 1;
f[x_] := f[x-1] + f[x-2]

这个功能在Mathematica中运行缓慢,我需要提高速度。我必须使用函数式编程和递归。我不确定为什么这么慢,甚至一点点想法如何改善这都会有所帮助。

3 个答案:

答案 0 :(得分:9)

编写更快的递归函数的一个好方法是让它记住以前的值。当然,这确实以内存为代价,但在这种情况下它可以提供帮助。要计算f[x],您需要计算f[x-1]f[x-2] - 然后计算f[x-1],再次计算f[x-2];你最终会重新计算很多价值。 (原谅我的不精确!)

要随时存储内容,您可以使用此成语:

f[x_] := ( f[x] = (* calculation of f[x] goes here *)  )

编辑:我在这台机器上没有mathematica,但我很确定没办法这会计算出错误的值。

f[0] = 0;
f[1] = 1;
f[x_] := ( f[x] = f[x-1] + f[x-2] );

f[256]

就像我在下面的评论中所说,如果您对f有其他定义,则可能需要先使用Clear[f]将其删除。

感谢rcollyer:小心$RecursionLimit!它默认为256.(当然,这是有充分理由的。真正的深度递归通常是一个坏主意。)

答案 1 :(得分:3)

Jefromi是对的。查看维基百科上的Memoization。他们使用阶乘的例子以及如何通过记忆来加速它。

答案 2 :(得分:1)

Memoization 编写更快的递归函数的好方法。但是,在这种情况下,有一个递归替代方案,其运行速度比原始函数快得多,无需记忆。

关键观察是看原始定义执行大量冗余计算。考虑如果我们计算fib[4]会发生什么:

fib[4] = fib[3] + fib[2]
  fib[3] = fib[2] + fib[1]
    fib[2] = fib[1] + fib[0]
      fib[1] = 1
      fib[0] = 1
    ∴ fib[2] = 1 + 1 = 2
    fib[1] = 1
  ∴ fib[3] = 2 + 1 = 3
  fib[2] = fib[1] + fib[0]
    fib[1] = 1
    fib[0] = 1
  ∴ fib[2] = 1 + 1 = 2
∴ fib[4] = 2 + 1 = 3

在此过程中,fib[2]fib[0]每次计算两次,fib[1]计算三次。对于更大的计算,浪费急剧增长 - 实际上呈指数级增长。

如果要手动计算相同的斐波纳契数,可以采用以下方法:

0: given   0
1: given   1
2: 0 + 1 = 1
3: 1 + 1 = 2
4: 1 + 2 = 3

没有多余的计算。在任何给定的点上,只需要考虑前两个结果。后一种方法可以递归地表达:

fib2[0] = 0;
fib2[n_] :=
  Module[{f},
    f[n, p1_, _] := p1;
    f[x_, p1_, p2_] := f[x + 1, p1 + p2, p1];
    f[1, 1, 0]
  ]

Block[{$IterationLimit = Infinity}, fib2[100000]]

毫无疑问,这种形式并不像原版那么容易阅读。另一方面,原始函数在我的机器上花了35秒来计算fib[35],而修改后的函数的运行时间被报告为零。此外,修订后的函数在0.281秒内计算fib2[100000],无需任何额外的存储器存储。 fib[100000]完全超出了原始函数的范围,memoized version崩溃了我的Mathematica 7.01内核 - 可能会有太多记忆规则?

请注意,默认情况下,Mathematica将迭代一个函数不超过4096次。要提高该限制,您必须为$IterationLimit分配更高的值,如上例所示。

当然,在Mathematica中有很多非递归方法来计算Fibonacci数,包括内置的Fibonacci函数。但这不是这项工作的重点。

尾部呼叫优化?

始终需要使用tail calls来表达递归函数。这允许通过简单的迭代来执行递归,而没有在堆栈上保留中间结果的开销。 fib2是尾递归的。一些语言,如Scheme,要求尾部调用优化。其他语言(如Java)可以支持,但不支持(或不支持,如Python的情况)。

在Mathematica的情况下,不清楚尾部调用优化的执行程度。有关这一点的进一步讨论,请参阅another SO question