简体   繁体   English

C++中的斐波那契记忆算法

[英]Fibonacci memoization algorithm in C++

I'm struggling a bit with dynamic programming.我在动态编程方面有点挣扎。 To be more specific, implementing an algorithm for finding Fibonacci numbers of n.更具体地说,实现一种用于查找 n 的斐波那契数的算法。

I have a naive algorithm that works:我有一个简单的算法:

int fib(int n) {
    if(n <= 1)
        return n;
    return fib(n-1) + fib(n-2);
}

But when i try to do it with memoization the function always returns 0:但是当我尝试通过记忆来实现时,该函数总是返回 0:

int fib_mem(int n) {
    if(lookup_table[n] == NIL) {
        if(n <= 1)
            lookup_table[n] = n;
        else
            lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
    }
    return lookup_table[n];
}

I've defined the lookup_table and initially stored NIL in all elements.我已经定义了lookup_table 并且最初在所有元素中存储了NIL。

Any ideas what could be wrong?任何想法可能是错误的?

Here's the whole program as requested:这是要求的整个程序:

#include <iostream>

#define NIL -1
#define MAX 100

long int lookup_table[MAX];

using namespace std;

int fib(int n);
int fib_mem(int n);

void initialize() {
    for(int i = 0; i < MAX; i++) {
        lookup_table[i] == NIL;
    }
}

int main() {
    int n;
    long int fibonnaci, fibonacci_mem;
    cin >> n;

    // naive solution
    fibonnaci = fib(n);

    // memoized solution
    initialize();
    fibonacci_mem = fib_mem(n);

    cout << fibonnaci << endl << fibonacci_mem << endl;

    return 0;
}

int fib(int n) {
    if(n <= 1)
        return n;
    return fib(n-1) + fib(n-2);
}

int fib_mem(int n) {
    if(lookup_table[n] == NIL) {
        if(n <= 1)
            lookup_table[n] = n;
        else
            lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
    }
    return lookup_table[n];
}

I tend to find the easiest way to write memoization by mixing the naive implementation with the memoization:我倾向于通过将幼稚的实现与记忆化混合来找到编写记忆化的最简单方法:

int fib_mem(int n);
int fib(int n) { return n <= 1 ? n : fib_mem(n-1) + fib_mem(n-2); }
int fib_mem(int n)
{
    if (lookup_table[n] == NIL) {
        lookup_table[n] = fib(n);
    }
    return lookup_table[n];
}
#include <iostream>
#define N 100

using namespace std;

const int NIL = -1;
int lookup_table[N];

void init()
{
    for(int i=0; i<N; i++)
        lookup_table[i] = NIL;
}
int fib_mem(int n) {
    if(lookup_table[n] == NIL) {
        if(n <= 1)
            lookup_table[n] = n;
        else
            lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
    }
    return lookup_table[n];
}
int main()
{
    init();
    cout<<fib_mem(5);
    cout<<fib_mem(7);
}

Using the exactly same function, and this is working fine.使用完全相同的功能,这工作正常。

You have done something wrong in initialisation of lookup_table .您在lookup_table初始化中lookup_table了什么。

Since the issue is initialization, the C++ standard library allows you to initialize sequences without having to write for loops and thus will prevent you from making mistakes such as using == instead of = .由于问题是初始化,C++ 标准库允许您在无需编写for循环的情况下初始化序列,从而防止您犯错误,例如使用==而不是=

The std::fill_n function does this: std::fill_n函数执行以下操作:

#include <algorithm>
//...

void initialize()
{
   std::fill_n(lookup_table, MAX, NIL);
}

There's a mistake in your initialize() function:您的initialize()函数中存在错误:

void initialize() {
    for(int i = 0; i < MAX; i++) {
        lookup_table[i] == NIL; // <- mistake
    }
}

In the line pointed you compare lookup_table[i] and NIL (and don't use the result) instead of assigning NIL to lookup_table[i] .在指出的行中,您比较lookup_table[i]NIL (并且不使用结果),而不是将NIL分配给lookup_table[i]

For assignment, you should use = instead of == .对于赋值,您应该使用=而不是==

Also, in such situations the most right thing to do is compilation of your program with all warnings enabled.此外,在这种情况下,最正确的做法是在启用所有警告的情况下编译您的程序。 For example, MS VC++ shows the following warning:例如,MS VC++ 显示以下警告:

warning C4553: '==': operator has no effect; did you intend '='?

The error is on initialize function (you've used comparison operator '==' where you want a attribution operator '=').错误出在初始化函数上(您在需要归因运算符“=”的地方使用了比较运算符“==”)。 But, on semantics, you don't need initialize look_table with -1 (NIL) because Fibonacci results never will be 0 (zero);但是,在语义上,您不需要使用 -1 (NIL) 初始化 look_table,因为斐波那契结果永远不会为 0(零); so, you can initialize it all with zero.所以,你可以用零初始化它。 Look below the final solution:看看下面的最终解决方案:

#include <iostream>

#define NIL 0
#define MAX 1000

long int lookup_table[MAX] = {};

using namespace std;

long int fib(int n) {
    if(n <= 1)
        return n;
    return fib(n-1) + fib(n-2);
}

long int fib_mem(int n) {
    assert(n < MAX);
    if(lookup_table[n] == NIL) {
        if(n <= 1)
            lookup_table[n] = n;
        else
            lookup_table[n] = fib_mem(n-1) + fib_mem(n-2);
    }
    return lookup_table[n];
}

int main() {
    int n;
    long int fibonnaci, fibonacci_mem;
    cout << " n = "; cin >> n;

    // naive solution
    fibonnaci = fib(n);

    // memoized solution
    // initialize();
    fibonacci_mem = fib_mem(n);

    cout << fibonnaci << endl << fibonacci_mem << endl;

    return 0;
}

Interesting concept.有趣的概念。 Speeding up by memoization.通过记忆加速。

There is a different concept.有一个不同的概念。 You could call it compile time memoization.你可以称之为编译时记忆。 But in reality it is a compile time pre calculation of all Fibonacci numbers that fit into a 64 bit value.但实际上,它是对适合 64 位值的所有斐波那契数的编译时预计算。

One important property of the Fibonacci series is that the values grow strongly exponential.斐波那契数列的一个重要属性是值呈指数级增长。 So, all existing build in integer data types will overflow rather quick.因此,所有现有的整数数据类型构建都会很快溢出。

With Binet's formula you can calculate that the 93rd Fibonacci number is the last that will fit in a 64bit unsigned value.使用比奈公式,您可以计算出第 93 个斐波那契数是最后一个适合 64 位无符号值的数。

And calculating 93 values during compilation is a really simple task.在编译期间计算 93 个值是一项非常简单的任务。

We will first define the default approach for calculation a Fibonacci number as a constexpr function:我们首先将计算斐波那契数的默认方法定义为constexpr函数:

// Constexpr function to calculate the nth Fibonacci number
constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
    // Initialize first two even numbers 
    unsigned long long f1{ 0 }, f2{ 1 };

    // calculating Fibonacci value 
    while (index--) {
        // get next value of Fibonacci sequence 
        unsigned long long f3 = f2 + f1;
        // Move to next number
        f1 = f2;
        f2 = f3;
    }
    return f2;
}

With that, Fibonacci numbers can easily be calculated at runtime.这样,可以在运行时轻松计算斐波那契数。 Then, we fill a std::array with all Fibonacci numbers.然后,我们用所有斐波那契数字填充std::array We use also a constexpr and make it a template with a variadic parameter pack.我们还使用constexpr并使其成为带有可变参数包的模板。

We use std::integer_sequence to create a Fibonacci number for indices 0,1,2,3,4,5, ....我们使用std::integer_sequence为索引 0,1,2,3,4,5, .... 创建一个斐波那契数。

That is straigtforward and not complicated:这很直接,并不复杂:

template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
    return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};

This function will be fed with an integer sequence 0,1,2,3,4,... and return a std::array<unsigned long long, ...> with the corresponding Fibonacci numbers.这个函数将输入一个整数序列 0,1,2,3,4,... 并返回一个std::array<unsigned long long, ...>与相应的斐波那契数列。

We know that we can store maximum 93 values.我们知道我们最多可以存储 93 个值。 And therefore we make a next function, that will call the above with the integer sequence 1,2,3,4,...,92,93, like so:因此我们创建了一个 next 函数,它将使用整数序列 1,2,3,4,...,92,93 调用上面的函数,如下所示:

constexpr auto generateArray() noexcept {
    return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
}

And now, finally,而现在,终于,

constexpr auto FIB = generateArray();

will give us a compile-time std::array<unsigned long long, 93> with the name FIB containing all Fibonacci numbers.将给我们一个编译时std::array<unsigned long long, 93>名称为 FIB 的包含所有斐波那契数。 And if we need the i'th Fibonacci number, then we can simply write FIB[i] .如果我们需要第 i 个斐波那契数,那么我们可以简单地写FIB[i] There will be no calculation at runtime.运行时不会有计算。

I do not think that there is a faster way to calculate the n'th Fibonacci number.我认为没有更快的方法来计算第 n 个斐波那契数。

Please see the complete program below:请参阅下面的完整程序:

#include <iostream>
#include <array>
#include <utility>
// ----------------------------------------------------------------------
// All the following will be done during compile time

// Constexpr function to calculate the nth Fibonacci number
constexpr unsigned long long getFibonacciNumber(size_t index) {
    // Initialize first two even numbers 
    unsigned long long f1{ 0 }, f2{ 1 };

    // calculating Fibonacci value 
    while (index--) {
        // get next value of Fibonacci sequence 
        unsigned long long f3 = f2 + f1;
        // Move to next number
        f1 = f2;
        f2 = f3;
    }
    return f2;
}
// We will automatically build an array of Fibonacci numberscompile time
// Generate a std::array with n elements 
template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
    return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};

// Max index for Fibonaccis that for in an 64bit unsigned value (Binets formula)
constexpr size_t MaxIndexFor64BitValue = 93;

// Generate the required number of elements
constexpr auto generateArray()noexcept {
    return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
}

// This is an constexpr array of all Fibonacci numbers
constexpr auto FIB = generateArray();
// ----------------------------------------------------------------------

// Test
int main() {

    // Print all possible Fibonacci numbers
    for (size_t i{}; i < MaxIndexFor64BitValue; ++i)

        std::cout << i << "\t--> " << FIB[i] << '\n';

    return 0;
}

Developed and tested with Microsoft Visual Studio Community 2019, Version 16.8.2.使用 Microsoft Visual Studio Community 2019 版本 16.8.2 进行开发和测试。

Additionally compiled and tested with clang11.0 and gcc10.2另外使用 clang11.0 和 gcc10.2 编译和测试

Language: C++17语言:C++17

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM