精华内容
下载资源
问答
  • 递归优化

    2019-01-23 17:17:33
    递归优化 原因: 在 Java 中,每个线程都有独立的 Java 虚拟机栈。栈具有后入先出的特点,递归调用也是需要后调用的方法先返回,因此使用栈来存储递归调用的信息。这些信息存储在栈帧中,每个 Java 方法在执行时都会...

    递归优化

    原因:

    在 Java 中,每个线程都有独立的 Java 虚拟机栈。栈具有后入先出的特点,递归调用也是需要后调用的方法先返回,因此使用栈来存储递归调用的信息。这些信息存储在栈帧中,每个 Java 方法在执行时都会创建一个栈帧,用来存储局部变量表操作数栈常量池引用等信息。在调用方法时,对应着一个栈帧入栈,而方法返回时,对应着一个栈帧出栈。

    img

    随着栈帧frame的增多,将会导致Stack Overflow的报错,例如

    int f(int i)
    {
        if(i == 1 || i == 2)
            return 1;
        else
            return (f(i - 1) + f(i - 2));
    }
    

    解决方法1:递归–>非递归

    其实很简单,就是用一个临时变量,来保存中间的值,而不是压入堆栈中,

    //费波纳列数列,前两位是1,之后没位数是前两位数的和
    private static void fibonacci(int n) {
        int temp1=1,temp2=1,temp;
        for (int i = 1; i <=n ; i++) {
            temp=temp1+temp2;
            temp1=temp2;
            temp2=temp;
        }
        System.out.println();
    }
    //粘贴于网上
    

    解决办法2:递归–>尾递归

    尾递归就是当函数在最后一步(尾部)调用自身,如:

    function f(x){
      return g(x);
    }
    

    以下算法来自阮一峰教程:

    function factorial(n) {
      if (n === 1) return 1;
      return n * factorial(n - 1);
    }
    
    factorial(5) // 120
    

    该算法并非是尾递归,因为其在返回值的时候进行了一个乘法操作,所以还是普通的递归,复杂度为O(n),而如果改成尾递归,则:

    function factorial(n, total) {
      if (n === 1) return total;
      return factorial(n - 1, n * total);
    }
    
    factorial(5, 1) // 120
    

    该算法只需要计算

    factorial(5,1)

    factorial(4,5)

    factorial(3,20)

    factorial(2,60)

    factorial(1,120)

    在进入新的递归函数时,尾递归不再需要使用栈帧保存数据,允许抛弃旧的栈帧,那么只需要保存一个栈帧即可

    参考资料:

    展开全文
  • 主要介绍了Python递归及尾递归优化操作,结合实例形式分析了Python递归及尾递归优化相关概念、原理、应用与操作技巧,需要的朋友可以参考下
  • 什么又是尾递归优化?码农唐磊 程序猿石头今天,咱们来聊聊递归函数。为啥忽然想到递归?其实就从电影名字《恐怖游轮》《盗梦空间》想到了。图片java递归是啥?递归函数你们确定写过,学校上课的时候,估计最开始的...

    你们都知道递归,尾递归呢?什么又是尾递归优化?

    码农唐磊 程序猿石头

    38398e4298ea55c4310c8e42f8b5702f.png

    今天,咱们来聊聊递归函数。为啥忽然想到递归?其实就从电影名字《恐怖游轮》《盗梦空间》想到了。图片java

    递归是啥?

    递归函数你们确定写过,学校上课的时候,估计最开始的例子就是斐波拉契数列了吧。例如:面试

    int Fibonacci(n) {

    if (n < 2) return n;

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

    }

    递归函数简而言之就是在一个函数中,又“递归”调用本身。在写递归函数的时候,须要注意的地方就是递归函数的结束条件。用递归函数确实能简化不少算法的实现,好比常见的二叉树遍历等。但每每在写递归函数的时候,最容易出现的问题就是所谓的“栈溢出”。算法

    为何会有“栈溢出”呢?由于函数调用的过程,都要借助“栈”这种存储结构来保存运行时的一些状态,好比函数调用过程当中的变量拷贝,函数调用的地址等等。而“栈”每每存储空间是有限的,当超过其存储空间后,就会抛出著名的异常/错误“StackOverflowError”。ide

    咱们以一个简单的加法为例,例如:函数

    int sum(int n) {

    if (n <= 1) return n;

    return n + sum(n-1);

    }

    std::cout << sum(100) << std::endl;

    std::cout << sum(1000000) << std::endl;

    很简答,编译运行后,比较小的数字,能获得正确的答案,当数字扩大后,就会直接发生“segmentation fault”。性能

    尾递归又是啥?

    我得知这个概念,最开始仍是由于不少年前一次面试,面试官问我“你知道什么是尾递归吗?”,我觉得是“伪”递归,难道是假的递归???当初我也是懵逼状态(当初面试官忍住没笑也是厉害了)。从“尾”字可看出来即若函数在尾巴的地方递归调用本身。上面的例子写成尾递归,就变成了以下:优化

    int tailsum(int n, int sum) {

    if (n == 0) return sum;

    return tailsum(n-1, sum+n);

    }

    能够试试结果,计算从 1 加到 1000000,仍然是segmentation fault。为何呢?由于这种写法,本质上仍是有多层的函数嵌套调用,中间仍然有压栈、出栈等占用了存储空间(只不过能比前面的方法会省部分空间)。scala

    尾递归优化

    当你给编译选项开了优化以后,见证奇迹的时刻到了,竟然能算出正确结果。如图所示:

    b3487130dd718942505cd1361f03ae71.pngcode

    C++ 默认 segmentation fault, 开启编译优化后,能正常计算结果。blog

    缘由就是由于编译器帮助作了尾递归优化,能够打开汇编代码看看(这里就不展现 C++的了)。后面我用你们比较熟悉的 JVM based 语言 Scala 来阐述这个优化过程。(好像 Java 的编译器没作这方面的优化,至少我实验我本地 JDK8 是没有的,不清楚最新版本的有木有)(scala 自己提供了一个注解帮助编译器强制校验是否可以进行尾递归优化@tailrec)

    object TailRecObject {

    def tailSum(n: Int, sum: Int): Int = {

    if (n == 0) return sum;

    return tailSum(n-1, n+sum);

    }

    def main(args: Array[String]) {

    println(tailSum(100, 0))

    println(tailSum(1000000, 0))

    }

    }

    结果以下图所示,默认状况下 scalac 作了尾递归优化,可以正确计算出结果,当经过 -g:notailcalls 编译参数去掉尾递归优化后,就发生了 Exception in thread "main" java.lang.StackOverflowError了。

    7f005daf32fd507b3a8532cc604a8e54.png

    默认启用尾递归优化正常计算结果,禁用尾递归优化则“StackOverflow”。

    咱们来看看生成的字节码有什么不一样。

    640b1d6dc761d657059cddc84f3811cc.png

    包含尾递归优化的字节码,直接 goto 循环。

    c44486e4db593e3f05125ecd822c8f51.png

    禁用尾递归优化的字节码,方法调用。

    从上面能够看出,尾递归优化后,变成循环了(前面的 C++ 相似)。

    好了,尾递归我们就了解到这里。我的见解,咱们知道有“尾递归”这个点就行了,有时候咱们写递归就是为了方便,代码可读性好,若是确实是出于性能考虑,咱们能够本身用迭代的方式去实现,不依赖于具体的编译器实现。固然对于像 scala 这样,有一些语法糖可以帮助校验和验证,也是一个不错的选择。但递归转迭代的能力,咱们能具有岂不更好。

    下次想聊什么话题吗?欢迎留言。老规矩,若是有帮助(对你身边的其余人有帮助也行呀,一点帮助也没有的话应该也不会看到这里了吧),写篇文章真心不易,但愿亲多多帮忙“在看”,转发分享支持。

    展开全文
  • kotlin 尾递归优化 Kotlin编译器通过一些捕获来优化尾部递归调用 。 考虑一个rank函数来搜索排序数组中元素的索引,使用尾部递归及其测试通过以下方式实现: fun rank(k: Int, arr: Array<Int>): Int { ...

    kotlin 尾递归优化

    Kotlin编译器通过一些捕获来优化尾部递归调用 考虑一个rank函数来搜索排序数组中元素的索引,使用尾部递归及其测试通过以下方式实现:

    fun rank(k: Int, arr: Array<Int>): Int {
        tailrec fun rank(low: Int, high: Int): Int {
            if (low > high) {
                return -1
            }
            val mid = (low + high) / 2
    
            return when {
                (k < arr[mid]) -> rank(low, mid)
                (k > arr[mid]) -> rank(mid + 1, high)
                else -> mid
            }
        }
    
        return rank(0, arr.size - 1)
    }
    
    @Test
    fun rankTest() {
        val array = arrayOf(2, 4, 6, 9, 10, 11, 16, 17, 19, 20, 25)
        assertEquals(-1, rank(100, array))
        assertEquals(0, rank(2, array))
        assertEquals(2, rank(6, array))
        assertEquals(5, rank(11, array))
        assertEquals(10, rank(25, array))
    }

    IntelliJ提供了一个很棒的功能,可以按照以下屏幕截图显示任何Kotlin代码的字节码:

    与Kotlin编译器生成的字节码类型等效的Kotlin如下:

    fun rankIter(k: Int, arr: Array<Int>): Int {
        fun rankIter(low: Int, high: Int): Int {
            var lo = low
            var hi = high
            while (lo <= hi) {
                val mid = (lo + hi)/2
    
                if (k < arr[mid]) {
                    hi = mid
                } else if (k > arr[mid]){
                    lo = mid + 1
                } else {
                    return mid
                }
    
            }
            return -1
        }
    
        return rankIter(0, arr.size - 1)
    }

    尾部调用已转换为简单循环。

    我可以看到一些收获:

    1.必须使用“ tailrec”修饰符明确告知编译器哪些调用是尾递归的

    2.在非尾递归函数中添加tailrec修饰符不会生成编译器错误,尽管在编译步骤中确实会出现警告

    翻译自: https://www.javacodegeeks.com/2017/12/kotlin-tail-recursion-optimization.html

    kotlin 尾递归优化

    展开全文
  • 递归优化之尾递归

    千次阅读 2015-09-13 18:28:14
    采用递归算法和尾递归算法解决斐波那契问题,并分析造成两者计算时间差距之大的原因。最后,以一个例子,说明函数式语言的尾递归优化

    一个函数直接或间接调用自己本身,这种函数即为递归函数。

    递归算法能够以一种优雅的思考方式简化问题,但由于递归通常是通过堆栈来实现的,一直背负着效率低的“臭名”。

    以计算斐波那契数列为例,程序代码如下

    	/**
    	 *  采用递归方式计算斐波那契数列
    	 */
    	public  static long recursiveFib(long n)
    	{
    		if(n<0)		return -1;
    		if(n <= 1)	return n;
    		
    		return recursiveFib(n-1)+recursiveFib(n-2);
    	}
    代码看起来很优雅简洁,但其运行效率奇低,运行测试代码
    	public static void main(String[] args) {
    		final int num = 50;
    		long begin = System.currentTimeMillis();
    		System.err.println("采用递归算法");
    		long result = recursiveFib(num);
    		long end = System.currentTimeMillis();
    		System.err.println("fib("+num+")=="+result+",耗时"+(end-begin)+"毫秒");
    	}

    运行结果如下


    仔细思考一下该方法的计算过程,很容易知道问题所在,以计算fib(5)为例,其计算过程如下



    从上图可以看出,子问题的答案被重复计算。只要输入的参数稍微大点(例如100),程序就会因创建过多的堆栈而挂掉。

    采用尾递归算法,可以极大地提高运行效率

    如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归。需要说明的是,只有当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这样的递归调用才是尾递归。

    采用尾递归方式计算斐波那契数列,代码如下

    /**
    	 *  采用尾递归方式计算斐波那契数列
    	 */
    	public  static long tailRecursiveFib(long a,long b,int n)
    	{
    		if(n <0 ) return -1;
    		if(n == 0) return a;
    		if(n == 1) return b;
    		
    		//返回值出现在函数的末尾,且没有包含其他表达式,是尾递归!
    		return tailRecursiveFib(b, a+b, n-1);  
    	}
    尾递归测试代码
    	public static void main(String[] args) {
    		final int num = 50;
    		long begin = System.currentTimeMillis();
    		System.err.println("采用尾递归算法");
    		long result = tailRecursiveFib(0L,1L,num);
    		long end = System.currentTimeMillis();
    		System.err.println("fib("+num+")=="+result+",耗时"+(end-begin)+"毫秒");
    	}
    代码运行结果如下



    单从理论上比较两种算法,可以知道,尾递归至少有两点是比普通递归优秀的

    1.尾递归通过迭代的方式,不存在子问题被多次计算的情况

    2.尾递归的调用发生在方法的末尾,在计算过程中,完全可以把上一次留在堆栈的状态擦掉,保证程序以O(1)的空间复杂度运行。

    遗憾的是,关于第二点,JVM并没有作出优化(运行上述尾递归函数,参数为10000,程序也会挂掉!!)

    在函数式编程语言的世界里,充斥着大量的递归调用。在大部分函数式语言环境里,尾递归都是经过优化的。

    下面采用Scheme-DrRacket(Lisp的一种方言)来展示尾递归的魅力,代码如下(语法有点奇怪,但不难理解)

    (define (fib a b n)
      (cond
        [(< n 0) -1]
        [(= n 0) a]
        [(= n 1) b]
        [else (fib b (+ a b) (- n 1))])
    )
    示例运行结果如下(计算时间几乎可以忽略不计,请不要惊讶于计算结果如此之长)



    展开全文
  • 递归优化

    2019-04-27 22:28:11
    1.需要递归优化的函数没有用timeout等异步队列进行递归调用函数自己 2.需要递归优化的递归函数的返回值不是每次都返回,而是条件性返回 尾递归优化后的递归demo /** * @method 测试尾递归demo方法 尾递归适合的...
  • 目录递归递归与循环抉择汉罗塔(hanoi)问题尾递归尾递归优化代码示例 递归 每个递归函数都有两部分: 1. 基线条件 一个或多个结束条件(if),不再调用函数自身,从而避免无限递归 2. 递归条件 调用函数自身func() ...
  • Kotlin尾递归优化

    千次阅读 2018-04-03 00:33:19
    一、尾递归优化 1.递归的一种特殊形式 2.调用自身后无其他的操作 3.tailrec关键字提示编译器尾递归优化 二、具体的来看看一下代码说明 package net.println.kotlin.chapter5.tailrecursive /** * @author:...
  • 编者荐语:本文旨在帮助大家掌握递归的性能优化方案——尾递归优化,以及如何对下列函数用尾递归进行优化?参考摘录:阮一峰-尾调用优化引子:这道题是作者在面字节跳动某部门时候印象比较深刻的一道...
  • 文章目录Kotlin递归与尾递归递归概念用途运用缺点尾递归优化概念 递归 概念 简单的来说递归就是一个函数直接或间接地调用自身,是为直接或间接递归。 用途 数据的定义是按递归定义的。(Fibonacci函数,n的阶乘) ...
  • 递归、递归算法的非递归优化 2019年07月10日 09:58:01最后一滴血的Lanbo阅读数 20 就以斐波那契数列为例,当时面试时问到了,很简单的题,问转化为非递归如何做,居然一时紧张没回答上来,太丢人了 using System...
  • 递归优化流程解析

    万次阅读 2019-02-11 16:02:29
    递归优化流程解析 function tco(f) { var value; var active = false; var accumulated = []; return function accumulator() { accumulated.push(arguments); if (!active) { active = true; ...
  • Python尾递归优化

    2019-09-27 15:43:54
    Python开启尾递归优化 cpython本身不支持尾递归优化, 但是一个牛人想出的解决办法:实现一个 tail_call_optimized 装饰器 #!/usr/bin/env python2.4 # This program shows off a python decorator( # which ...
  • 前面了我学习了Kotlin的递归,那么我还接触到了Kotlin的尾递归优化。什么是尾递归优化呢?带着疑问更好去学习。 尾递归 1.尾递归是递归的一种特殊形式; 2.调用自身无其他操作; 3.tailrec关键字提示编译器尾...
  • groovy伪递归优化

    2019-09-14 15:47:03
    groovy伪递归优化 1、官网文档闭包这一章介绍了使用trampoline进行伪递归优化 def factorial factorial = { int n, def accu = 1G -> if (n < 2) return accu factorial.trampoline(n - 1, n * accu) ...
  • python递归函数的尾递归优化 参考:廖雪峰python教程 递归函数:函数内部调用自身来完成计算。 比如我们要计算阶乘n! = 1 x 2 x 3 x ... x n,用函数function1来表示,可采用如下写法。 def function1(n): if n==1: ...
  • 本文实例讲述了Python递归及尾递归优化操作。分享给大家供大家参考,具体如下:1、递归介绍递归简而言之就是自己调用自己。使用递归解决问题的核心就是分析出递归的模型,看这个问题能拆分出和自己类似的问题并且有...
  • 斐波那契数列递归优化算法 参考了另一个帖子:link 优化之前的代码,和非递归代码都好理解。而递归优化代码中,n只是起计数作用,递归用于循环: /** * 优化后递归,假设递归层次为 i * @param a fib(i)的值 * @...
  • Python尾递归优化一般递归与尾递归一般递归:def normal_recursion(n):if n == 1:return 1else:return n + normal_recursion(n-1)执行:normal_recursion(5)5 + normal_recursion(4)5 + 4 + normal_recursion(3)5 + 4...
  • python尾递归优化

    2020-06-05 15:47:07
    python尾递归优化,摆脱递归层级问题 def demo_recursive(n): if n == 10000: return else: print(n) n += 1 return demo_recursive(n) demo_recursive(0) 这是一个简单的尾递归函数示例,如不设置最大递归...
  • scala 尾递归优化

    2019-11-09 20:55:51
    scala 尾递归优化 1. 递归 1.1 递归的定义  一个函数直接或间接的调用它自己本身,就是递归。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的代码就可以执行多次...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 186,011
精华内容 74,404
关键字:

递归的优化