如何更快地生成斐波那契数

发布于 2024-09-11 10:48:15 字数 593 浏览 7 评论 0原文

我是一名 CSE 学生,正在为编程竞赛做准备。现在我正在研究斐波那契数列。我有一个大小约为几千字节的输入文件,其中包含正整数。输入格式看起来像

3 5 6 7 8 0

一个零意味着文件结束。输出应该像

2 
5 
8 
13 
21 

我的代码

#include<stdio.h>

int fibonacci(int n) {
  if (n==1 || n==2)
    return 1;
  else
    return fibonacci(n-1) +fibonacci(n-2);
}
int main() {
  int z;
  FILE * fp;    
  fp = fopen ("input.txt","r");    
  while(fscanf(fp,"%d", &z) && z) 
   printf("%d \n",fibonacci(z));
  return 0;
}

一样该代码适用于示例输入并提供准确的结果,但问题是对于我的真实输入集,它花费的时间比我的时间限制更多。谁能帮我一下。

I am a CSE student and preparing myself for programming contest.Now I am working on Fibonacci series. I have a input file of size about some Kilo bytes containing positive integers. Input formate looks like

3 5 6 7 8 0

A zero means the end of file. Output should like

2 
5 
8 
13 
21 

my code is

#include<stdio.h>

int fibonacci(int n) {
  if (n==1 || n==2)
    return 1;
  else
    return fibonacci(n-1) +fibonacci(n-2);
}
int main() {
  int z;
  FILE * fp;    
  fp = fopen ("input.txt","r");    
  while(fscanf(fp,"%d", &z) && z) 
   printf("%d \n",fibonacci(z));
  return 0;
}

The code works fine for sample input and provide accurate result but problem is for my real input set it is taking more time than my time limit. Can anyone help me out.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

等风来 2024-09-18 10:48:15

如果内存有限,您可以简单地使用函数的尾递归版本,该函数返回最后两个斐波那契数。

int fib(int n)
{
    int a = 0;
    int b = 1;
    while (n-- > 1) {
        int t = a;
        a = b;
        b += t;
    }
    return b;
}

这是 O(n) 并且需要一个常量空间。

You could simply use a tail recursion version of a function that returns the two last fibonacci numbers if you have a limit on the memory.

int fib(int n)
{
    int a = 0;
    int b = 1;
    while (n-- > 1) {
        int t = a;
        a = b;
        b += t;
    }
    return b;
}

This is O(n) and needs a constant space.

不气馁 2024-09-18 10:48:15

您可能应该研究一下记忆​​。

http://en.wikipedia.org/wiki/Memoization

它有一个解释和一个谎言示例就在那里

You should probably look into memoization.

http://en.wikipedia.org/wiki/Memoization

It has an explanation and a fib example right there

一抹微笑 2024-09-18 10:48:15

您可以通过矩阵乘法来完成此操作,将矩阵求 n 次方,然后乘以一个向量。您可以在对数时间内将其提高功率。

我认为您可以在此处找到问题。它是罗马尼亚语,但您可以使用谷歌翻译进行翻译。这正是您想要的,并且解决方案已列在此处。

You can do this by matrix multiplictation, raising the matrix to power n and then multiply it by an vector. You can raise it to power in logaritmic time.

I think you can find the problem here. It's in romanian but you can translate it with google translate. It's exactly what you want, and the solution it's listed there.

笑叹一世浮沉 2024-09-18 10:48:15

您的算法是递归的,并且大约具有 O(2^N) 复杂度。

这个问题之前在stackoverflow上讨论过:
斐波那契数列的计算复杂性

在该特定讨论中还发布了更快的实现。

Your algorithm is recursive, and approximately has O(2^N) complexity.

This issue has been discussed on stackoverflow before:
Computational complexity of Fibonacci Sequence

There is also a faster implementation posted in that particular discussion.

心不设防 2024-09-18 10:48:15

看看维基百科,有一个公式给出了斐波那契数列中的数字,没有递归全部

Look in Wikipedia, there is a formula that gives the number in the Fibonacci sequence with no recursion at all

抚你发端 2024-09-18 10:48:15

使用记忆。也就是说,您缓存答案以避免不必要的递归调用。

这是一个代码示例:

#include <stdio.h>

int memo[10000]; // adjust to however big you need, but the result must fit in an int
                 // and keep in mind that fibonacci values grow rapidly :)

int fibonacci(int n) {
  if (memo[n] != -1)
    return memo[n];

  if (n==1 || n==2)
    return 1;
  else
    return memo[n] = fibonacci(n-1) +fibonacci(n-2);
}
int main() {
  for(int i = 0; i < 10000; ++i)
    memo[i] = -1;
  fibonacci(50);
}

Use memoization. That is, you cache the answers to avoid unnecessary recursive calls.

Here's a code example:

#include <stdio.h>

int memo[10000]; // adjust to however big you need, but the result must fit in an int
                 // and keep in mind that fibonacci values grow rapidly :)

int fibonacci(int n) {
  if (memo[n] != -1)
    return memo[n];

  if (n==1 || n==2)
    return 1;
  else
    return memo[n] = fibonacci(n-1) +fibonacci(n-2);
}
int main() {
  for(int i = 0; i < 10000; ++i)
    memo[i] = -1;
  fibonacci(50);
}
冷月断魂刀 2024-09-18 10:48:15

没有人提到 2 值堆栈数组版本,所以我只是为了完整性而这样做。

// do not call with i == 0
uint64_t Fibonacci(uint64_t i)
{
  // we'll only use two values on stack,
  // initialized with F(1) and F(2)
  uint64_t a[2] = {1, 1};

  // We do not enter loop if initial i was 1 or 2 
  while (i-- > 2)
    // A bitwise AND allows switching the storing of the new value
    // from index 0 to index 1.
    a[i & 1] = a[0] + a[1];

    // since the last value of i was 0 (decrementing i),
    // the return value is always in a[0 & 1] => a[0].
  return a[0];
}                                                                

这是一个 O(n) 常量堆栈空间解决方案,在优化编译时其执行效果与记忆化略有相同。

// Calc of fibonacci f(99), gcc -O2
Benchmark            Time(ns)    CPU(ns) Iterations
BM_2stack/99                2          2  416666667
BM_memoization/99           2          2  318181818

这里使用的 BM_memoization 只会初始化数组一次,并在每次其他调用中重用它。

优化后,2 值堆栈数组版本的性能与带有临时变量的版本相同。

Nobody mentioned the 2 value stack array version, so I'll just do it for completeness.

// do not call with i == 0
uint64_t Fibonacci(uint64_t i)
{
  // we'll only use two values on stack,
  // initialized with F(1) and F(2)
  uint64_t a[2] = {1, 1};

  // We do not enter loop if initial i was 1 or 2 
  while (i-- > 2)
    // A bitwise AND allows switching the storing of the new value
    // from index 0 to index 1.
    a[i & 1] = a[0] + a[1];

    // since the last value of i was 0 (decrementing i),
    // the return value is always in a[0 & 1] => a[0].
  return a[0];
}                                                                

This is a O(n) constant stack space solution that will perform slightly the same than memoization when compiled with optimization.

// Calc of fibonacci f(99), gcc -O2
Benchmark            Time(ns)    CPU(ns) Iterations
BM_2stack/99                2          2  416666667
BM_memoization/99           2          2  318181818

The BM_memoization used here will initialize the array only once and reuse it for every other call.

The 2 value stack array version performs identically as a version with a temporary variable when optimized.

梅倚清风 2024-09-18 10:48:15

您还可以使用生成斐波那契数列的快速倍增方法
链接: fastest-way-to-compute -fibonacci-number

它实际上是由矩阵求幂方法的结果推导出来的。

You can also use the fast doubling method of generating Fibonacci series
Link: fastest-way-to-compute-fibonacci-number

It is actually derived from the results of the matrix exponentiation method.

北座城市 2024-09-18 10:48:15

构建一个数组 Answer[100],在其中缓存 fibonacci(n) 的结果。
检查您的斐波那契代码,看看您是否已预先计算出答案,并且
使用该结果。结果会让你大吃一惊。

Build an array Answer[100] in which you cache the results of fibonacci(n).
Check in your fibonacci code to see if you have precomputed the answer, and
use that result. The results will astonish you.

醉酒的小男人 2024-09-18 10:48:15

您是否保证,如您的示例所示,输入将按升序提供给您?如果是这样,你甚至不需要记忆;只需跟踪最后两个结果,开始生成序列,但如果 N 是输入中的下一个索引,则仅显示序列中的第 N 个数字。当你达到索引 0 时停止。

类似这样的事情:

int i = 0;
while ( true ) {
    i++; //increment index
    fib_at_i = generate_next_fib()
    while ( next_input_index() == i ) {
        println fib_at_i
}

我留下退出条件并实际为你生成序列。

Are you guaranteed that, as in your example, the input will be given to you in ascending order? If so, you don't even need memoization; just keep track of the last two results, start generating the sequence but only display the Nth number in the sequence if N is the next index in your input. Stop when you hit index 0.

Something like this:

int i = 0;
while ( true ) {
    i++; //increment index
    fib_at_i = generate_next_fib()
    while ( next_input_index() == i ) {
        println fib_at_i
}

I leave exit conditions and actually generating the sequence to you.

向日葵 2024-09-18 10:48:15

在 C# 中:

        static int fib(int n)
        {
            if (n < 2) return n;
            if (n == 2) return 1;
            int k = n / 2;
            int a = fib(k + 1);
            int b = fib(k);
            if (n % 2 == 1)
                return a * a + b * b;
            else
                return b * (2 * a - b);
        }

In C#:

        static int fib(int n)
        {
            if (n < 2) return n;
            if (n == 2) return 1;
            int k = n / 2;
            int a = fib(k + 1);
            int b = fib(k);
            if (n % 2 == 1)
                return a * a + b * b;
            else
                return b * (2 * a - b);
        }
远昼 2024-09-18 10:48:15

矩阵乘法,无浮点运算,O(log N) 时间复杂度(假设整数乘法/加法在恒定时间内完成)。

这是Python代码

def fib(n):
    x,y = 1,1
    mat = [1,1,1,0]
    n -= 1
    while n>0:
        if n&1==1:
            x,y = x*mat[0]+y*mat[1], x*mat[2]+y*mat[3]
        n >>= 1
        mat[0], mat[1], mat[2], mat[3] = mat[0]*mat[0]+mat[1]*mat[2], mat[0]*mat[1]+mat[1]*mat[3], mat[0]*mat[2]+mat[2]*mat[3], mat[1]*mat[2]+mat[3]*mat[3]
    return x

Matrix multiplication, no float arithmetic, O(log N) time complexity assuming integer multiplication/addition is done in constant time.

Here goes python code

def fib(n):
    x,y = 1,1
    mat = [1,1,1,0]
    n -= 1
    while n>0:
        if n&1==1:
            x,y = x*mat[0]+y*mat[1], x*mat[2]+y*mat[3]
        n >>= 1
        mat[0], mat[1], mat[2], mat[3] = mat[0]*mat[0]+mat[1]*mat[2], mat[0]*mat[1]+mat[1]*mat[3], mat[0]*mat[2]+mat[2]*mat[3], mat[1]*mat[2]+mat[3]*mat[3]
    return x
跨年 2024-09-18 10:48:15

您可以减少 if 语句的开销: Calculate Fibonacci Numbers Recursively in C

You can reduce the overhead of the if statement: Calculating Fibonacci Numbers Recursively in C

梦幻之岛 2024-09-18 10:48:15

首先,您可以使用 memoization 或同一算法的迭代实现。

考虑您的算法进行的递归调用次数:

fibonacci(n) 调用 fibonacci(n-1) 和 fibonacci(n-2)
fibonacci(n-1) 调用 fibonacci(n-2)fibonacci(n-3)
fibonacci(n-2) 调用 fibonacci(n-3) 和 fibonacci(n-4)

注意到一个模式了吗?您计算同一函数的次数超出了需要的次数。

迭代实现将使用数组:

int fibonacci(int n) {
    int arr[maxSize + 1]; 
    arr[1] = arr[2] = 1; // ideally you would use 0-indexing, but I'm just trying to get a point across
    for ( int i = 3; i <= n; ++i )
        arr[i] = arr[i - 1] + arr[i - 2]; 

    return arr[n];
}

这已经比您的方法快得多。您可以按照相同的原则更快地完成此操作,只需构建一次数组,直到达到 n 的最大值,然后通过打印数组的元素在单个操作中打印正确的数字。这样您就不必为每个查询调用该函数。

如果您无法承担初始预计算时间(但这通常仅在您被要求对结果取模时才会发生,否则他们可能不希望您实现大数算术,而预计算是最好的解决方案),请阅读斐波那契维基页面了解其他方法。重点关注矩阵方法,在竞赛中了解该方法非常有用。

First of all, you can use memoization or an iterative implementation of the same algorithm.

Consider the number of recursive calls your algorithm makes:

fibonacci(n) calls fibonacci(n-1) and fibonacci(n-2)
fibonacci(n-1) calls fibonacci(n-2) and fibonacci(n-3)
fibonacci(n-2) calls fibonacci(n-3) and fibonacci(n-4)

Notice a pattern? You are computing the same function a lot more times than needed.

An iterative implementation would use an array:

int fibonacci(int n) {
    int arr[maxSize + 1]; 
    arr[1] = arr[2] = 1; // ideally you would use 0-indexing, but I'm just trying to get a point across
    for ( int i = 3; i <= n; ++i )
        arr[i] = arr[i - 1] + arr[i - 2]; 

    return arr[n];
}

This is already much faster than your approach. You can do it faster on the same principle by only building the array once up until the maximum value of n, then just print the correct number in a single operation by printing an element of your array. This way you don't call the function for every query.

If you can't afford the initial precomputation time (but this usually only happens if you're asked for the result modulo something, otherwise they probably don't expect you to implement big number arithmetic and precomputation is the best solution), read the fibonacci wiki page for other methods. Focus on the matrix approach, that one is very good to know in a contest.

赢得她心 2024-09-18 10:48:15
#include<stdio.h>

 int g(int n,int x,int y)
   {
   return n==0 ? x : g(n-1,y,x+y);}

 int f(int n)
   {
   return g(n,0,1);}

 int main (void)
   {  
   int i;
   for(i=1; i<=10 ; i++)
     printf("%d\n",f(i)
   return 0;
   }
#include<stdio.h>

 int g(int n,int x,int y)
   {
   return n==0 ? x : g(n-1,y,x+y);}

 int f(int n)
   {
   return g(n,0,1);}

 int main (void)
   {  
   int i;
   for(i=1; i<=10 ; i++)
     printf("%d\n",f(i)
   return 0;
   }
宛菡 2024-09-18 10:48:15

在函数式编程中,有一种特殊的斐波那契计数算法。该算法使用累积递归。累积递归用于最小化算法使用的堆栈大小。我认为这会帮助你最大限度地减少时间。如果你愿意的话可以尝试一下。

int ackFib (int n, int m, int count){
    if (count == 0)
        return m;
    else
        return ackFib(n+m, n, count-1);
}



int fib(int n)
{
 return ackFib (0, 1, n+1);
}

In the functional programming there is a special algorithm for counting fibonacci. The algorithm uses accumulative recursion. Accumulative recursion are used to minimize the stack size used by algorithms. I think it will help you to minimize the time. You can try it if you want.

int ackFib (int n, int m, int count){
    if (count == 0)
        return m;
    else
        return ackFib(n+m, n, count-1);
}



int fib(int n)
{
 return ackFib (0, 1, n+1);
}
少跟Wǒ拽 2024-09-18 10:48:15

使用以下任意一种:两个递归示例,一个使用 for 循环 O(n) 时间,一个使用黄金比例 O(1) 时间:

private static long fibonacciWithLoop(int input) {
    long prev = 0, curr = 1, next = 0;      
    for(int i = 1; i < input; i++){
        next = curr + prev;
        prev = curr;
        curr = next;
    }
    return curr;
}

public static long fibonacciGoldenRatio(int input) {
    double termA = Math.pow(((1 + Math.sqrt(5))/2), input);
    double termB = Math.pow(((1 - Math.sqrt(5))/2), input);
    double factor = 1/Math.sqrt(5);
    return Math.round(factor * (termA - termB));
}

public static long fibonacciRecursive(int input) {
    if (input <= 1) return input;
    return fibonacciRecursive(input - 1) + fibonacciRecursive(input - 2);
}

public static long fibonacciRecursiveImproved(int input) {
    if (input == 0) return 0;
    if (input == 1) return 1;
    if (input == 2) return 1;
    if (input >= 93) throw new RuntimeException("Input out of bounds");
    // n is odd
    if (input % 2 != 0) {
        long a = fibonacciRecursiveImproved((input+1)/2);
        long b = fibonacciRecursiveImproved((input-1)/2);
        return a*a + b*b;
    }

    // n is even
    long a = fibonacciRecursiveImproved(input/2 + 1);
    long b = fibonacciRecursiveImproved(input/2 - 1);
    return a*a - b*b;
}

use any of these: Two Examples of recursion, One with for Loop O(n) time and one with golden ratio O(1) time:

private static long fibonacciWithLoop(int input) {
    long prev = 0, curr = 1, next = 0;      
    for(int i = 1; i < input; i++){
        next = curr + prev;
        prev = curr;
        curr = next;
    }
    return curr;
}

public static long fibonacciGoldenRatio(int input) {
    double termA = Math.pow(((1 + Math.sqrt(5))/2), input);
    double termB = Math.pow(((1 - Math.sqrt(5))/2), input);
    double factor = 1/Math.sqrt(5);
    return Math.round(factor * (termA - termB));
}

public static long fibonacciRecursive(int input) {
    if (input <= 1) return input;
    return fibonacciRecursive(input - 1) + fibonacciRecursive(input - 2);
}

public static long fibonacciRecursiveImproved(int input) {
    if (input == 0) return 0;
    if (input == 1) return 1;
    if (input == 2) return 1;
    if (input >= 93) throw new RuntimeException("Input out of bounds");
    // n is odd
    if (input % 2 != 0) {
        long a = fibonacciRecursiveImproved((input+1)/2);
        long b = fibonacciRecursiveImproved((input-1)/2);
        return a*a + b*b;
    }

    // n is even
    long a = fibonacciRecursiveImproved(input/2 + 1);
    long b = fibonacciRecursiveImproved(input/2 - 1);
    return a*a - b*b;
}
两仪 2024-09-18 10:48:15
using namespace std;

void mult(LL A[ 3 ][ 3 ], LL B[ 3 ][ 3 ]) {

     int i,

         j,

         z;

     LL C[ 3 ][ 3 ];

     memset(C, 0, sizeof( C ));

     for(i = 1; i <= N; i++)

         for(j = 1; j <= N; j++) {

             for(z = 1; z <= N; z++)

                 C[ i ][ j ] = (C[ i ][ j ] + A[ i ][ z ] * B[ z ][ j ] % mod ) % mod;
         }

     memcpy(A, C, sizeof(C));
};

void readAndsolve() {

    int i;

    LL k;

    ifstream I(FIN);
    ofstream O(FOUT);

    I>>k;

    LL A[3][3];
    LL B[3][3];

    A[1][1] = 1; A[1][2] = 0;
    A[2][1] = 0; A[2][2] = 1;

    B[1][1] = 0; B[1][2] = 1;
    B[2][1] = 1; B[2][2] = 1;

    for(i = 0; ((1<<i) <= k); i++) {

          if( k & (1<<i) ) mult(A, B);

          mult(B, B);
    }

    O<<A[2][1];
}

//1,1,2,3,5,8,13,21,33,...

int main() {

    readAndsolve();

    return(0);
}
using namespace std;

void mult(LL A[ 3 ][ 3 ], LL B[ 3 ][ 3 ]) {

     int i,

         j,

         z;

     LL C[ 3 ][ 3 ];

     memset(C, 0, sizeof( C ));

     for(i = 1; i <= N; i++)

         for(j = 1; j <= N; j++) {

             for(z = 1; z <= N; z++)

                 C[ i ][ j ] = (C[ i ][ j ] + A[ i ][ z ] * B[ z ][ j ] % mod ) % mod;
         }

     memcpy(A, C, sizeof(C));
};

void readAndsolve() {

    int i;

    LL k;

    ifstream I(FIN);
    ofstream O(FOUT);

    I>>k;

    LL A[3][3];
    LL B[3][3];

    A[1][1] = 1; A[1][2] = 0;
    A[2][1] = 0; A[2][2] = 1;

    B[1][1] = 0; B[1][2] = 1;
    B[2][1] = 1; B[2][2] = 1;

    for(i = 0; ((1<<i) <= k); i++) {

          if( k & (1<<i) ) mult(A, B);

          mult(B, B);
    }

    O<<A[2][1];
}

//1,1,2,3,5,8,13,21,33,...

int main() {

    readAndsolve();

    return(0);
}
赠我空喜 2024-09-18 10:48:15
public static int GetNthFibonacci(int n)
    {
        var previous = -1;
        var current = 1;
        int element = 0;

        while (1 <= n--)
        {
            element = previous + current;
            previous = current;
            current = element;
        }

        return element;
    }
public static int GetNthFibonacci(int n)
    {
        var previous = -1;
        var current = 1;
        int element = 0;

        while (1 <= n--)
        {
            element = previous + current;
            previous = current;
            current = element;
        }

        return element;
    }
孤云独去闲 2024-09-18 10:48:15

这与之前给出的答案类似,但有一些修改。正如其他答案中所述,记忆是实现此目的的另一种方法,但我不喜欢随着技术变化而无法扩展的代码(unsigned int 的大小因平台而异),因此最高值可以达到的顺序也可能会有所不同,而且在我看来,记忆是丑陋的。

#include <iostream>

using namespace std;

void fibonacci(unsigned int count) {
   unsigned int x=0,y=1,z=0;
   while(count--!=0) {
      cout << x << endl;  // you can put x in an array or whatever
      z = x;
      x = y;
      y += z;
   }
}

int main() {
   fibonacci(48);// 48 values in the sequence is the maximum for a 32-bit unsigend int
   return 0;
}

此外,如果您使用,则可以编写一个编译时常量表达式,该表达式将为您提供序列中任何整数数据类型均可达到的最大索引。

This is similar to answers given before, but with some modifications. Memorization, as stated in other answers, is another way to do this, but I dislike code that doesn't scale as technology changes (size of an unsigned int varies depending on the platform) so the highest value in the sequence that can be reached may also vary, and memorization is ugly in my opinion.

#include <iostream>

using namespace std;

void fibonacci(unsigned int count) {
   unsigned int x=0,y=1,z=0;
   while(count--!=0) {
      cout << x << endl;  // you can put x in an array or whatever
      z = x;
      x = y;
      y += z;
   }
}

int main() {
   fibonacci(48);// 48 values in the sequence is the maximum for a 32-bit unsigend int
   return 0;
}

Additionally, if you use <limits> its possible to write a compile-time constant expression that would give you the largest index within the sequence that can be reached for any integral data type.

请你别敷衍 2024-09-18 10:48:15
#include<stdio.h>
main()
{
 int a,b=2,c=5,d;
   printf("%d %d ");
do
{
   d=b+c;
     b=c;
    c=d;
   rintf("%d ");
  }
#include<stdio.h>
main()
{
 int a,b=2,c=5,d;
   printf("%d %d ");
do
{
   d=b+c;
     b=c;
    c=d;
   rintf("%d ");
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文