Discuss / Python / 递归函数的一般方式

递归函数的一般方式

Topic source

先帮着大家了解一下递归函数的一般形式,这样有利于解决本题和其他的递归问题。

首先,计算机计算递归的方式是,计算递归函数func(n)时发现要先计算func(n-1),计算func(n-1)时需要先计算func(n-2)......以此类推,最后在计算func(1)的时候,计算机终于得到了一个答案。然后计算机再把func(1)得到的答案带入到func(2)中,得到func(2)结果带入func(3)中,以此类推,不断地带入最后得到func(n)的结果。那么func(1)的那个答案,就是递归的基础。

其次,再来看看一般形式:

def func(n):

    if n == 1:  #也有可能是n==0,具体问题具体分析

        return xxx xxx xxx

    else:

        return yyy func(n-1) yyy  #也有可能用到func(n-2)甚至更多,具体问题具体分析

上面这个一般形式由三部分组成:

1、return xxx xxx xxx 这里是递归的基础。计算机终于在func(1)时得到了一个答案。也就是func(1) = xxx xxx xxx

2、return yyy func(n-1) yyy 这里是递归的规律。表明func(n)和func(n-1)的关系,如何操纵func(n-1)得到func(n)。也就是func(n) = yyy func(n-1) yyy

3、通过if else 分支语句区分什么是递归的基础,什么是递归的规律

有了这个一般形式,现在就可以看看如何把老师的问题套入到这个一般形式中。

1、先设定这个递归函数 def move(n, a, b, c),n是汉诺塔上盘子的数量。特别需要注意,a表面上看是代表第一个柱子,但同时,a所在的是第二个参数位置,这个位置的参数表示的是“起始的柱子”。b表示第二个柱子,但这第三个参数位置其实表示的是“辅助的柱子”。c表示第三个柱子,但这第四个参数表示的是最终要挪动到的“终点柱子”。

那么move(n, a, b, c)的意思是,把“起始柱子“上n个圆盘,通过”辅助柱子“,挪动到最终的”终点柱子”上。因为最开始,所有的圆盘都在a柱子上,所以才把a写在了第二个参数起始柱子的位置,b是辅助的柱子,所以写在第三个参数位置。c是终点柱子,所以写在最后的参数位置上。如果给你的汉诺塔,所有的圆盘都在中间那个b柱子上,要通过a柱子的帮助,挪动到c柱子上的话,函数就要写成 move(n, b, a, c) 了。

2、这个递归问题的基础是什么:基础是,如果汉诺塔上只有一个盘子。也就是if n==1 的时候程序应该做什么。由于本问题是输出搬运过程,而不是得到一个返回值,所以不用写return,直接写成 if n == 1: print(a '-->' c)    它表示,当只有一个圆盘时,那就直接把这个圆盘从”起始柱子“搬运到”终点柱子“,也就是把在a上的圆盘,要搬到c上。

3、这个递归问题的规律是什么:思考下n个圆盘和n-1个圆盘之间的关系是什么。n个和n-1个差的就是下面最大的那个圆盘。需要注意:不是表面上move(n, a, b, c)和move(n-1, a, b, c)的关系。 虽然得到move(n, ......)要先做move(n-1, ......),但如果move(n, a, b, c)的前一步如果是 move(n-1, a, b, c),那也就是a上面n-1个圆盘已经到了c上,那a上剩下最大的那个圆盘还是不能挪动到c上。我们实际要做的是:先把n-1个圆盘挪动到b柱子上,再把a上最后的大圆盘挪动到c上,最后把b上的n-1个圆盘挪动到c上。

4、那么如何:“先把n-1个圆盘挪动到b柱子上”。这时就要相信def move(n, a, b, c) 就像一个神奇的咒语。虽然还没定义完这个函数,但它已经有了“生命”可以实现我们的愿望了。也就是调用它就可以实现汉诺塔搬移。这时,要把a上的n-1个圆盘,通过c柱子的帮助,挪动到b柱子上。起始柱子是a,辅助柱子是c,终点柱子是b。这样就可以调用递归函数 move(n-1, a, c, b)

5、下一步是:“再把a上最后的大圆盘挪动到c上”,我们可以写成 print(a '-->' c)。同样也可以写成 move(1, a, b, c) 因为 n==1得到的基础结果或答案就是print(a '-->' c)

6、最后一步是:“最后把b上的n-1个圆盘挪动到c上”。还是使用magic word。 move(n-1, b, a, c)   表示,这时n-1个圆盘在b上,通过a柱子的帮忙,挪动到c上。

完整的函数就可以写出来了:

def move(n, a, b, c):

    if n == 1:

        print(a '-->' c)

    else:

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

        print(a '-->' c)  #或者写成 move(1, a, b, c)

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

最后再唠叨两句(我比较墨迹):

1、不要被参数叫a、b、c这些名字所迷惑。要知道这个参数位置真正表示的是什么意思。第一个参数表示是圆盘数量,第二个参数表示“起始柱子”,第三个参数表示“辅助柱子”,第四个参数表示“终点柱子”。也就是从哪个柱子开始搬运,哪个柱子名字就放在第二个参数位置。谁是辅助,谁的名字就在第三个参数位置上。谁是终点,谁的名字就在第四个参数位置上。

2、递归函数要有递归的基础,递归的规律,并且通过分支结构(if...else...)把基础和规律分开。

3、递归基础的语句是要给个确定的结果。一般递归是n == 1 或者 n==0开始的。也就是if n == 1 : xxx 或者 if n==0: xxx  甚至有可能是if n== 0 or 1: xxx。有的递归可能有多个基础(比如斐波那契数列)

4、递归规律的语句要体现func(n)和func(n-1)的关系,也就是 else :yyy func(n-1) yyy。或者说,else语句要用到你定义的这个函数本身func(n-1)才能实现递归。(当然可能有时候不光要用n-1甚至还会用到n-2或者更多)

5、考查一下自己是否真的完全理解的话,可以这样试试,还是这个递归函数 def move(n, a, c, b) 第一个参数是圆盘数量,第二个参数还是“起始柱子”,第三个参数是“终点柱子”,第四个参数是“辅助柱子”。要如何修改这个函数才能得到相同的答案呢~~~

我把这个回答也粘贴到了zhihu同名问题下:https://www.zhihu.com/question/37152936/answer/728940730

解答的真好。

暗影小X

#3 Created at ... [Delete] [Delete and Lock User]

6666非常好

SuperLove_Q

#4 Created at ... [Delete] [Delete and Lock User]
def move(n, a, c, b):    if n == 1:        print(a, '-->', c)    else:        move(n-1, a, b, c)        move(1, a, c, b)        move(n-1, b, c, a)move(3, 'A', 'C', 'B')

突然让我想起了,把大象装进冰箱那三步


  • 1

Reply