斐波那契使用递归

时间:2018-08-14 18:45:25

标签: c recursion fibonacci

这是我解决“具有最小处理能力的斐波那契级数的第n个项”的想法-

int fibo(int n, int a, int b){
    return (n>0) ? fibo(n-1, b, a+b) : a;
}

main(){
    printf("5th term of fibo is %d", fibo(5 - 1, 0, 1));
}

要打印所有术语,直到第n个术语,

int fibo(int n, int a, int b){
    printf("%d ", a);
    return (n>0)? fibo(n-1, b, a+b): a;
}

我向我的大学教授展示了此代码,根据她的说法,这是解决斐波那契问题的错误方法,因为它没有抽象该方法。我应该将函数称为fibo(n)而不是fibo(n,0,1)。这对我来说不是一个令人满意的答案,因此我想到向SOF的专家咨询。

与解决斐波那契问题的传统方法相比,它具有自己的优势。我们采用两次并行递归来获得斐波那契第n项(fibo(n-1)+ fibo(n-2))的技术可能很慢,无法给出该系列的第100个项,而即使在最坏的情况下,我的技术也会更快场景。

要抽象化它,我可以使用默认参数,但C语言不是这种情况。尽管我可以使用-

int fibo(int n){return fiboN(n - 1, 0, 1);}
int fiboN(int n, int a, int b){return (n>0)? fiboN(n-1, b, a+b) : a;}

但是抽象整个想法就足够了吗?我应该如何说服其他人该方法没有错(尽管有点含糊)?

(我知道,这不是我应该对SOF提出的问题,但我只是想从这里征求专家的意见。)

6 个答案:

答案 0 :(得分:4)

了解到递归的基本情况应该是a而不是0,因此在我看来,这是一个很好的解决方案(尽管不是最佳选择)。该函数的递归是尾递归,因此,好的编译器将能够避免堆栈增长,从而使函数O(1)停滞和O(n)成为时间(忽略数字大小的快速增长)。

您的教授是正确的,即呼叫者不必处理正确的初始化。因此,您应该提供一个外部包装程序,从而避免填写这些值。

int fibo(int n, int a, int b) {
    return n > 0 ? fibo(b, a + b) : a;
}
int fib(int n) { return fibo(n, 0, 1); }

但是,如果调用者实际上想要更改初始值,则提供和记录更通用的接口也可能很有用。

顺便说一句,基于递归,有一种更快的计算技术

fib(a + b - 1) = f(a)f(b) + f(a - 1)f(b - 1)

b代替b + 1会产生:

fib(a + b) = f(a)f(b + 1) + f(a - 1)f(b)

这些公式可以让我们计算:

fib(2n - 1) = fib(n + n - 1)
            = fib(n)² + fib(n - 1)²

fib(2n)     = fib(n + n)
            = fib(n)fib(n + 1) + fib(n - 1)fib(n)
            = fib(n)² + 2fib(n)fib(n - 1)

这允许以O(log n)步骤执行计算,每个步骤产生两个连续的值。

答案 1 :(得分:2)

使用您的方法,您的结果将是 0 。您只需递归,直到 n = 0 ,然后返回 0 。但是,您还必须检查 n == 1 的时间,并且应该返回 1 ;另外,您还具有 a b 值,而您对它们不执行任何操作。

我建议您看一下下面的递归函数,也许它将有助于修复您的递归函数:

int fibo(int n){
    if(n < 2){
        return n;
    }
    else 
    {
       return (fibo(n-1) + fibo(n-2));    
    }
}

这是研究递归的经典问题。

EDIT1:根据@Ely的建议,波纹管是一种具有记忆技术的优化递归。计算列表中的一个值后,将不会像第一个示例中那样再次对其进行重新计算,但是将其存储在数组中,并在需要时从该数组中获取:

const int MAX_FIB_NUMBER = 10;

int storeCalculatedValues[MAX_FIB_NUMBER] = {0};

int fibo(int n){

    if(storeCalculatedValues[n] > 0)
    {
        return storeCalculatedValues[n];
    }

    if(n < 2){
        storeCalculatedValues[n] = n;
    }
    else 
    {
       storeCalculatedValues[n] = (fibo(n-1) + fibo(n-2));
    }
    return storeCalculatedValues[n];
}

答案 2 :(得分:1)

使用递归,并以最低处理能力为目标,一种解决fibonacci()的方法是让每个呼叫返回2个值。也许一个通过返回值,另一个通过int *参数。

递归的通常想法是让顶层函数一次性执行准备工作并检查参数,然后以精简的方式编写本地帮助函数。


以下内容遵循OP的int fibo(int n)和助手int fiboN(int n, additional parameters)

的想法。

递归深度为O(n),内存使用量也为O(n)。

static int fib1h(int n, int *previous) {
  if (n < 2) {
    *previous = n-1;
    return n;
  }
  int t;
  int sum = fib1h(n-1, &t);
  *previous = sum;
  return sum + t;
}

int fibo1(int n) {
  assert(n >= 0); // Handle negatives in some fashion
  int t;
  return fib1h(n, &t);
}

答案 3 :(得分:0)

  

求解“具有最小处理能力的斐波那契级数的第n个项”

我可能不需要向您解释斐波那契数的递归关系。尽管您的教授给了您很好的提示。

提取细节。她是对的。如果您想要第n个斐波那契数,只需告诉程序仅Fibonacci(n)

就足够了

因为您的目标是提高处理能力,您的教授的提示也适用于称为memoization的技术,这基本上意味着如果您一次计算了第n个斐波那契数,则只需重用结果;无需重做计算。在本文中,您可以找到阶乘数的示例。

为此,您可能需要考虑一个存储第n个斐波那契数的数据结构;如果该内存中已有斐波那契数,则只需检索它,否则将计算出的斐波那契数存储在其中。

顺便说一句,从理论上讲并没有帮助,但很有趣:第n个斐波那契数也有一个closed form expression

  

这对我来说不是一个令人满意的答案,所以我想到了问   SOF专家。

“呃,您不认为您的教授是专家吗?”是我的第一个念头。

答案 4 :(得分:0)

作为旁注,您几乎可以进行fibonacci problem而无需递归,这使其成为我所知最快的方法。该代码虽然在Java中:

public int fibFor() {
    int sum = 0;
    int left = 0;
    int right = 1;
    for (int i = 2; i <= n; i++) {
        sum = left + right;
        left = right;
        right = sum;
    }
    return sum;
}

答案 5 :(得分:0)

尽管@rici的回答大部分令人满意,但是我只想分享我学到的解决该问题的知识。因此,这就是我对使用递归查找斐波那契的理解-

  • 传统的实现fibo(n) { return (n < 2) n : fibo(n-1) + fibo(n-2);}在时间和空间要求方面都效率低下。这不必要地构建堆栈。它需要O(n)个堆栈空间和O(r n )时间,其中r =(√5+1)/ 2。
  • 使用@Simion答案中建议的记忆技术,我们只创建一个永久堆栈,而不是在运行时由编译器创建的动态堆栈。因此内存需求保持不变,但时间复杂度以摊销的方式降低。但是如果我们只需要使用一次就没有帮助。
  • 我在问题中建议的方法需要O(1)空间和O(n)时间。在这里,也可以使用相同的记忆技术以摊销的方式减少时间需求。
  • 在@rici的帖子fib(2n) = fib(n)² + 2fib(n)fib(n - 1)中,正如他建议将时间复杂度降低到O(log n)一样,我想堆栈的增长仍然是O(n)。

因此,我的结论是,如果我进行了适当的研究,则无法使用递归计算同时降低时间复杂度和空间需求。为了达到这两个目的,替代方案可以使用迭代Matrix exponentiation or fast doubling

相关问题