汉诺塔问题以及递归

来源:互联网 发布:魅族6手机备份数据 编辑:程序博客网 时间:2024/06/10 06:09

汉诺塔问题

我最初接触汉诺塔问题大概是在高三的时候,那个时候的数学试卷出了这么一道题目,记得当初觉得这道题目很有趣,自己翻前覆后想了很多,但是终究还是没有想出个所以然来,或许在那个时候,这道题目在老师和学霸的眼里就是那么简单,不过对于那个时候的我来说,还是很有难度的一道题,当然今天不是,今天几行代码就搞定了。

问题的描述

现在有三根连在一起的柱子,为了方便,我们标记他们为A,B,C。A柱子上从上到下按照金字塔状叠放着n个不同大小的圆盘,现在我们想做这么一件事情,我们要讲A柱子上的圆盘全都移动的C柱子上去,但是我们有这么一个限制:任何大的圆盘永远都不能放置在小的圆盘上,现在我们要知道我们要怎么移动圆盘,请编程写出移动的轨迹。
汉诺塔问题

思路

我们需要倒过来考虑这道问题。我们要怎么办呢?首先,第一个从A柱子移动倒C柱子的圆盘必然是A柱子的最后一个圆盘,然后A柱子的前n-1个圆盘必然都按照从上倒下的金字塔形状叠在B柱子上。我们将A柱子的n个圆盘从A移动倒C之后,问题的规模就缩小了,问题变成了将B柱子上的n-1个圆盘借助A移动倒C柱子上。依次类推。当问题的规模缩小倒1的时候,整个问题就已经解决了。

move(n, a, b, c)表示a柱子上有n个圆盘,要借助b柱子从a柱子移动n个圆盘到c柱子,如果n == 1,我们直接输出a-->c,表示将圆盘直接从a柱子移动倒c柱子,如果n > 1,我们这么干:先将前面的n - 1个圆盘从a柱子移动到b柱子,然后将第n个圆盘从a柱子移动到c柱子,即先调用move(n -1, a, c, b)然后调用move(1, a, b, c),最后调用move(n - 1, b, a, c)

一个例子

前面的思路其实就是递归了,我曾经不太敢相信递归,认为递归这东西有点玄乎,我怎么确定最后这个程序一定会终止,我怎么确定程序一定会对?递归的思想有点类似于管理,总经理管一堆经理,经理管一堆员工,员工管理一堆临时工,有些事情总经理不需要亲力亲为,事实上他要是这么干就坏菜了,一个人怎么顶起公司那么多大大小小的事情,比如说总经理今年有一个目标,今年全年的业绩要达到5千万,总经理要做的就是给经理下命令,小李这个部门今年业绩要达到1千万,小花这个部门要达到500万,等等。总经理只需要按照自己对部门的了解来下达自己的命令即可,他的估计里或者说他相信自己给各个部门定下的目标一定回实现。

经理们也干不了这么多活,他们一样的根据自己的预估来分派任务,每个员工应该搞定多少业绩,他们确信自己定下的目标一定可以实现,然后这样一层一层分派下去。

到了最底层的临时工,他们分配的业绩一定在他们能够承受的范围之内,也就是说虽然任务繁重,咬咬牙还是可以完成的,好了,他们一旦完成了任务,那么相应的员工的任务完成了,经理的任务完成了,总经理的任务也完成了,这其实就是递归不是么?

关于递归函数

从上面的例子,我们可以看到,从上到下,任务量是在减小的,也就是逐渐达到一个人可以接受的地步的,然后公司的层次也是有限的,没有无限的分派任务下去,最后,其实大部分人干的事情都是相同的,只有最底层的人干的事情有所不同,这其实也反映了递归的三大条件,第一是一定要有递归出口,也就是要有下限,最后的小事一定要有人来办,也就是说公司不能只有管理层,要有干实事的人。第二就是向下派发的任务应该是逐渐减小的,如果没有减小,说明你这个人没干什么事,你的存在就没有多么大的意义了,第三,就是很多人干的事情应该是一致的,这也是为什么要递归的主要原因,干的事不一样就应该调用别的函数了,而不是函数本身了。

总结来说:相同的事是递归,然后不同的事就是递归出口了,然后问题的规模在逐渐减小。自己好好体会吧!

再谈汉诺塔

我们反过头来看这道题目,我们的任务是将n个圆盘从A柱子移动倒C柱子,总经理布置任务了,小李,你将前n - 1个圆盘从A借助C移动到B,然后寡人将第n个圆盘从A移动到C,最后小花,你将n - 1个圆盘从B借助A移动到C,这件事情不就解决了吗?寡人真是太聪明了。

然后经理们拿到自己分配的任务也开始琢磨了,他们发现领导还是很有水平的,因为自己的问题也可以按照领导的方法来解决啊,小李同志发现,自己先命令一个员工小猪,将n -2个圆盘从A借助B倒C,然后自己将第n-1个圆盘从A移动到B,最后自己再命令小狗将n - 2个圆盘从C移动到B,这样自己的问题不就解决了吗?

就这样一层一层下来,n最终会到1,然而这对于最底层的人来说非常简单了,难道说A柱子上一个圆盘,要你借助B到C还有困难吗?这个人解决了问题,他就不会递归下去,递归就有了底线,他的完成会帮助他上面的人完成一部分工作,同样的如果他上面的人完成了自己的工作,然后他的上面的上面的人也会完成一部分工作,一层一层上去,最终所有的任务都完成了。

小结

我并不是闲到蛋疼来写一个很小很小的递归函数,只是自己曾经被这道题目伤害过,算是填补一下自己受伤过的心吧!

代码

代码其实用处不大,重要的是思想。我给出python的代码供参考:

#!/usr/bin/env python3def move(n, a, b, c):#n表示a中含有的盘子的数目,我们的目的是借助盘子c将a中的盘子移动到b#要求采用递归的算法来解决这个问题#参数n,表示3个柱子a、b、c中第1个柱子a的盘子数量,然后打印出把所有盘子从a借助b移动到c的方法    if n == 1:#递归终止条件        print(a, '-->', c)    else:    #将a的前面的n-1个盘子借助c移动到b        move(n - 1, a, c, b)    #将最后一个盘子从a移动到c        move(1, a, b, c)    #将n-1个圆盘从b借助a移动c        move(n - 1, b, a, c)n = input('请输入A柱子上的圆盘的数目:')move(int(n), 'A', 'B', 'C')
0 0
原创粉丝点击