`
cq520
  • 浏览: 166852 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

重识递归

阅读更多

递归算法想必大家都已经非常熟悉了,这一次主要也不是教大家怎么使用递归的,在这点上想必有更多的大师在那,小生就不在这献丑了^-^。这次主要是给大家讲解一些我们在使用递归算法时可能没有考虑到的一些问题,在此之前,先看看下面的一个算法:

public long sum(int n){

              if(n==1){

                     return 1;

              }

              else

                     return sum(n-1)+n;

}

不用我说,想必大家也已经知道,这是一个用递归定义的求和算法,在语法结构上没有错误,反复测试也没有问题,但是如果你向这个式子中输入20000或者更大的数字时,你会发现,程序报错了,不是超出长整形的最大范围,因为120000的和才200010000,没有超过长整形的最大值。那么是什么呢?运行一下你会发现,是溢出栈空间,那这是为什么呢?教科书上一般并没有讲到这个问题。这是由于:

递归的实现,需要记录一些调用过程以跟踪挂起的递归调用,对于一条足够长的递归链,计算机会耗尽内存来记录递归调用,当它超出的时候,计算机就再也没有内存来记录递归链了。

很明显,不同的计算机内存空间是不一样的,那么他们可以记录的递归链的长度也不一样,我的计算机的记录递归链的最大长度为10464,你也可以试一下自己的。

其实这样的问题要解决也很简单,因为它可以用简单循环来实现,而且既然计算机要记录递归链,那么相对于简单循环来说,递归定义的算法,肯定要稍微耗时一点,不过相对于现代计算机这么快的计算速度,这点时间差异是可以忽略不计的,不过通过上例我们也大概知道了什么时候要少用递归,我们一般也默认为:如果可以用简单循环解决的问题尽量不要用递归。

再看一个大家同样非常熟悉的例子:Fibonacci数,它的定义规则是,从第三个数开始,任意一个数是前两个数字之和,从定义规则中就不难看出,这是一个通过递归定义的数,那么这样的数用递归算法来实现自然也在情理之中了,如下:

public long fibonacci(int n){

              if(n<=2){

                     return 1;

              }

              else

                     return fibonacci(n-1)+fibonacci(n-2);

}

       向其中输入一个100,然后我想你就可以先去喝杯茶了,回来之后你会发现,程序还在运行,而实际上,我们要计算出Fibonacci的第一百个数只要进行99次加法!这对于计算机来说是相当轻松的,但是为什么会出现这种局面了,原因在于:

       这个算法执行了冗余的计算,为了计算fibonacci(n),就要计算fibonacci(n-1)fibonacci(n-2),而实际上,在计算fibonacci(n-1)的过程中就已经计算过了fibonacci(n-2),因此对fibonacci(n-2)的再次调用其实是多此一举的,数值越大,这个情况就会越遭,对于n=40,F40=102334155,而总的递归次数却超过了300000000次,所以为了保证递归算法的执行效率,我们一定注意一条复利原则:永远不要在不同的递归中重复执行解决同一问题实例的工作。

       递归在实际的操作中运用非常广泛,不过我们在使用的时候也要注意场合,考虑程序的执行速度,以后再慢慢的跟大家一起探讨递归的巧妙用处。

1
1
分享到:
评论
6 楼 dt_flys 2013-05-02  
qswdit 写道
没看懂  为什么    return fibonacci(ret, ret, a);    而不是调用本身!

不好意思,写错了,应该是fibonacci_iter(ret, ret, a);
5 楼 qswdit 2013-05-02  
没看懂  为什么    return fibonacci(ret, ret, a);    而不是调用本身!
4 楼 cq520 2013-05-01  
dt_flys 写道
你用的是树形递归,可以转换成尾递归,比如:
public long fibonacci_iter(long ret, long a, long b, int n) {
    if (n == 1 || n == 2) {
        return 1;
    }
    ret = a + b;
    return fibonacci(ret, ret, a);
}

public long fibonacci(int n) {
    return fibonacci(1, 1, n);
}



即使是没有尾递归优化的语言也比树形递归要节约一般的资源, 不过可能你可能觉得代码还没for循环来的清晰。


之前没了解过尾递归,感谢提出
3 楼 dt_flys 2013-04-30  
dt_flys 写道
上面我代码写错了
应该是
public long fibonacci_iter(long ret, long a, long b, int n) {  
    if (n == 1 || n == 2) {  
        return ret;  
    }  
    ret = a + b;  
    return fibonacci(ret, ret, a);  
}  
  
public long fibonacci(int n) {  
    return fibonacci(1, 1, n);  
}  


public long fibonacci_iter(long ret, long a, long b, int n) {  
    if (n == 1 || n == 2) {  
        return ret;  
    }  
    ret = a + b;  
    return fibonacci(ret, ret, a);  
}  
  
public long fibonacci(int n) {  
    return fibonacci_iter(1, 1, n);  
}  
[
2 楼 dt_flys 2013-04-30  
上面我代码写错了
应该是
public long fibonacci_iter(long ret, long a, long b, int n) {  
    if (n == 1 || n == 2) {  
        return ret;  
    }  
    ret = a + b;  
    return fibonacci(ret, ret, a);  
}  
  
public long fibonacci(int n) {  
    return fibonacci(1, 1, n);  
}  
1 楼 dt_flys 2013-04-30  
你用的是树形递归,可以转换成尾递归,比如:
public long fibonacci_iter(long ret, long a, long b, int n) {
    if (n == 1 || n == 2) {
        return 1;
    }
    ret = a + b;
    return fibonacci(ret, ret, a);
}

public long fibonacci(int n) {
    return fibonacci(1, 1, n);
}



即使是没有尾递归优化的语言也比树形递归要节约一般的资源, 不过可能你可能觉得代码还没for循环来的清晰。

相关推荐

    C#递归 C#递归 C#递归

    根据给定的信息,本文将详细解释C#中的递归概念,并通过具体的代码示例来解析递归函数在构建树形结构中的应用。 ### C#递归基础 #### 什么是递归? 递归是一种编程技术,它允许一个方法或函数直接或间接地调用自身...

    熵的递归图分析_熵_熵递归_递归图分析_一维信号特征_递归图_

    本主题聚焦于熵的递归图分析,这是一种利用递归图来提取一维信号特征的方法,广泛应用于信号的分类、识别和特征提取。 首先,我们要理解“熵”的基本概念。熵在信息论中定义为一个随机变量的不确定性,通常用数学...

    .net 递归算法 .net 递归算法.net 递归算法

    在.NET编程环境中,递归算法是一种强大的工具,它允许函数或方法调用自身来解决复杂问题。递归的核心思想是将大问题分解为相同或相似的小问题,直到问题变得足够简单,可以直接得出答案。这种解决问题的方式在数据...

    递归算法与非递归转化

    递归算法与非递归转化 递归算法是把问题转化为规模缩小了的同类问题的子问题,然后递归调用函数(或过程)来表示问题的解。递归的效率一般不高,但是递归比较符合人类的思维方式。一般而言非递归算法更有效;但很多...

    ackermann函数的递归实现和非递归实现

    阿克曼函数是一种非常特殊的数学函数,常用于理论计算和计算机科学中,特别是在讨论递归算法和计算复杂性时。这个函数是由荷兰数学家格奥尔格·阿克曼在20世纪早期提出的,它展示了超越函数的概念,即无法用初等函数...

    可并行递归算法的递归多线程实现

    ### 可并行递归算法的递归多线程实现:深入解析 #### 引言:多线程与并行处理的重要性 随着计算任务日益复杂,传统的单线程编程模型已无法满足高效处理大规模数据的需求。多线程编程作为一种提高程序并发性和性能...

    abap简单递归算法

    ### ABAP简单递归算法解析 #### 一、引言 ABAP(Advanced Business Application Programming)是一种用于SAP系统的编程语言。它不仅支持传统的过程化编程,还支持面向对象编程和Web开发。本文将深入探讨一个ABAP中...

    acm递归算法总结竞赛

    在ACM(国际大学生程序设计竞赛)中,递归算法是一种常见的解决问题的方法,它通过函数自身调用自身来实现问题的解决。递归的核心在于找到基本情况(base case),即可以直接求解的问题,以及每次递归调用时问题规模...

    快速选择非递归与递归算法实现

    空间复杂度方面,非递归实现主要取决于分区操作和栈的使用,而递归实现则依赖于递归深度,一般情况下都是O(log n)。 在实际编程中,可以根据具体需求选择非递归或递归实现。非递归版本更适合内存有限或者递归深度...

    数据结构二叉树遍历递归,非递归

    在本主题中,我们将深入探讨二叉树的三种主要遍历方法:中序遍历、前序遍历和后序遍历,以及如何通过递归和非递归的方式实现这些遍历。 首先,让我们理解递归遍历的概念。递归是一种解决问题的方法,它将问题分解为...

    5.6 递归思想和递归函数1

    在编程领域,递归是一种强大的思想,它基于解决问题的子问题与原问题具有相同结构的特点。递归函数是实现递归思想的一种方式,通常在函数内部调用自身来解决复杂问题。本节将深入探讨递归思想和递归函数的概念,并...

    CRP.zip_CRP_CRP递归图_matlab 递归图_递归 MATLAB_递归分析

    在IT领域,递归是一种强大的编程技术,常用于解决复杂问题。递归图和递归分析是理解递归过程和优化算法效率的重要工具。本文将深入探讨这些概念,并结合MATLAB环境,阐述如何利用它们进行复杂的系统分析。 首先,...

    阿克曼函数 c程序 递归与非递归算法的综合

    阿克曼函数是一种非常特殊的数学函数,它在计算理论和计算机科学中被广泛用来探讨递归的概念。这个函数因其复杂的性质而闻名,特别是在其参数达到一定值时,增长速度极其迅速,以至于很快超出任何可计算的范围。在这...

    二叉树递归与非递归遍历

    递归遍历的优点在于其代码简洁明了,但缺点是当处理大规模的二叉树时,递归可能会导致栈溢出,因为每次递归调用都会增加栈的深度。 **非递归遍历** 1. **前序遍历**: - 使用一个栈来保存待访问的节点。 - 将根...

    递归算法到非递归算法的转换.ppt

    递归算法是编程中一种强大的工具,它通过调用自身来解决问题或简化复杂的问题。然而,在某些场景下,非递归算法可能更有利于性能优化或理解。本章将探讨如何将递归算法转换为非递归算法。 首先,我们要了解递归的...

    消除文法左递归

    在编译原理中,消除文法的左递归是一个重要的概念,主要应用于解析器的构造。这个过程是为了使解析过程更加高效,避免无限循环的发生。本文将深入探讨左递归的定义、为何需要消除以及如何消除,同时结合课程设计与...

    读懂C++递归程序

    C++递归程序的概念及其执行过程 递归是计算机科学中的一个核心概念,它是一种解决复杂问题的方法,通过将大问题分解为规模更小、更容易解决的小问题来实现。在程序设计中,递归允许程序调用自身来处理这些分解后的...

    递归实现n重循环

    使用递归实现N重循环,这里的N是不确定的。 此代码实现的功能描述如下: 1. 有一个字符串的矩阵,用vector&lt; vector&lt; CStirng &gt; &gt; 表示 2. 行与行之间进行排列组合 3. 输出所有组合的方式

    5!递归算法和非递归算法

    递归算法和非递归算法 在计算机科学与编程领域中,递归算法与非递归算法是两种非常重要的计算方法。本文将详细介绍这两种算法的特点、应用场景以及如何实现它们,特别针对初学者及面试准备者。 #### 递归算法 ...

Global site tag (gtag.js) - Google Analytics