C语言用递归求斐波那契数,让你发现递归的缺陷和效率瓶颈
C语言用递归求斐波那契数,让你发现递归的缺陷和效率瓶颈
- 分享到:
- QQ空间
- 新浪微博
- 腾讯微博
- 豆瓣
- 人人网
一般需要递归解决的问题有两个特点:
- 存在限制条件,当符合这个条件时递归便不再继续;
- 每次递归调用之后越来越接近这个限制条件。
递归使用最常见的一个例子就是求阶乘,具体描述和代码请看这里:C语言递归和迭代法求阶乘
但是,递归函数调用将涉及一些运行时开销——参数必须压到堆栈中,为局部变量分配内存空间(所有递归均如此,并非特指求阶乘这个例子),寄存器的值必须保存等。当递归函数的每次调用返回时,上述这些操作必须还原,恢复成原来的样子。所以, 基于这些开销,对于递归求阶乘而言,它并没有简化问题的解决方案。
迭代求阶乘使用简单循环的程序,看上去不甚符合前面阶乘的数学定义,但它却能更为有效地计算出结果。如果你仔细观察递归函数,你会发现递归调用是函数所执行的最后一项任务。这个函数是尾部递归(tail recursion)的一个例子。由于函数在递归调用返回之后不再执行任何任务,所以尾部递归可以很方便地转换成一个简单循环,完成相同的任务。
提示:许多问题是以递归的形式进行解释的,这只是因为它比非递归形式更为清晰。但是,这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性可能稍差一些,当一个问题相当复杂,难以用迭代形式实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
这里有一个更为极端的例子,菲波那契数就是一个数列,数列中每个数的值就是它前面两个数的和。 这种关系常常用递归的形式进行描述:
同样,这种递归形式的定义容易诱导人们使用递归形式来解决问题。这里有一个陷牌:它使用递归步骤计算Fibonacci(n-1)和Fibonacci(n-2)。但是,在计算Fibonacci(n-1)时也将计算Fibonacci(n-2)。这个额外的计算代价有多大呢?
答案是,它的代价远远不止一个冗余计算:每个递归调用都触发另外两个递归调用,而这两个调用的任何一个还将触发两个递归调用,再接下去的调用也是如此。这样,冗余计算的数量增长得非常快。例如,在递归计算Fibonacci(10)时,Fibonacci(3)的值被计算了21次。但是,在递归计算 Fibonacci(30)时,Fibonacci(3)的值被计算了317811次。当然,这317811次计算所产生的结果是完全一样的,除了其中之一外,其余的纯属浪费。这个额外的开销真是相当恐怖!
如果使用一个简单循环来代替递归,这个循环的形式肯定不如递归形式符合前面菲波那契数的抽象定义,但它的效率提高了几十万倍!
当你使用递归方式实现一个函数之前,先问问你自己使用递归带来的好处是否抵得上它的代价。 而且你必须小心:这个代价可能比初看上去要大得多。
不信请看下面的代码,分别用递归和迭代计算斐波那契数,效率差距真是大的惊人。
复制纯文本复制
- #include <stdio.h>
- #include <time.h>
- #include <windows.h>
- // 递归计算斐波那契数
- long fibonacci_recursion( int n )
- {
- if( n <= 2 )
- return 1;
- return fibonacci_recursion(n-1) + fibonacci_recursion(n-2);
- }
- // 迭代计算斐波那契数
- long fibonacci_iteration( int n )
- {
- long result;
- long previous_result;
- long next_older_result;
- result = previous_result = 1;
- while( n > 2 ){
- n -= 1;
- next_older_result = previous_result;
- previous_result = result;
- result = previous_result + next_older_result;
- }
- return result;
- }
- int main(){
- int N = 45;
- // 递归消耗的时间
- clock_t recursion_start_time = clock();
- long result_recursion = fibonacci_recursion(N);
- clock_t recursion_end_time = clock();
- // 迭代消耗的时间
- clock_t iteration_start_time = clock();
- long result_iteration = fibonacci_iteration(N);
- clock_t iteration_end_time = clock();
- // 输出递归消耗的时间
- printf("Result of recursion: %ld \nTime: %fseconds",
- fibonacci_recursion(N),
- (double)(recursion_end_time-recursion_start_time)/CLOCKS_PER_SEC
- );
- printf("\n-----------------------\n");
- // 输出迭代消耗的时间
- printf("Result of iteration: %ld \nTime: %fseconds",
- fibonacci_iteration(N),
- (double)(iteration_end_time-iteration_start_time)/CLOCKS_PER_SEC
- );
- return 0;
- }
#include <stdio.h> #include <time.h> #include <windows.h>// 递归计算斐波那契数 long fibonacci_recursion( int n ) {if( n <= 2 )return 1;return fibonacci_recursion(n-1) + fibonacci_recursion(n-2); }// 迭代计算斐波那契数 long fibonacci_iteration( int n ) {long result;long previous_result;long next_older_result;result = previous_result = 1;while( n > 2 ){n -= 1;next_older_result = previous_result;previous_result = result;result = previous_result + next_older_result;}return result; }int main(){int N = 45;// 递归消耗的时间clock_t recursion_start_time = clock();long result_recursion = fibonacci_recursion(N);clock_t recursion_end_time = clock();// 迭代消耗的时间clock_t iteration_start_time = clock();long result_iteration = fibonacci_iteration(N);clock_t iteration_end_time = clock();// 输出递归消耗的时间printf("Result of recursion: %ld \nTime: %fseconds",fibonacci_recursion(N),(double)(recursion_end_time-recursion_start_time)/CLOCKS_PER_SEC);printf("\n-----------------------\n");// 输出迭代消耗的时间printf("Result of iteration: %ld \nTime: %fseconds",fibonacci_iteration(N),(double)(iteration_end_time-iteration_start_time)/CLOCKS_PER_SEC);return 0; }
运行结果:
Result of recursion: 1134903170 Time: 7.494000 seconds --------------------------------------- Result of iteration: 1134903170 Time: 0.000000 seconds
注意:上面的程序最好在GCC(Linux下的GCC或Windows下的Code:Blocks)下运行,VC下可能统计不到运行时间。
看吧,用递归花了将近7.5秒的时间,但是用迭代几乎不费吹灰之力,效率快到统计不到运行时间。
C语言用递归求斐波那契数,让你发现递归的缺陷和效率瓶颈相关推荐
- Java递归求斐波那契数求猴子吃桃
递归求斐波那契数 斐波那契数是指前两位是1,后面的数依次是其前两位的和.即1,1,2,3,5,8-给你一个 n,求其值是多少 public class recursionExercise01{//定义 ...
- scala递归求斐波那契数列
object RecursiveFnb {def main(args: Array[String]): Unit = {var count = BigInt(0)//1 1 2 3 5 ?printl ...
- C语言入门——求斐波那契数
斐波那契数列,又称黄金分割数列,因数学家莱昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为"兔子数列",指的是这样一个数列:1.1.2.3.5.8.13.21.34.--斐波那契数 ...
- C语言以递归求斐波那契数列(附完整源码)
递归求斐波那契数列 递归求斐波那契数列完整源码(定义,实现,main函数测试) 递归求斐波那契数列完整源码(定义,实现,main函数测试) #include <locale.h> #inc ...
- 用递归和非递归求斐波那契数列
递归的方法 #include<stdio.h> #include<stdlib.h> 递归的方式计算斐波那契数 int FeiBo(int f) {int n = 0;if ( ...
- 求斐波那契数(递归,非递归)
目录 一.斐波那契数? 二.递归实现求第n个斐波那契数 2.1代码与运行结果 2.1.1图解递归过程 三.非递归求法 3.1为什么不用递归求法 3.2非递归 一.斐波那契数? 它指的是这样的数列:1, ...
- 递归求斐波那契数列第n个数
斐波那契数列:第一个和第二个是1,从第三个开始每一项都是前两项的和 1 1 2 3 5 8 13 21 34 - 求斐波那契数列的第n项,利用递归思想,除了第一.二位,每一位都是前两项的和.递归函数的 ...
- 用C语言求斐波那契数1,1,2,3,5,8......
斐波那契数规律:第三个数等于前两个数之和 分别设为a,b,c,则 当n<=2时,c=1; 当n>2时,c=a+b 运用循环求,n--是防止程序进入死循环,当n>2时,每循环一次减一, ...
- c语言斐波那契数列前20项和,,c语言利用数组求斐波那契数列的前20项
推荐回答 一.斐波那契数列指的是这样一个数列1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711 ...
最新文章
- 原理图、PCB和实物是如何对应起来的
- 你为何要带着我的爱远走
- AndroidStudio中如何打开hierarchyviewer.bat
- 表单php跳转页面跳转,form表单页面跳转方式提交练习
- 基于ViSual Studio 2013 + MYSQL9_5302 + Navicat for MySQL9_5302的ATM自动存取款系统
- php多图片上传并压缩,PHP 上传图片并压缩方法详解
- win10我的电脑在哪里找到
- Golang的工程管理
- 写项目经历的注意事项
- 通过pip下载python包缓慢的解决方法
- patterns practices: Mobile Architecture Pocket Guide
- 计算机楼综合布线设计图,现代综合大楼综合布线设计方案
- matlab二重积分运算,matlab二重积分
- android吸顶效果,RecyclerVIew实现悬浮吸顶效果
- 关于编译报错“dereferencing pointer to incomplete type...
- Docker学习总结(46)——生产环境中遇到的Docker常见异常错误总结
- 2010年度个人工作总结
- 男人必学的几样家常炒菜,尤其是面对一个不会做饭的媳妇。
- 王者荣耀服务器能不能注销,王者荣耀游戏账号能永久注销吗 永久删除后还能恢复吗...
- Json系列之二 json to bean(JSONObject类详解)
热门文章
- 金坛区实验幼儿园服务器不稳定,2019年金坛城区部分公办幼儿园服务区划分方案(试行)...
- Linux:init0和shutdown -h哪个用来关机比较安全【转载】
- mysql的主从复制优缺点_MySQL主从复制原理,超级详细的总结,看完全通了
- python分析推特_用Python关注者的关注者抓取Twitter数据的最快方法
- 基于matlab实现的云模型计算隶属度,基于MATLAB实现的云模型计算隶属度
- python pandas库 画图_python绘图:matplotlib和pandas的应用
- 一般项目中哪里体现了数据结构_优秀程序员都应该学习的数据结构与算法项目(GitHub 开源清单)...
- mysql进阶知识_Mysql面试知识点总结(进阶篇)
- 云计算的概念_近500亿资金汹涌出逃!云计算概念龙头抛压沉重,科技股资金出逃名单出炉...
- aws python lambda_python – AWS Lambda发送HTTP请求