为什么用 递归 计算“阶乘”和“斐波那契数列”是不合适的?

8 篇文章 0 订阅
2 篇文章 0 订阅
Technorati 标签: 递归, 阶乘, 斐波那契数列, fibonacci

        我们看到的参考书中,当讲到递归时基本上都是使用“阶乘”和“斐波那契数列”来举例子,确实可以帮助我们了解递归,但却导致我们在平时编写类似程序时,总是使用递归来实现。那么在实际项目中,使用递归来实现这两个程序到底是否合适?答案是否定的。

        《C和指针》的作者Kenneth A. Reek说,他认为这是很不幸的:“计算阶乘时不能提供任何优越之处;在斐波那契数列中,使用递归效率非常非常低”。  尤其对于计算斐波那契数,使用递归和使用迭代的效率有可能相差几十万倍,下面有代码分析。

1. 技巧

    1.1 “尾部递归”可以使用“迭代”来替换。 学过数据结构之后,对这个应该不陌生。

    1.2   递归的优点在于易于理解迭代的优点在于效率高。如何选择取决于你在“易于理解”和“效率”中如何作出权衡。

            递归函数调用涉及一些运行时开销,包括参数必须压到堆栈中、为局部变量分配内存、寄存器的值必须保存等等,并且在每次调用返回时,上述保存操作必须还原,恢复成调用前的样子。迭代的实现方式开销显然要小。所以在可理解性相差不大的情况下,为了保证效率应该优先选择迭代

2. 阶乘

         阶乘的一种定义方式为:

F(n) = 1        当 n<=0
F(n) = n * F(n-1)       当n>0

    2.1  递归实现

          实现代码如下

/* 利用 递归 来计算整数n的阶乘 */
long factorial_recursion( int n ){
    if( n<=0 ){
        return 1;
    }else{
        return n*factorial_recutsion( n-1 ); 
    } 
}

         很明显是“尾部递归”,所以我们可以很轻松的改写为“迭代”的形式。

    2.2   迭代实现

         迭代实现代码如下

/* 利用 迭代 来计算整数n的阶乘 */ 
long factorial_iteration( int n ){
    int result = 1;
    while( n>1 ){
        result *=n;
        n--;
    }
    return result;
}

    2.3   两种方式的比较

        很明显,两种实现方式都非常简单易懂,在实际项目中,为了效率,应该优先选择 迭代

3. 斐波那契数列

        斐波那契数列中的每个数,都是它前面两个数的和。其计算公式如下:

F(0) = 0
F(1) = 1
F(n)= F(n-1) + F(n-2)

    3.1  递归实现

          递归实现代码如下

/* 计算斐波那契数列的第num个数 */
int fibonacci_count( int num ){
    if( num<=2 ){
        return 1;
    }else{
        return fibonacci_count(num-1) + fibonacci_count(num-2);
    }
}

    3.2  迭代实现

           迭代实现代码如下

/* 
利用 迭代 计算 斐波那契数 
其形式为: 1, 1, 2, 3, 5, 8, 13, 21, 34 … 
*/
long fibonacci(int n){
    long result;
    long previous;
    long next;
    
    result = previous = 1;
    
    if(n<=2){
        return 1;
    }
    int i =3; /* 用于计算已经计算到第几个 */ 
    while( i<=n ){
        next = result;
        result = previous + next;
        previous = next;
        i++;
    } 
    return result;    
}

    3.3  两种方式比较

        下面一个程序反映了,利用递归方式计算斐波那契函数时,调用fibonacci函数的次数,以及计算fibonacci(3)的次数。

#include <stdio.h>
#include <stdlib.h> 

int fibonacci_count( int num );

long count;    /* 用来记录斐波那契数列中计算调用 fibonacci_rec_count函数 的次数  */
int num_three; /* 计算 fibonacci_rec_count(3)的次数 */
int main(){
    printf("Fibonacci(num)/t/tNumber of Calls/t/tnumbers of Call fibonacci_rec_count(3)   /n");
    int i;
    for( i=1; i<=30; i++ ){
        int result = fibonacci_rec_count(i);
        printf("num: %d/t/t/tcount: %ld   /t/t%d/n", i, count, num_three);
        count = 0;
        num_three = 0;
    } 
    
    getchar();
    return EXIT_SUCCESS;
}
/* 计算斐波那契数列的第num个数 */
int fibonacci_rec_count( int num ){
    count++;    /* 每调用一次fibonacci_rec_count函数,count的值便加1  */
    if( num==3 ){
        num_three++;
    } 
    if( num<=2 ){
        return 1;
    }else{
        return fibonacci_rec_count(num-1) + fibonacci_rec_count(num-2);
    }
}

运行结果为:

Fibonacci(num)          Number of Calls         numbers of fibonacci(3)
num: 1                  count: 1                0
num: 2                  count: 1                0
num: 3                  count: 3                1
num: 4                  count: 5                1
num: 5                  count: 9                2
num: 6                  count: 15               3
num: 7                  count: 25               5
num: 8                  count: 41               8
num: 9                  count: 67               13
num: 10                 count: 109              21
num: 11                 count: 177              34
num: 12                 count: 287              55
num: 13                 count: 465              89
num: 14                 count: 753              144
num: 15                 count: 1219             233
num: 16                 count: 1973             377
num: 17                 count: 3193             610
num: 18                 count: 5167             987
num: 19                 count: 8361             1597
num: 20                 count: 13529            2584
num: 21                 count: 21891            4181
num: 22                 count: 35421            6765
num: 23                 count: 57313            10946
num: 24                 count: 92735            17711
num: 25                 count: 150049                   28657
num: 26                 count: 242785                   46368
num: 27                 count: 392835                   75025
num: 28                 count: 635621                   121393
num: 29                 count: 1028457                  196418
num: 30                 count: 1664079                  317811

        在递归实现方式中,每次递归调用都会触发另外两个递归调用,而且这两个调用的任何一个又会触发两个递归调用,依次越来越多。冗余的计算增长迅速。例如:在计算fibonacci(10) 时,fibonacci(3)被计算了21次;但在计算fibonacci(30) 时,fibonacci(3)被计算了 317811 次;这 317811 次计算结果完全一样,除了一次有用外,其余都是浪费。

参考资料:《C和指针》

本文链接:http://blog.csdn.net/daheiantian/archive/2011/01/12/6198912.aspx

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值