【问题标题】:Improve C++ Fibonacci series改进 C++ 斐波那契数列
【发布时间】:2015-07-05 02:29:58
【问题描述】:

我知道:

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)?

【问题讨论】:

  • 是的,它叫memoization
  • 可以使用迭代法
  • 这实际上是动态编程和记忆化的一个常见示例。只需快速进行 Google 搜索 - 您应该会找到一些解释它的资源。这是一个:geeksforgeeks.org/program-for-nth-fibonacci-number
  • 顺便说一句:你肯定知道你可以通过比内公式得到斐波那契数列的第 n 个元素...

标签: c++ dynamic-programming fibonacci


【解决方案1】:

在 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);
}

因为数学很酷。

【讨论】:

  • 不错的答案。作为改进建议:您可以在显式函数static 中设置常量...否则可能是每次都重新计算它们。
  • 数学真不错!我认为你应该能够做这样的事情,但不知道怎么做。谢谢!需要更多的支持:)
  • 记忆和动态规划有什么不同?动态编程基本上使用内存(记忆)。唯一的区别是从上到下和从下到上?
  • @SunggukLim 不是记忆化,而是记忆化。记忆是关于缓存答案——它们不必是子问题的答案。例如,您可以记住读取文件,如果您必须在许多不同的地方读取同一个文件,并且每次都不会写入。 DP 是关于有效地递归解决问题。
【解决方案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 中,并在重新计算之前尝试从中获取它们。这称为记忆化。它可能看起来像这样:

>>> 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)]很多,因为使用后者,您仍然会遇到从头开始计算列表中每个数字的问题。

查看不同的算法: https://www.nayuki.io/page/fast-fibonacci-algorithms

学分:kqr

【讨论】:

  • 虽然这是一个很好的答案,但请注意该问题被标记为 C++ - 而不是 Python。最好用 C++ 编写示例。
  • OP 自己用 Python 编写了代码。我认为他很困惑。
  • @Richard3 很抱歉造成混乱。感谢您的回答:)
【解决方案3】:

当然可以!

python中非常基本的例子:

cache = {}

def fib(n):
    if n <= 1:
        return n
    if n not in cache:
        cache[n] = fib(n-1) + fib(n-2)
    return cache[n]

【讨论】:

  • 请注意,该问题已标记为 C++ - 而不是 Python
  • @monoceres 感谢您的回复:)
【解决方案4】:

具有动态规划的高效斐波那契数列解决方案。

#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.*/

【讨论】:

    猜你喜欢
    • 2015-06-05
    • 2021-11-30
    • 2019-04-06
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多