groovy斐波那契蹦床和备忘录

时间:2016-03-21 18:42:03

标签: groovy

我正在尝试评估10000000的斐波纳契序列。

使用基本的trampoline,它看起来像这样

def rFibonacchi
rFibonacchi = { 
    BigInteger n, prev = 0, next = 1 ->
        (n < 2) ? prev : rFibonacchi.trampoline(n - 1, prev, next + prev)
}.trampoline()

但是使用trampoline & memoize组合我不断得到OutOfMemoryError

def tFibonacchi, mFibonacchi

mFibonacchi = { BigInteger n, prev, next ->
    n < 2 ? prev : tFibonacchi.trampoline(n - 1, next, prev + next)
}.memoize()

tFibonacchi = { BigInteger n, prev = 0, next = 1 ->
    mFibonacchi(n, next, prev)
}.trampoline()

tFibonacchi(10000000); // GC overhead limit exceed

这是我算法的问题吗?

1 个答案:

答案 0 :(得分:2)

使用memoization,您的算法不会获得任何奖励。引用the groovy docs on memoization:

  

Memoization允许缓存闭包调用的结果。有趣的是,函数(闭包)完成的计算速度很慢,但是你知道这个函数经常会用相同的参数调用。一个典型的例子是Fibonacci套件。一个天真的实现可能如下所示:

def fib
fib = { long n -> n<2?n:fib(n-1)+fib(n-2) }
assert fib(15) == 610 // slow!
     

这是一个天真的实现,因为'fib'通常使用相同的参数递归调用,从而产生指数算法:

     
      
  • 计算fib(15)需要fib(14)fib(13)

  • 的结果   
  • 计算fib(14)需要fib(13)和fib(12)

  • 的结果   
     

由于调用是递归的,您已经可以看到我们将一次又一次地计算相同的值,尽管它们可以被缓存。通过使用memoize:

缓存调用结果,可以“修复”这种天真的实现
fib = { long n -> n<2?n:fib(n-1)+fib(n-2) }.memoize()
assert fib(25) == 75025 // fast!
     

缓存使用参数的实际值。

您正在使用改进的Fibonacci算法来实现上述算法。你的迭代更多,它永远不会用所有相同的参数调用mFibonacchi两次。这会导致groovy缓存每个调用的结果,但实际上从不使用此缓存,这会导致内存溢出。记忆实际上是一个问题。

您的算法等同于:

BigInteger fibonacchi(BigInteger n) {
    BigInteger prev = 0, next = 1
    for (; n > 2; n--) {
        BigInteger temp = prev
        prev = next
        next = prev + temp
    }
    return prev
}