Discuss / Python / 交作业,带解释,参考FC的解释

交作业,带解释,参考FC的解释

Topic source

郝仁E哥

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

汉诺塔思想笔记

认识汉诺塔的目标:把A柱子上的N个盘子移动到C柱子

递归的思想就是把这个目标分解成三个子目标

子目标1:将前n-1个盘子从a移动到b上

子目标2:将最底下的最后一个盘子从a移动到c上

子目标3:将b上的n-1个盘子移动到c上

然后每个子目标又是一次独立的汉诺塔游戏,也就可以继续分解目标直到N为1

def move(n, a, b, c): if n == 1: print(a, '-->', c) else: move(n-1, a, c, b) # 子目标1 move(1, a, b, c) # 子目标2 move(n-1, b, a, c) # 子目标3

move(3,'A','B','C')

大佬,我有一个问题哇,就是算法里面写的确实没问题啊,但是输出结果不是错的嘛,有三个盘子再A上,不是应该输出:A->B,A->B,A->C,B->C,B->C。这样才对嘛?为什么输出结果是A-C,A-B,C-B,A-C,B-A,B-C,A-C。你们都说没问题呢

郝仁E哥

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

@猫悟空被抢走了 你好,你可能对汉诺塔问题的移动规则不够了解,百度百科解释:

 汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,**在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘**。

针对你的这个问题:

  我的例子里有三个柱子,三个盘子,初始状态:三个盘子按小中大的顺序放置在A柱子上,我们每次只能移动1个盘子,所以刚开始应该先把小盘子先移到C柱子上(A->我的例子里有三个柱子,三个盘子,初始状态:三个盘子按小中大的顺序放置在A柱子上,我们每次只能移动1个盘子,所以刚开始应该先把小盘子先移到C柱子上(A->C),然后再把中盘子移到B柱子上(A->B),然后再把小盘子移到B盘子上(C->B),而不应该是你的认为输出:先移动小盘子到B上(A->B),再移中盘子到B盘子上(A->B),这时B柱子上的放置顺序违反了汉诺塔问题的规则,因为B柱子上的小盘子上面有中盘子,所以不能得出正确的移动方法。

后面的步骤也是一样的原理,希望能帮助你理解这个题。

嚜鹿

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

请教一下,当执行到这里的时候 else: move(n-1, a, c, b) # 子目标1 move(1, a, b, c) # 子目标2 move(n-1, b, a, c) # 子目标3 当n-1不等于1时,下一步是执行 move(n-1, a, c, b), 还是move(n-1, b, a, c),怎么看的?

老哥,我看了两天还是琢磨不过来,最后三行是怎么运行的啊?能细化一下吗,就是它的运行顺序是怎么样的。想不明白我快憋死了,,,

else: 1 move(n-1, a, c, b) # 子目标1 2 move(1, a, b, c) # 子目标2 3 move(n-1, b, a, c) # 子目标3 当n=3,执行1,然后下一步就变成了move(2,A,C,B),然后下一步是什么?是执行2呢还是继续执行move(2,A,C,B)?

大佬。第一个A——>C是由子目标1实现的还是子目标2实现的,这搞不明白,在执行完move(n-1,a,c,b)后下一步是继续执行这一个式子还是执行move(1,a,b,c)

有一个很通俗易懂的例子,把大象放到冰箱需要几步: 第一步:打开冰箱门(把n-1个先放到B柱上) 也就是move(n-1, a, c, b) 第二步:把大象装进去(把最大的一个放到C柱上) 也就是move(1, a, b, c) 第三步:关上冰箱门(把n-1)个放到C柱上也就是move(n-1,b,a,c) 你说的当n=3时 move(2,a,c,b)这个会继续向下递归直到n-1 = 1() move(1,a,b,c) move(2,b,a,c)同理这个会继续向下递归直到n-1 = 1 也就是move(n-1,a,c,b), move(n-1,b, a,c)会继续递归 希望对你有帮助

郝仁E哥

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

@我叫刘璇尊 @嚜鹿
else: move(n-1, a, c, b) # 子目标1 move(1, a, b, c) # 子目标2 move(n-1, b, a, c) # 子目标3

 先说明一下递归调用的函数参数的一些问题,否则容易混乱,我调用时使用move(3,'A','B','C'),而我的代码函数原型为move(n,a,b,c),此处的后3个形式参数为a,b,c,故容易混乱,因为在递归调用时,a,b,c的值根据传入的参数顺序而定,并不是对应'A','B','C'不变的,a,b,c只是形参的名字而已,可以把原型改为move(n,g1,g2,g3)也是可以的,这里需弄明白,在下面我会讲思路时举例解释清楚:

此处的执行是这样的:

 我的例子是3个盘子,当n=3时,执行子目标1:move(2,a,c,b),这里实际上是move(n=2,a='A',b='C',c='B')根据顺序一一对应,此时会进入子目标1的递归执行中去,在子目标1的递归执行中,你可以看到此时n=2,不等于1,所以进入else,再次进入新的子目标1:move(2-1,a,c,b)即move(1,a,c,b)=move(n=1,a=上级的a='A',b=上级的c='B',c=上级的b='C')的递归调用中,此时n终于等于1了,执行print(a,'->',c)所以第一次到达n=1时调用的是move(1,a='A',b='B','C'),,所以输出A->C,这里刚好a='A',b='B',c='C',其他情况下也许是不对应的,输出完就返回上一级的调用函数move(2,'A','C','B')中,刚执行完它的子目标1,,继续执行它的子目标2:move(1,a,b,c)=move(1,'A','C','B'),输出了A->B,在执行它的子目标3:move(2-1,b,a,c)=move(1,a=上一级的b='C',b=上一级的a='A',c=上一级的c='B'),所以输出C->B,返回上一级调用函数,至此,move(n=2,a='A',b='C',c='B')执行完毕,即move(3,'A','B','C')的子目标1执行完毕,返回上一级调用函数,继续它的其他子目标,原理差不多,不再赘述了,其实递归调用就是压栈出栈。

希望能帮助大家明白,这个用文字描述起来也是挺难的,码字好累,尽力了,画图会很好理解,可以拿纸和笔推算一下整个过程。

这方式挺厉害的,入栈和弹栈理清了,这问题就好理解了。


  • 1
  • 2

Reply