快速编译时评估的秘诀在于,只有极少数的斐波那契数可以适合当今最大的数据类型 unsigned long long。
斐波那契数计算的基本信息是:根本不需要在运行时进行任何计算!一切都可以而且应该在编译时完成。然后,可以使用简单的查找机制。这将永远是最有效和最快的解决方案。
因此,使用比内特的公式,我们可以计算出只有 很少 斐波那契数可以适合 C++ unsigned long long 数据类型,该数据类型现在通常在 2021 年为 64 位,并且是“最大”的可用数据类型。 93 号环岛。现在这个数字真的很低。
借助现代 C++ 17(及更高版本)功能,我们可以在编译时轻松创建 64 位数据类型的所有斐波那契数的 std::array。因为如上所述,只有 93 个数字。
因此,我们将只为查找数组花费 93*8= 744 BYTE 的非运行时内存。这实在是微不足道。而且,编译器可以快速获取这些值。
编译时间计算后,我们可以很容易的通过写FIB[n]得到斐波那契数n。详细解释见下文。
而且,如果我们想知道,如果一个数字是斐波那契,那么我们使用std::binary_search 来查找值。所以,这个函数将是例如:
bool isFib(const unsigned long long numberToBeChecked) {
return std::binary_search(FIB.begin(), FIB.end(), numberToBeChecked);
}
FIB(当然任何其他可能的名称)是一个编译时间,constexpr std::array。那么,如何构建这个数组呢?
我们首先将计算斐波那契数的默认方法定义为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;
}
这样,斐波那契数可以在编译时轻松计算为constexpr values。然后,我们用所有斐波那契数填充std::array。我们还使用了constexpr 并使其成为带有可变参数包的模板。
我们使用std::integer_sequence 为索引0、1、2、3、4、5、...创建一个斐波那契数。
这很简单,并不复杂:
template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};
这个函数将输入一个整数序列 0,1,2,3,4,... 并返回一个 std::array<unsigned long long, ...> 和相应的斐波那契数。
我们知道我们最多可以存储 93 个值。因此我们创建了一个 next 函数,它将使用整数序列 1,2,3,4,...,92,93 调用上述函数,如下所示:
constexpr auto generateArray() noexcept {
return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
}
现在,终于,
constexpr auto FIB = generateArray();
将给我们一个编译时std::array<unsigned long long, 93>,名称为 FIB,包含所有斐波那契数。如果我们需要第 i 个斐波那契数,那么我们可以简单地写 FIB[i]。运行时不会进行计算。
整个示例程序如下所示:
#include <iostream>
#include <array>
#include <utility>
#include <algorithm>
#include <iomanip>
// ----------------------------------------------------------------------
// 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) 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;
}
// We will automatically build an array of Fibonacci numbers at compile 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 an 64bit unsigned value (Binet's 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();
// All the above was compile time
// ----------------------------------------------------------------------
// Check, if a number belongs to the Fibonacci series
bool isFib(const unsigned long long numberToBeChecked) {
return std::binary_search(FIB.begin(), FIB.end(), numberToBeChecked);
}
// Test
int main() {
const unsigned long long testValue{ 498454011879264ull };
std::cout << std::boolalpha << "Does '" <<testValue << "' belong to Fibonacci series? --> " << isFib(testValue) << "\n\n";
for (size_t i{}; i < 10u; ++i)
std::cout << i << '\t' << FIB[i] << '\n';
return 0;
}
使用 Microsoft Visual Studio Community 2019 版本 16.8.2 开发和测试
使用 gcc 10.2 和 clang 11.0.1 进行了额外测试
语言:C++ 17