改进C ++ Fibonacci系列

时间:2015-04-26 19:04:43

标签: c++ dynamic-programming fibonacci

我知道:

int fib(int n) 
{
    if (n == 0 || n == 1)
        return 1;

    return fib(n − 1)+ fib(n − 2);
}

当n = 5时,fib(5)评估为:

fib(5)
fib(4) + fib(3)
(fib(3) + fib(2)) + (fib(2) + fib(1))
((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
(((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))

请注意,每个基本元素被多次使用,有没有办法使用map来存储前一个值,只需要做fib(n - 1)+ fib(n - 2)吗?

4 个答案:

答案 0 :(得分:7)

在C ++中,可以为您节省时间的两种解决方案是动态编程方法和记忆方法。

动态编程

我们只是从[1..n]构建一个表并填入:

int fib(int n) 
{
    if (n <= 1)
        return n;

    std::vector<int> table(n + 1);
    table[0] = table[1] = 1;
    for (int i = 2; i <= n; ++i) {
        table[i] = table[i-1] + table[i-2];
    }    
    return table.back();
}

记忆化

在这里,我们正常实施fib,但省略了中间步骤:

int fib(int n) {
    static std::vector<int> table; // our cache
    if (n <= 1) {
        return n;
    }
    else if (n >= table.size()) {
        table.resize(n+1);
    }

    if (table[n] == 0) {
        // only recalc if we don't have a value
        table[n] = fib(n-1) + fib(n-2);
    }
    return table[n];
}

更加标准的记忆方法将涉及输入的哈希表 - 但在这种情况下,因为我们知道要计算fib(n),我们还需要fib(1)fib(n-1)vector会更有效率。或者我们呢?

次线性

我们实际上无需计算fib(1)fib(n-1)来获取fib(n)。我们可以直接做到:

int fib(int n) {
    const double sqrt5 = std::sqrt(5);
    const double phi = (1 + sqrt5) / 2;
    return (int)(std::pow(phi, n+1) / sqrt5 + 0.5);
}

因为数学很酷。

答案 1 :(得分:2)

是。原始递归解决方案需要很多时间。这样做的原因是,对于计算的每个数字,它需要多次计算所有先前的数字。

使甚至更糟糕的是,您在列表中计算的每个斐波那契数字都不会使用您之前知道的数字来加速计算 - 您计算每个数字“从零开始。“

有一些方法可以加快速度:

1。从“自下而上”

创建一个列表

最简单的方法是创建一个斐波纳契数列表,最多可达到您想要的数字。如果你这样做,你可以“自下而上”构建或者说,你可以重复使用以前的数字来创建下一个数字。如果您有斐波纳契数字[0, 1, 1, 2, 3]的列表,则可以使用该列表中的最后两个数字来创建下一个数字。

这种方法看起来像这样:

>>> def fib_to(n):
...     fibs = [0, 1]
...     for i in range(2, n+1):
...         fibs.append(fibs[-1] + fibs[-2])
...     return fibs
...

然后你可以通过

获得前20个斐波那契数字
>>> fib_to(20)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]

或者你可以通过

从前40个列表中获得第17个斐波那契数字
>>> fib_to(40)[17]
1597

2。记忆(相对先进的技术)

另一种使其更快的替代方案存在,但它也有点复杂。由于您的问题是您重新计算已经计算的值,因此您可以选择将已经计算的值保存在dict中,并在重新计算之前尝试从中获取它们。这称为 memoization 。它可能看起来像这样:

>>> def fib(n, computed = {0: 0, 1: 1}):
...     if n not in computed:
...         computed[n] = fib(n-1, computed) + fib(n-2, computed)
...     return computed[n]

这使您可以轻松计算大的斐波那契数字:

>>> fib(400)
176023680645013966468226945392411250770384383304492191886725992896575345044216019675

这实际上是一种常见的技术,Python 3包含一个装饰器来为您完成此操作。我告诉你,自动记忆!

import functools

@functools.lru_cache(None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

这与上一个函数几乎完全相同,但computed装饰器处理了所有lru_cache内容。

3。算起来(一个天真的迭代解决方案)

Mitch建议的第三种方法是在不保存列表中的中间值的情况下进行计数。你可以想象做

>>> def fib(n):
...     a, b = 0, 1
...     for _ in range(n):
...         a, b = b, a+b
...     return a

如果您的目标是创建斐波纳契数字的列表,我不推荐这两种方法。 fib_to(100)将比[fib(n) for n in range(101)]更快 ,因为对于后者,您仍然会遇到从头开始计算列表中每个数字的问题。

查看此内容以了解不同的算法: http://www.nayuki.io/page/fast-fibonacci-algorithms

致谢:kqr

答案 2 :(得分:0)

当然可以!

python中非常基本的例子:

@return

答案 3 :(得分:0)

采用动态编程的高效Fibonacci系列解决方案。

#include<iostream>
using namespace std;
int main()
{
    int n;
    cin>>n;
    int fib[n];
    fib[0]=fib[1]=1;
    for(int i = 2;i<n;i++)
    {
        fib[i]=fib[i-1]+fib[i-2];
    }
    for(int i = 0;i<n;i++)
    {
        cout<<fib[i]<<" ";
    }
    return 0;
}
/*input and output
5
1 1 2 3 5
Process returned 0 (0x0)   execution time : 2.557 s
Press any key to continue.*/