签到成功

知道了

CNDBA社区CNDBA社区

浅谈python中的递归调用问题

2018-01-16 14:09 3532 0 原创 学习

python 浅谈 递归函数
最近在自学一些python,找了些资料。自己慢慢研究到了递归函数这一章,碰到个很经典的例子。汉诺塔的移动。一开始尝试自己写的时候发现,这东西怎么可能写的出来。但是看到别人写出来以后发现,这东西真的能写出来。
本着借鉴的目的想去分析一下别人写的东西。觉得很有意思想给大家分享一下,如果有误请大家指正首先大家可以先自己想想如何能写出来。
先说一下:所谓的递归,我认为就是不断重复调用。直到return 出当前的递归循环。在我拆分的过程中,大家不妨先自己想一下结果,然后看一下我执行出来的结果,是否和各位所想的一样呢。
本文仅代表自己观点,如有错误大家指出。http://www.cndba.cn/549974293/article/2568

http://www.cndba.cn/549974293/article/2568
http://www.cndba.cn/549974293/article/2568http://www.cndba.cn/549974293/article/2568http://www.cndba.cn/549974293/article/2568http://www.cndba.cn/549974293/article/2568http://www.cndba.cn/549974293/article/2568http://www.cndba.cn/549974293/article/2568

`    --------------python 环境为3.6.4-------- 下面为高手写的代码`
            def move(n, a, b, c):
                if n == 1:
                    return print(a, '-->', c)
                move(n-1,a,c,b)
                move(1,a,b,c)
                move(n-1,b,a,c)
`    ----------------------------------------------`
        乍一看真的是一个很简单很简单的三层递归函数。我第一眼看到的时候都不认为这个是对的。但是真的执行以后发现。写的很有道理。输入2和3执行一下。发现都能出来。
    >>> move(2,'a','b','c')
    a --> b
    a --> c
    b --> c
    >>> move(3,'a','b','c')
    a --> c
    a --> b
    c --> b
    a --> c
    b --> a
    b --> c
    a --> c


    直接看,我的水平着实有限,所以打算拆开来一步步看。首先改写一下格式。我准备一个一个拆分来看。首先将第一个递归拆出来。带入几个值看一下
    def move(n, a, b, c):
        if n == 1:
            return print(a, '-->', c)
        move(n-1,a,c,b)    
    >>> move(2,'a','b','c')
    a --> b
    >>> move(3,'a','b','c')
    a --> c
    >>> move(4,'a','b','c')
    a --> b
    >>> move(5,'a','b','c')
    a --> c
    发现了一个很有意思的结果,就是在输入值(n>=2)且n为偶数的时候 是 a --> b,输入值为奇数的时候 是 a --> c。原因也很简单。在这个函数做递归的时候,因为n不等于1,所以递归函数一直在自己调用自己。所以字符 b和c的位置一直在互换。  

    继续拆分。
    def move(n, a, b, c):
        if n == 1:
            return print(a, '-->', c)
        move(1,a,c,b)
    >>> move(2,'a','b','c')
    a --> b
    >>> move(3,'a','b','c')
    a --> b
    >>> move(4,'a','b','c')
    a --> b
    因为,这个n值为定值1.所以无论n(n>=2)值输入的值为何值都只会输出 a --> b。

    最后拆分最后一个递归函数来看,
    def move(n, a, b, c):
        if n == 1:
            return print(a, '-->', c)
        move(n-1,b,a,c)
        根据之前的两个可以想象得出来,这个函数在n(n>=2)输入奇数和偶数的时候输出值一定不一样。这里不做多解释。
    >>> move(2,'a','b','c')
    b --> c
    >>> move(3,'a','b','c')
    a --> c
    >>> move(4,'a','b','c')
    b --> c
    >>> move(5,'a','b','c')
    a --> c



    接着如果两两结合呢。
    执行 n=2、3、4的结果分别为:
    def move(n, a, b, c):
        if n == 1:
            return print(a, '-->', c)
        move(n-1,a,c,b)
        move(1,a,b,c)
    >>> move(2,'a','b','c')
    a --> b
    a --> c
    >>> move(3,'a','b','c')
    a --> c
    a --> b
    a --> c
    >>> move(4,'a','b','c')
    a --> b
    a --> c
    a --> b
    a --> c
    一旦开始两两结合。立马开始变得复杂了,这里有如下几种可能:
    1 顺序执行,即传输一个值进去,会完整的走一次函数。
    如果传输值为2,执行move(2-1,a,c,b) return ,move1。输出结果为如上所示。
    如果传输值为3,则执行为,move(3-1,a,c,b) pass, move1, move(2-1,a,c,b) return, move(1,a,b,c)。 输出结果为如上所示
    如果传输值为4,则执行为,move(4-1,a,c,b) pass,move1,move(3-1,a,c,b),move1,move(2-1,a,c,b) return,move(1,a,b,c)。 这里很明显输出结果并不是这样的。所以并不是顺序执行这种。

    2 组内循环,即传输一个n值进去,在带有n值运算的条件中,一直循环调用到能return时再跳出当前循环。我认为这种方式应该是正确的。(句末带--为输出行,代码中abc为实际代表的abc)
    如输入的n为2,则
    move(2,a,b,c)
        move(1,a,c,b)--return跳出当前循环,abc值为上一层所变化的值
    move(1,a,b,c)--
    输出结果为: a-b a-c
    输入的n为3,则
    move(3,a,b,c)
        move(3-1,a,c,b)
            move(3-1-1,a,b,c)--return跳出当前循环,abc值为上一层所变化的值
        move(1,a,c,b)--
    move(1,a,b,c)    --
    输出结果为 : a-c a-b a-c 
    输入n为4,则
    move(4,a,b,c)
        move(4-1,a,c,b)
            move(4-1-1,a,b,c)
                move(4-1-1-1,a,c,b)-return跳出当前循环,abc值为上一层所变化的值
            move(1,a,b,c)--
        move(1,a,c,b)--
    move(1,a,b,c)--
    输出结果为:a-b a-c a-b a-c 
    这类型的递归类似于一个凹字。




    两两结合还有一种情况就是:这类的循环是先做一次 move1 然后做 move n 时会去调用move1。结果如下所示。
    def move(n, a, b, c):
        if n == 1:
            return print(a, '-->', c)
        move(1,a,b,c)
        move(n-1,a,c,b)
    执行 n=2、3、4的结果分别为:
    >>> move(2,'a','b','c')
    a --> c
    a --> b
    >>> move(3,'a','b','c')
    a --> c
    a --> b
    a --> c
    >>> move(4,'a','b','c')
    a --> c
    a --> b
    a --> c
    a --> b

    这种情况下如果n为2 
    move(2,a,b,c)
        move(1,a,b,c)--
        move(2-1,a,c,b)--
    输出结果为:a-c a-b
    n为3则
    move(3,a,b,c)
    move(1,a,b,c)--
        move(3-1,a,c,b)
            move(1,a,c,b)--
                move(3-1-1,a,b,c)--
    输出结果为 a-c a-b a-c 
    n为4则
    move(4,a,b,c)
        move(1,a,b,c)--
        move(4-1,a,c,b)
            move(1,a,c,b)--
                move(4-1-1,a,b,c)
                    move(1,a,b,c)--
                        move(4-1-1-1,a,c,b)--
    输出结果为:a-c a-b a-c a-b
    这类型的递归类似于一个不断增加的直线(语言很笨拙)


    两两结合最后一种情况如下所示:这类递归中,会先调用n-1,然后,后续函数在当前循环中使用n-1后的值。一组递归函数完成之后。重新调用第二组时,会重新初始化输入值。
    def move(n, a, b, c):
        if n == 1:
                return print(a, '-->', c)
        move(n-1,a,c,b)
        move(n-1,b,a,c)
    >>> move(2,'a','b','c')
    a --> b
    b --> c
    >>> move(3,'a','b','c')
    a --> c
    c --> b
    b --> a
    a --> c
    >>> move(4,'a','b','c')
    a --> b
    b --> c
    c --> a
    a --> b
    b --> c
    c --> a
    a --> b
    b --> c

    如果输入的n为2
    move(2,a,b,c)
        move(2-1,a,c,b)--循环1 
        move(2-1,b,a,c)--循环2 
    输出结果为: a-b a-c    
    如果输入的n为3
    move(3,a,b,c)
        move(3-1,a,c,b)
            move(3-1-1,a,b,c)--
        move(3-1-1,b,a,c)--循环1结束
        move(3-1,b,a,c)#2组循环开始循环2为2时
            move(3-1-1,b,c,a)--
        move(3-1-1,a,b,c)--
    输出结果为:a-c b-c b-a c-a
    如果输入的n为4
    move(4,a,b,c)
        move(4-1,a,c,b)
            move(4-1-1,a,b,c)
                move(4-1-1-1,a,c,b)--
            move(4-1-1-1,b,a,c)--
        move(4-1-1,c,a,b)
            move(4-1-1-1,c,b,a)--
        move(4-1-1-1,a,c,b)--至此循环1结束
        move(4-1,b,a,c)#2组循环开始循环2为3时
            move(4-1-1,b,c,a)
                move(4-1-1-1,b,a,c)--
            move(4-1-1-1,c,b,a)--
        move(4-1-1,a,b,c)#组循环开始循环2为2时
            move(4-1-1-1,a,c,b)--
        move(4-1-1-1,b,a,c)--
    输出结果为:a-b b-c c-a a-b b-c c-a a-b b-c 
    这类递归类似于波形。    



    个人认为我这种想法应该是对的,下面是我自己突发奇想想到的一个验证我的这个想法的代码。这个代码中可以观察n的各个阶段中各个值的实际值。
    def move(n, a, b, c):
        print(n,a, b, c)
        if n == 1:
            return print(a, '-->', c)
        move(n-1,a,c,b)
        move(1,a,b,c)
    这个代码可以在任意步骤显示,a,b,c中的值。可以参考。



    接下来就是三个递归函数的调用了。根据两个递归函数的调用来猜测一下。
    def move(n, a, b, c):
        if n == 1:
            return print(a, '-->', c)
        move(n-1,a,c,b)
        move(1,a,b,c)
        move(n-1,b,a,c)
    >>> move(2,'a','b','c')
    a --> b
    a --> c
    b --> c
    >>> move(3,'a','b','c')
    a --> c
    a --> b
    c --> b
    a --> c
    b --> a
    b --> c
    a --> c
    如果n的传输值为2时
    move(2,a,b,c)
        move(2-1,a,c,b)--
        move(1,a,b,c)--
        move(2-1,b,a,c)--

    如果n的传输值为3时
    move(3,a,b,c)
        move(3-1,a,c,b)
            move(3-1-1,a,b,c)--
        move(1,a,c,b)--
        move(3-1-1,c,a,b)--
    move(1,a,b,c)--
        move(3-1,b,a,c)
            move(3-1-1,b,c,a)--
        move(1,b,a,c)--
        move(3-1-1,a,b,c)--
    详细说一下这个吧:根据之前所说的,
    1.首先带入(3,a,b,c)进入递归调用,先走第一个小循环move(n-1,a,c,b),此时n=3。
    小循环中走n=3-1条件不满足,继续走小小循环n=3-1-1 return,得出结果,退出当前小小循环。
    用当前小循环中的值走(1,a,b,c)输出一个值,然后用当前的n=2走小循环中的move(n-1,b,a,c)。走完之后。第一个小循环整体走完。
    2.然后在整体递归函数中走move(1,a,b,c)。
    3.然后开始move(n-1,b,a,c)的递归调用,同1类似。

    move(4,a,b,c)
        move(4-1,a,c,b)
            move(4-1-1,a,b,c)
                move(4-1-1-1,a,c,b)--
            move(1,a,b,c)--
            move(4-1-1-1,b,a,c)--
        move(1,a,c,b)--
        move(4-1-1,c,a,b)
            move(4-1-1-1,c,b,a)--
        move(1,c,a,b)--
        move(4-1-1-1,a,c,b)--
    move(1,a,b,c)--
        move(4-1,b,a,c)
            move(4-1-1,b,c,a)
                move(4-1-1-1,b,a,c)--
            move(1,b,c,a)--
            move(4-1-1-1,c,b,a)--
        move(1,b,a,c)--
        move(4-1-1,a,b,c)
            move(4-1-1-1,a,c,b)--
        move(1,a,b,c)--
        move(4-1-1-1,b,a,c)--

以上是n=4时汉诺塔的具体执行步骤。我自己能写明白。讲就算了。大家自行理解吧。
写在最后,可能我的语言表述并不是特别好,但是步骤写的很清楚了。每一次的递归,递归调用递归,递归调用常函数。常函数调递归。以及各个阶段值的变换。
因为我也才接触python没多久。都是自己在摸索。各位如有问题请及时联系我。我再进行相应的修改。

http://www.cndba.cn/549974293/article/2568
http://www.cndba.cn/549974293/article/2568

版权声明:本文为博主原创文章,未经博主允许不得转载。

用户评论
* 以下用户言论只代表其个人观点,不代表CNDBA社区的观点或立场
啥都不懂的小白

啥都不懂的小白

关注
  • 10
    原创
  • 0
    翻译
  • 1
    转载
  • 11
    评论
  • 访问:40469次
  • 积分:52
  • 等级:注册会员
  • 排名:第40名
精华文章
    最新问题
    查看更多+
    热门文章
      热门用户
      推荐用户
        Copyright © 2016 All Rights Reserved. Powered by CNDBA · 皖ICP备2022006297号-1·

        QQ交流群

        注册联系QQ