这个功能可以找到第n个斐波那契。
.as-console-wrapper { max-height: 100% !important; top: 0; }
但它很慢。如果我做a = 1
b = 2
fibonacci :: Int -> Int
fibonacci 1 = a
fibonacci 2 = b
fibonacci n = (fibonacci (n-1)) + (fibonacci (n-2))
它会随着数字的增加而减慢。我猜这是开销,因为正在使用多少堆栈和大量的计算 - 将每一个计算到map fibonacci [1..]
和a
,而不是仅仅将最后两个加在一起。
我如何改进它以便更快,但仍然使用函数式编程风格? (我是一个明确的haskell和FP新手!) 我在Python中尝试了一些比较闪亮的东西。
如果不比工作代码更受欢迎,欢迎提示!
答案 0 :(得分:6)
问题是,这将导致戏剧性的分支。假设您致电fibonacci 5
,那么这将导致以下调用树:
fibonacci 5
fibonacci 4
fibonacci 3
fibonacci 2
fibonacci 1
fibonacci 2
fibonacci 3
fibonacci 2
fibonacci 1
如您所见,fibonacci 3
被调用两次,fibonacci 2
被调用三次,fibonacci 1
被调用两次(在这个非常小的例子中)。存在大量重叠:您使用相同的参数多次调用相同的函数。这当然是低效的:一旦你知道fibonacci 3
是3
,就没有必要再次计算它。当然,在这个非常小的例子中,这不是一个问题:计算fibonacci 3
只需几纳秒。但是如果你必须多次计算fibonacci 100
,这将产生巨大的影响。 冗余调用的数量也呈指数级增长(因此这不是一个只会对边距产生一些影响的小问题)。
你可以做的是使用 accumulator(s):递归传递的变量并相应地更新。对于斐波那契,有两个这样的变量, f(n-2)和 f(n-1)。然后你每次计算这两者之和和 shift 所以:
fibonacci :: Int -> Int
fibonacci 1 = a
fibonacci 2 = b
fibonacci n = fib' (n-2) a b
fib' 0 x y = x+y
fib' i x y = fib' (i-1) y (x+y)
在这种情况下,调用树将如下所示:
fibonacci 5
fib' 3 1 2
fib' 2 2 3
fib' 1 3 5
fib' 0 5 8
这导致五次调用(针对原始代码的九次调用)。当然,调用的次数并不是性能的保证,因为有些方法可以做更多工作,但是我们的方法使用 n 进行线性,而原始方法按比例缩放 n 。因此,即使原始方法的速度提高了数千倍,最终调用次数的差异将是巨大的,它将会超越它。
答案 1 :(得分:1)
您可以使用memoization将其打包以消除冗余计算
fibonacci = (map fibm [0..] !!)
where fibm 1 = a
fibm 2 = b
fibm n = fibonacci (n-1) + fibonacci (n-2)
表示您的a
和b
初始值。
答案 2 :(得分:0)
您可以使用矩阵乘法来计算O(logN)时间内的斐波纳契数。类似的问题是here。