zoukankan      html  css  js  c++  java
  • 理解递归

    在初学递归的时候, 看到一个递归实现, 我们总是难免陷入不停的回溯验证之中, 因为回溯就像反过来思考迭代, 这是我们习惯的思维方式, 但是实际上递归不需要这样来验证. 比如, 另外一个常见的例子是阶乘的计算. 阶乘的定义: “一个正整数的阶乘(英语:factorial)是所有小于或等于该数的正整数的积,并且0的阶乘为1。” 以下是Ruby的实现:

    int factorial(n) 
      if (n <= 1) 
        return 1;
      else
        return n * factorial(n - 1);
    

    我们怎么判断这个阶乘的递归计算是否是正确的呢? 先别说测试, 我说我们读代码的时候怎么判断呢?
    回溯的思考方式是这么验证的, 比如当n = 4时, 那么factoria(4)等于4 * factoria(3), 而factoria(3)等于3 * factoria(2), factoria(2)等于2 * factoria(1), 等于2 * 1, 所以factoria(4)等于4 * 3 * 2 * 1. 这个结果正好等于阶乘4的迭代定义.
    用回溯的方式思考虽然可以验证当n = 某个较小数值是否正确, 但是其实无益于理解.
    Paul Graham提到一种方法, 给我很大启发, 该方法如下:

    1. 当n=0, 1的时候, 结果正确.
    2. 假设函数对于n是正确的, 函数对n+1结果也正确.
      如果这两点是成立的,我们知道这个函数对于所有可能的n都是正确的。

    这种方法很像数学归纳法, 也是递归正确的思考方式, 事实上, 阶乘的递归表达方式就是1!=1,n!=(n-1)!×n. 当程序实现符合算法描述的时候, 程序自然对了, 假如还不对, 那是算法本身错了…… 相对来说, n,n+1的情况为通用情况, 虽然比较复杂, 但是还能理解, 最重要的, 也是最容易被新手忽略的问题在于第1点, 也就是基本用例(base case)要对. 比如, 上例中, 我们去掉if n <= 1的判断后, 代码会进入死循环, 永远不会结束.

    使用尾递归

    我们常见的递归函数比如Fabpnpcci函数可以通过尾递归来进行优化,尾递归能进行优化的原因是编译器会自动识别尾递归并且为其优化。 现在我们来看一下两个例子

    正常递归版阶乘法
    int f(const int n){
        if(n<1)return 0;
        if(n==1)return 1;
        else return f(n-1)*n;
    }
    
    尾递归版本阶乘
    int f(const int n,const int res){
        if(n==1||n==2)return 1;
        else return f(n-1,res+n)
    }
    
  • 相关阅读:
    linux 命令学习
    反编译学习一:Mac下使用dex2jar
    如何删除你的MacOS上的旧版本Java
    使用screen 遇到的多窗口
    dede简略标题调用标签
    JQ实现导航滚动到指定位置变色,并置顶
    JQ实现当前页面导航加效果(栏目页有效)
    wordpress首页调用指定分类下的文章
    作业1#python用列表实现多用户登录,并有三次机会
    python数据类型之间的转换
  • 原文地址:https://www.cnblogs.com/adamwong/p/10534296.html
Copyright © 2011-2022 走看看