一、软件背景介绍
我们今天要陈述的应用叫做汉诺塔,大家可能小时候都接触过类似于鲁班锁,九连环的益智玩具,我们要说的汉诺塔其实也可以说是益智玩具的一种。 下面我们具体介绍一下汉诺塔。汉诺塔有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。求按下列规则将所有圆盘移至C杆,必须遵循以下原则:
每次只能移动一个圆盘
大盘不能叠在小盘上面
所以在这个前提下,我们做的程序就可以更快更精确的完成这个汉诺塔盘子从A盘移动到C盘的详细过程,具体算法和程序以及代码我们在后面的文档中会涉及到。下面给出汉诺塔程序设计的一种简单情况,以三个盘子的汉诺塔移动过程为例。
以三个盘子为例,过程如下图所示:
本程序在dosbox中运行需用户输入汉诺塔的盘子数,目前仅支持输入1至9,具体运行效果如图1图示过程,用户输入窗口如下图:
程序在运行过程中的按下Esc键,程序会弹出提示是否退出汉诺塔演示,输入Y则退出,输入N则退出,效果如下图:
二、核心算法思想
如下图所示,从左到右有A、B、C三根柱子,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有两个原则:一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数。
解:这里我们先用一些数学方法求解
(1)n==1
第1次1号盘A---->C sum=1次
(2)n==2
第1次1号盘A---->B
第2次2号盘A---->C
第3次1号盘B---->C sum=3次
(3)n==3
第1次1号盘A---->C
第2次2号盘A---->B
第3次1号盘C---->B
第4次3号盘A---->C
第5次1号盘B---->A
第6次2号盘B---->C
第7次1号盘A---->C sum=7次
不难发现规律:1个圆盘的次数2的1次方减1
2个圆盘的次数2的2次方减1
3个圆盘的次数2的3次方减1
……
n个圆盘的次数2的n次方减1
所以我们可以发现移动次数为:2^n – 1
我们在利用计算机求汉诺塔问题时,必不可少的一步是对整个实现求解进行算法分析。到目前为止,求解汉诺塔问题最简单的算法还是同过递归来求, 再加上上面数学问题解法的分析,我们不难发现,移到的步数必定为奇数步:
中间的一步是把最大的一个盘子由A移到C上去
中间一步之上可以看成把A上n-1个盘子借助辅助塔C塔移到了B上
中间一步之下可以看成把B上n-1个盘子借助辅助塔A塔移到了C上
首先把三根柱子按顺序排成品字型,把所有的圆盘按从大到小的顺序放在柱子A上,根据圆盘的数量确定柱子的排放顺序:
若n为偶数,按顺时针方向依次摆放 A B C;若n为奇数,按顺时针方向依次摆放 A C B
按顺时针方向把圆盘1从现在的柱子移动到下一根柱子,即当n为偶数时,若圆盘1在柱子A,则把它移动到B;若圆盘1在柱子B,则把它移动到C;若圆盘1在柱子C,则把它移动到A
接着,把另外两根柱子上可以移动的圆盘移动到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘
反复进行(1)(2)操作,最后就能按规定完成汉诺塔的移动。
如3阶汉诺塔的移动:
A→C,A→B,C→B,A→C,B→A,B→C,A→C
三、核心算法流程图
汉诺塔动画演示的算法流程图如下:
图3为汉诺塔图形演示的简单流程图,用以明确程序圆形过程中的各个部分模块,清楚程序运行的步骤,是汉诺塔演示中的宏观过程的流程。
图4为汉诺塔程序的算法流程图,描述了程序在运行过程中的详细步骤,涵盖了程序的应输入的条件,汉诺塔中的盘子应该何时移动,往哪里移动,应移动几个盘子等诸多问题在程序中的实现。
四、开发中遇到的问题
第一点是我想整体上先去陈述,顺便提一个最后的小细节。我们制定项目后,在自以为编写完成后,却发现自己弄好的代码运行不了,需要不断地去调试,检查在哪里出现错误,再加以修改,这本就是一个艰辛的过程。尤其最后我们的代码提示只有一个错误缺少“start”时,我们怎么加也没有用,都要崩溃了。不过感谢上课时刘老师的提醒,我们把结尾的“start”删去,才终于运行好了程序。
第二点是在开发过程中怎样去完成汉诺塔的过程,汉诺塔的盘子是如何在三根轴线上正确移动的。通过在纸上自己进行的汉诺塔游戏发现五个盘子以上还是比较有难度的,在网上搜查资料后发现了一种比较容易解决的方法。这种方法是一个只要轮流进行两步操作就可以了。首先把三根柱子按顺序排成品字型,把所有的圆盘按从大到小的顺序放在柱子A上,根据圆盘的数量确定柱子的排放顺序:若n为偶数,按顺时针方向依次摆放 A B C;若n为奇数,按顺时针方向依次摆放 A C B。
按顺时针方向把圆盘1从现在的柱子移动到下一根柱子,即当n为偶数时,若圆盘1在柱子A,则把它移动到B;若圆盘1在柱子B,则把它移动到C;若圆盘1在柱子C,则把它移动到A
接着,把另外两根柱子上可以移动的圆盘移动到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘。这一步没有明确规定移动哪个圆盘,你可能以为会有多种可能性,其实不然,可实施的行动是唯一的
反复进行⑴⑵操作,这样最终就能按规定完成汉诺塔的移动
通过查阅资料和自己的验证发现实现这个算法可以简单分为三个步骤:
把n-1个盘子由A 移到 B
把第n个盘子由 A移到 C
把n-1个盘子由B 移到 C
从这里入手,在加上上面数学问题解法的分析,我们不难发现,移到的步数必定为奇数步:
中间的一步是把最大的一个盘子由A移到C上去
中间一步之上可以看成把A上n-1个盘子借助辅助塔C塔移到B上
中间一步之下可以看成把B上n-1个盘子借助辅助塔A塔移到C上
第三点是开发过程中如何用图形化的方式将汉诺塔过程显示出来。关于这一点,我们想了很久也没有思路。后来我们意识到自己的能力肯定有不足,于是我们决定向老师请教,于是我们去了学院楼老师办公室,向老师咨询了这个问题,后来老师告诉我们,可以通过定义宏的方式在窗口上画出汉诺塔的基本要素和操作,定义了竖线的宏,横线的宏和矩形的宏,用以描绘汉诺塔的形状和盘子,定义了up,down,left,right等子程序通过递减盘子的横纵坐标来实现盘子的上下左右的基本移动,通过bottom参数来实现盘子位置的确定,并通过递归的主函数实现汉诺塔盘子从A借助辅助轴移到C的过程。
以上三点大抵上是我们开发中遇到的“三座大山”,庆幸来自各方的努力,我们终于克服。不过记录下来总是好的。
六、心得体会
大一期间学习了一些高级语言,如C语言,c++,在对一些实际问题的编程处理上使用这些高级语言显得很是方便。于是在刚接触汇编语言这门课的时候,就对其实用性产生了怀疑和一些的抵触情绪。一开始,虽然对一些繁杂的指令有些厌恶,但为了项目还是硬着头皮学着下来了,再经过后来的学习感觉汇编语言并不是那么的枯燥无味,而是很有实际用途的。在很多问题的处理上,汇编语言编程很是节省系统的资源。汇编的学习不仅仅是学习其语法,而更多的是学习计算机基本的体系结构,汇编语言有很多的指令和语句,这是学习汇编前必须要知道和掌握的,只有知道了汇编的实质、如何工作及一些基本概念,才能进行下一步的学习。直到后面学到了80×86指令系统和寻址方式及后来的汇编语言格式,才真正进入了汇编的指令学习阶段,每一条指令的学习虽然简单但比较多,特别是有些指令的使用场合及错误用法等易犯错、易混淆。例如在编写数据传送指令时,目的操作数和源操作数的类型一定要匹配,CS不能作为目的操作数等。但总的归纳起来主要掌握三点:
要求指令操作数的寻址方式
指令对标志位的影响和标志位对指令的影响
指令的执行时间,对可完成同样功能的指令,要选用执行时间短的指令
这样学习起来方便的多,也更容易理解记住,这为以后的学习做了铺垫。
身为一名转专业生,对于同学大一的知识暂时缺乏了解,加上对汇编语言的畏难情绪,一开始的我是很没有自信的,所以一拿到要求就显得无从下手,不知道怎么去操作。一开始也做了很多草稿,总是无疾而终,后来在另一个队友的陪伴下,定下来汉诺塔这个主题,于是我们开始了艰辛地一段路途,也在各方面的努力下最终完成了这个课题,心里十分激动。对于汇编语言,我了解到汇编语言不像其他大多数的程序设计语言一样被广泛用于程序设计。在今天的实际应用中,它通常被应用在底层,硬件操作和高要求的程序优化的场合。