zoukankan      html  css  js  c++  java
  • 递 归

    递归的定义:在数学和计算机科学中,递归是指在函数的定义中调用函数自身的方法。

    递归举例:斐波那契数列。

    >>> def fib(n):
    ...     '''This is Fibonacci by Recursion'''
    ...     if n == 0:    # 函数结束的条件
    ...         return 0
    ...     elif n == 1:
    ...         return 1
    ...     else:
    ...         return fib(n-1) + fib(n-2)    # 递归,定义函数的内部调用函数自身
    ...
    >>> fib(10)
    55

      从上面的计算过程可以看出,每个递归的过程都是向着最初的已知条件(结束条件)方向挺近一步,直到通过这个最底层的条件得到结果,然后再一层一层向上回馈(返回)计算结果。

           将上面的例子优化:

    >>> m = {0:0, 1:1}    # n=1和n=0的值已知,直接定义出来,省去每次的多个判断
    >>> def fib(n):
    ...     if not n in m:
    ...         m[n] = fib(n-1) + fib(n-2)
    ...     return m[n]
    ...
    >>> fib(10)
    55

      递归的要素:

                  结束条件:满足条件时,函数调用结束,直接返回一个值,即有一个明确的递归结束条件,称为递归出口。

                  递归调用:包含一个或多个调用,这些调用旨在解决问题的一部分。

             这里的关键是,通过一次次的分解问题,将问题分解为较小的部分,最终分解到结束条件。

      递归的回溯与递推:

        递推:像上边递归实现所拆解,递归每一次都是基于上一次进行下一次的执行,这叫递推;

        回溯:则是在遇到终止条件,则从最后往回返,一级一级的把值返回,这叫回溯。

    >>> lst =[1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15,[16,[17,]],19]]]]]]]
    >>> def search(lst):
    ...     for item in lst:
    ...         if type(item) is list:
    ...             search(item)
    ...         else:    # 递归调用结束条件
    ...             print(item, end=' ')    
    ...
    >>> search(lst)
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19    

      深刻理解:每次递归调用函数时,都将为这个递归调用的函数创建一个新的命名空间。各自的命名空间是相互独立的,每个递归函数在各自的命名空间内生存并依次按顺序执行;当执行到结束条件时,从结束的位置开始向前一次次的反馈(返回)并继续执行每个命名空间内剩余的部分。关键在于每次递归时都会创建各自相互独立的命名空间。

      两个经典案例:阶乘和幂

        阶乘:1的阶乘为1;

                对于大于1的数字n,其阶乘为n-1的阶乘再乘以n。

    >>> def func(n):
    ...     if n == 1:
    ...         return 1
    ...     return n * func(n-1)
    ...
    >>> func(5)
    120

      幂:对于任何数字x,power(x, 0)都为1;

        n>0时,power(x, n)为power(x, n-1)与x的乘积。

    >>> def func(n, m):
    ...     if m == 0:
    ...         return 1
    ...     if m < 0:
    ...         return (1/n) * func(n, m+1)
    ...     else:
    ...         return n * func(n, m-1)
    ...
    >>> func(3, 3)
    27
    >>> func(3, 1)
    3
    >>> func(3, 0)
    1
    >>> func(3, -1)
    0.3333333333333333
    >>> func(3, -2)
    0.1111111111111111
    >>> func(3, -3)
    0.037037037037037035

    递归的特点:

             必须有一个明确的结束条件;

             每次进入更深一层递归时,问题的规模相比上次递归都应有所减少;

             递归效率不高,递归层次太多,会导致栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的。每当进入一个函数调用,栈就会增加一层栈帧;每当函数返回,栈就会减少一层栈帧。由于栈的大小不是无限的,所以递归调用次数过多,会导致栈溢出。

    递归的优缺点:

      优点:

        递归使代码看起来更加整洁、优雅;

        可以用递归将复杂任务分解成更简单的子问题;

        使用递归比使用一些嵌套迭代更容易。

      缺点:

        递归的逻辑很难调试、跟进;

        递归调用的代价高昂(效率低),因为占用了大量的内存和时间。

      注意:Python3默认递归的深度不能超过997层。

      尾递归调用优化:

        在函数的最后一步进入递归调用。这样每层函数的调用就直接结束了,不会去等待下一次递归调用的返回值,这样每次执行完毕进入下一次后,就不会占用内存去保存当前层的状态,节省内存空间。

  • 相关阅读:
    CRM PrincipalObjectAccess(POA)
    crmForm.SubmitCRMForm
    transactionCurrencyId needs to be supplied to format a transaction money field.
    GitLab 之 Linux十分钟快装
    GitLab 之 Linux十分钟快装
    秒杀系统架构分析与实战
    秒杀系统架构分析与实战
    秒杀系统架构分析与实战
    创建微服务?请先回答这10个问题
    创建微服务?请先回答这10个问题
  • 原文地址:https://www.cnblogs.com/wgbo/p/9670842.html
Copyright © 2011-2022 走看看