如何使这个斐波纳契功能更快?

时间:2017-05-16 12:58:57

标签: haskell functional-programming fibonacci

这个功能可以找到第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中尝试了一些比较闪亮的东西。

如果不比工作代码更受欢迎,欢迎提示!

3 个答案:

答案 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 33,就没有必要再次计算它。当然,在这个非常小的例子中,这不是一个问题:计算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)

表示您的ab初始值。

答案 2 :(得分:0)

您可以使用矩阵乘法来计算O(logN)时间内的斐波纳契数。类似的问题是here