我正在寻找一种更快的方法来计算前N个斐波那契数。我已经知道使用记忆法解决这个问题的递归方法,以及从1 - N迭代的更简单的方法。但是,有没有一种数学方法我可以解决这个问题;或者另一种算法,甚至可以使这个过程稍微短一点?
我目前使用memoization和递归的实现速度相当快,但它不能满足我的需求。
#include<bits/stdc++.h>
using namespace std;
int fib(int n) {
int a = 0, b = 1, c, i;
if( n == 0)
return a;
for(i = 2; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return b;
}
int main()
{
int n = 9;
cout << fib(n);
}
4条答案
按热度按时间oxcyiej71#
不要否认查找表的强大功能。
一般来说,绝对数学比较快。然而,本页中介绍和讨论的一些数学解决方案依赖于浮点数学和
pow
函数。两者都可能是不精确的。Fib(46)开始接近32位有符号整数的极限。Fib(92)是有符号64位整数的极限。因此,简单地对答案进行硬编码是一件合理的事情。
我知道从计算的Angular 来看这不是你想要的答案。但是,如果你真的需要在现实世界中计算 fib(n),并且在不同的平台上使用合理的值不会导致溢出或浮点偏差,那么很难对此提出异议。
t5zmwmid2#
使用Binet's formula。
下面是C++中的一个基本实现:
Live Example
1.没有迭代或递归来获得
nth
Fibonacci数-它是直接计算的。1.对Binet公式使用memoization是可以的,因为您不会用
fib(n)
的相同值多次调用pow
。o8x7eapl3#
没有一个例子在编译时使用C++的全部功能来创建这样的表(https://godbolt.org/z/nMeqj6xvq):
9wbgstp74#
为了好玩,我用下面的代码做了一个快速的比较:
我得到的结果如下:
观察结果:
稍微推测一下,在迭代和表查找之间进行选择可能并不简单。我想这很大程度上取决于通话模式。表查找显然是O(1),但涉及的常数可能变化很大。如果你从该高速缓存中检索数据,它会非常快,但如果你必须从主内存中检索任何数据,那将是 * 相当 * 慢。如果你仔细地反复调用它以获取所有的fib(1)到fib(93),访问模式将是相当可预测的,所以在典型的CPU上,除了第一个缓存行之外的所有缓存行都将被预取该高速缓存中,所以总时间将是1个主内存读取+92个缓存读取。
在最新的CPU上读取主存储器大约需要42个时钟周期+51 ns。后续的缓存读取可能来自L1缓存,每次读取大约需要4个时钟。因此,对于这种在紧密循环中被调用的模式,我们总共得到约92*4个时钟+51ns。在(比如)4 GHz时,这大约是92+51ns或143ns。
但是,如果我们将对它的调用与许多其他代码交织在一起,那么基本上所有的读取都来自主内存而不是缓存,那么计算所有这些最终将达到93 *(42个时钟+51ns)。在这种情况下,在我们假设的4 GHz处理器上,它的最终时间约为5,700 ns。
相比之下,迭代算法每次迭代可能需要大约一个时钟(一次加法,两次移动,其可能被处理为ROB中的寄存器重命名)。为了计算fib(1)到fib(93),平均46.5次迭代,总共大约46.5 * 93 = 4325个时钟。在4 GHz时,这大约是1,100 ns。
因此,为了计算所有这些,迭代解的速度从大约10倍慢到大约5倍快。
旁注:根据您使用的编译器,它可能会或可能不会直接打印出每个算法的最后时间产生的持续时间。在这种情况下,更改:
duration_cast<nanoseconds>(stop - start)
到类似duration_cast<nanoseconds>(stop - start).count() << "ns"
的值。参考
这里有一页关于内存访问速度的测试结果(需要注意的是,这显然取决于你使用的处理器和内存)。
https://www.7-cpu.com/cpu/Skylake.html