理解递归

在初学递归的时候, 看到一个递归实现, 咱们老是不免陷入不停的回溯验证之中, 由于回溯就像反过来思考迭代, 这是咱们习惯的思惟方式, 可是实际上递归不须要这样来验证. 好比, 另一个常见的例子是阶乘的计算. 阶乘的定义: “一个正整数的阶乘(英语: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)
}
相关文章
相关标签/搜索