使用 C++ 查找第 1500000 个斐波那契数

Finding the 1500000th Fib Number Using C++

本文关键字:C++ 查找 1500000 使用      更新时间:2023-10-16

我写了以下代码来查找第 1500000 个斐波那契数(请忽略可怕的缩进,我在大约 2 分钟内写了这个)。我需要它作为字符串。假设这应该有效:

#include <cstdlib>
#include <iostream>
#include <sstream>
int i;
int b=1;
int c=2;
using namespace std;
int main(int argc, char *argv[])
{
    int fib=1500000;
for (i=1;i<fib-3;i++){
c=b+c;
b=c-b;
}
   stringstream ss;
   ss << c;
   string d=ss.str();
cout << d << endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}

它经历了1500000-3次的过程(每次超过3个数字)我知道问题是这个数字太大,不能作为一个整数来包含。有没有办法让我在不将其包含为 int 或文件的情况下存储它(因为那效率极低)?如果是这样,我该怎么做?

使用 Bignum 库。

如果您需要一个确切的形式,则可以对 bignum 库使用其他递归关系之一:

fib(2*n) = fib(n)^2 + fib(n-1)^2
fib(2*n-1) = fib(n)*(2*fib(n-1)+fib(n))

这应该减少所需的计算次数到O(log(n))而不是O(n)您可以在此处查看基于此方法的伪代码:亚线性时间中的第 n 个斐波那契数

请注意,第 n^ 个斐波那契数需要大约 n*log(phi)/log(2) = n*0.69 个二进制数字来表示,因此 1.5M^th 的精确表示将需要大约 130kb 以二进制表示,或 300kb 表示为字符串(大约为 2^(10000000) 或 10^(300000) )


在大约 n=1500 时作为双倍溢出删除

你可以直接使用双精度来做到这一点,如下所示(改编自 http://en.wikipedia.org/wiki/Fibonacci_number):

double fib( int n )
{
   static const SQRT_5 = sqrt(5.0);
   static const phi = (1.0+SQRT_5)/2.0;
   return floor( (pow(phi,n)/SQRT_5) + 0.5 );
}

虽然如果你需要每个数字,这是行不通的。(它只会给出大约第 78 个斐波那契数的每个数字)

Boost 使使用 bignums 变得非常容易。

#include <boost/multiprecision/cpp_int.hpp>
typedef boost::multiprecision::cpp_int integer;
integer fibonacci( unsigned long long n )
{
  integer a = 0;
  integer b = 1;
  while (n --> 0)
  {
    integer c = a + b;
    a = b;
    b = c;
  }
  return a;
}

#include <iostream>
#include <string>
int main( int argc, char ** argv )
try
{
  long long n = std::stoll( argc == 2 ? argv[1] : "" );
  if (n < 0) throw 1;
  std::cout << fibonacci( n ) << "n";
}
catch (...)
{
  std::cerr << 
    "usage:                                     n"
    "  fibonacci N                              n"
    "where N is the Fibonacci number to compute.n";
  return 1;
}

在上面的例子中,我使用了原生的 Boost bignum 类型,即:

  • 仅标头(无需编译 Boost!
  • 非常快
    — 我在古老的 AMD 闪龙 130 上用大约两分钟计算斐波那契 (1500000)。如果我敲掉一个零,结果几乎是瞬间的。

如果这对您来说还不够快,Boost Multiprecision 还提供以下后端:

  • GNU GMP
    这是目前最好的 bignum 库,几乎在所有严肃的 bignum 计算环境中使用,但它受到 copyleft 的阻碍,并且众所周知很难正确设置。
  • libTomMath
    这更容易安装,但你仍然必须知道你在做什么。

第 1500000 个斐波那契数列在 C99 中,使用这个答案,只需更改一下:

#define BUFFER_SIZE 18935

没有库,输出应该是(313,482位):

129089216811873951612785 ... 853738956168354898000000
took 59s

f(1500000) 的计算也花了 30 秒和基本转换,所以大约一分钟。

1963年,Stephen P. Geller使用IBM 1620计算机显示,最后四位数字每15,000次重复一次,最后五位数字每150,000次重复一次,最后,在计算机运行近三个小时后,最后六位数字的重复出现在第1,500,000个斐波那契数上。

在线尝试f(15000) - 谢谢。

你确定C++快吗?

    gawk  -v _____='1500000' -v PREC=20000 -p- -Mbe '
    BEGIN {
     1      _ += ___ = _ ^= ____ = __ = _ < _
     1      _____ += ___
     1      ____ = --_____
     1      do {
     1          print __ = length(_ = fib(____)), substr(_, 1, 50), substr(_, __ + 1 - 50)
        } while (++____ < _____)
    }
     1  function fib(______, _, __, ___, ____, _____)
    {
     1      if ((______ = +_ < ______ ? +______ : +_) < (__ *= (__ = _ ~ _) + (++__))) {
            return (______ - ((! _) < (______ % --__)))
        }
     1      _____ = (___ = __ = (_ = _ < _) ~ _) + ___
    20      while ((___ += ___) <= ______) {
    20          ++____
        }
    21      do {
    21          __ = (__ * __ + _ * _) substr(_ *= __ + __ - _, ___, ! ___)
    21          if (int(______ / (___ /= _____)) % _____) { # 10
    10              __ = (__ + _) substr(_ = __, ___, ! ___)
            }
        } while (____--)
     1      return _
    }
( gawk -v _____='1500000' -v PREC=20000 -p- -Mbe ; )  0.10s user 0.01s system 95% cpu 0.115 total
 313482 12908921681187395161278531776694736120150441703840        
        02113051432895630172796932853738956168354898000000

*到底怎么C++花了 59.0 秒awk需要 0.115 秒