我正在尝试评估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
这是我算法的问题吗?
答案 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
}