多柱汉诺塔最优算法设计探究

多柱汉诺塔最优算法设计探究
 
引言
汉诺塔算法一直是算法设计科目的最具代表性的研究问题,本文关注于如何设计多柱汉诺塔最优算法的探究。最简单的汉诺塔是三个柱子(A、B、C),因此多柱汉诺塔的柱子个数M≥3。下面从三柱汉诺塔说起,慢慢深入我们要关心的问题。
1. 三柱汉诺塔
三柱汉诺塔是经典的汉诺塔问题,在算法设计中是递归算法的典型问题。其算法是这样的: 首先把A 柱上面的n- 1 个碟子通过C 柱移到B 柱上【T(n-1)步】,然后把A 柱剩下的一个碟子移到C 柱上【1步】, 最后把B 柱上所有的碟子通过A 柱移到C 柱上【T(n-1)步】。很容易得到算法的递归方程为:T(n)=2*T(n-1)+1,因此,不难算出步数是T(n)=2^n-1。对于三柱汉诺塔的算法的正确性自然是毫无争议的,我们需要的是从三柱汉诺塔的设计中引申出多柱汉诺塔的设计方法。
2. 四柱汉诺塔
四柱汉诺塔并不是仅仅是多了一根柱子那么简单,所以我们先尝试从正常的思维出发来探究如何使移动步数最少。
首先我们会想到,三柱汉诺塔需要借助另一个柱子存放前n-1个盘子,再把第n个盘子移动到目的位置。顺其自然的,四柱汉诺塔由于多了一个柱子,所以移动起来就更方便了,我们可以多留下一个盘子n-2,而不让它借位到其他柱子直接移动到目的位置。这样我们就得出算法的基本流程:
(1)       从A借助C、D将 n-2个盘子移动到B上。
(2)       将n-2移动到C上。
(3)       将n-1移动到D上。
(4)       将n-2移动到D上。
(5)       从B借助A、C将 n-2个盘子移动到D上。
另外,这么设计是符合正常思维原则的。以为随着柱子的个数增多,我们希望每次移动的时候盘子尽可能不发生折叠,也就是说我们希望除了需要借助存放n-2个盘子的柱子。那么剩下的两个柱子可以允许至多两个盘子不发生折叠就能直接移动到目的位置,这样才使得移动起来比较方便,步骤也会比较少。事实真的是如此吗?我们具体分析一下算法。
按照以上设计的算法流程,我们得到递归方程:F(n)=2*F(n-2)+3。因此得到移动步数为:F(n)=4*2^(n/2)-3:n为奇数;F(n)=6*2^(n/2-1)-3:n为偶数。下边列出6个盘子的移动步数:
n      1     2     3     4     5     6
F(n)  1     3     5     9     13    21
       到这里,我们已经看出我们的设计的算法已经和经典的汉诺塔算法几乎如出一辙了,甚至是如此的对称和谐!基于此我们甚至可以推广到M(M≥3)个柱子的情况,来得到我们希望的最优解,假设柱子编号为1,2,3…M算法主题框架流程应该如下:
(1)       从1柱借助3…M柱子将n-(M-2)个盘子移动到2柱上。
(2)       将M-2个通过3…M-1柱简单的移动到M柱上【2*(M-2)-1步骤】。
(3)       从2柱借助1,3…M-1柱子将n-(M-2)个盘子移动到M柱上。
具体步骤和四柱类似,不再做具体分析。这样我们看到我们自己亲手构建的算法模式如此完美,我们甚至不忍心去破坏它。但是我很遗憾的告诉自己,这种算法虽然正确,却不是最优!!!比如,对于6个盘子4个柱子的汉诺塔,按照我们的想法是保留2个盘子进行移动。现在假如我们保留3个盘子,因此上边的三个盘子按照4柱汉诺塔规则移动到B,步数应该是5(已经算出,可以验证),剩下三个盘子按照3柱汉诺塔规则移动到D上,步数应该是2^3-1=7步,然后B上的三个盘子移动到D上仍然是5步,总步数为5+7+5=17步<21步!现在我们可以确信的告诉自己,我们的想法太“天真”了。虽然我们想到让盘子尽量不发生重叠来保证步数的最少,但是这并不能绝对保证。或许在盘子较少的情况下是可行的,但是盘子增多时,那些多余的只有一个盘子的柱子是可以加以利用的。虽然这么做加多了每次的移动步数,但是却从另一个侧面减少了递归的数量,因此我们需要从这里边找一个平衡点。
从上边的例子中,我们得到一个启示:在递归程序中剩余盘子的个数并不一定是M-2,也有可能是M-1,我们假设剩余盘子是M-r,那么r到底取得多少才合适呢?其实,早在1941年,一位名叫J. S. Frame的人在《美国数学月刊》上提出了一种解决四柱汉诺塔问题的算法,这是人们熟知的Frame算法:
(1)用4柱汉诺塔算法把A柱上部分的n- r个碟子通过C柱和D柱移到B柱上【F( n- r )步】。
(2)用3柱汉诺塔经典算法把A柱上剩余的r个碟子通过C柱移到D柱上【2^r-1步】。
(3)用4柱汉诺塔算法把B柱上的n-r个碟子通过A柱和C柱移到D柱上【F(n-r)步】。
(4)依据上边规则求出所有r(1≤r≤n)情况下步数f(n),取最小值得最终解。
因此Frame算法的递归方程如下:
F(n)=min(2*F(n-r)+2^r-1),(1≤r≤n)。
通过这个方程我们能得到所有4柱汉诺塔的步骤个数,同时也有人证明[1]了,对于四柱汉诺塔,当r=(sqrt(8*n+1)-1)/2时,能保证f(n)取得最小值F(n)=(n-(r^2-r+2)/2)*2^r+1。所以算法的复杂度是F(n)=O(sqrt(2*n)*2^ sqrt(2*n))。从这这个方程中也可以看出,在n<6的时候,我们可以验证是和我们起初的构想的结构是相同的,但是当n再增多时就不是当初想的那样了。
3. 多柱汉诺塔
基于四柱汉诺塔的Frame算法,我们可以引申到多柱(M柱)汉诺塔的情况,我们简称M柱汉诺塔算法:
(1)用M柱汉诺塔算法把1柱上部分的n-r个碟子通过3…M柱移到2柱上【M( n- r )步】。
(2)用M-1柱汉诺塔算法把1柱上剩余的r个碟子通过3…M-1柱移到M柱上【<M-1>(r)步】。
(3)用M柱汉诺塔算法把2柱上的n-r个碟子通过1柱和3…M柱移到M柱上【M( n- r )步】。
(4)依据上边规则求出所有r(1≤r≤n)情况下步数m(n),取最小值得最终解M(n)。
从4柱汉诺塔的递归方程和结果公示中我们可以看出,随着柱子数量的增加,算法的复杂程度也是不断地增加。对于解决M柱汉诺塔问题需要使用M-1柱汉诺塔的算法,因此除了算法解决问题需要递归外,算法的流程本身也需要递归,这种递归结构已经远远地复杂于当前所接触的递归算法。如果有兴趣可以尝试去设计这种算法,算法所涉及的参数应该有盘子的个数n、柱子的个数m、算法的编号num、参数r等信息。因为需要根据不同柱子情况下通过循环和递归找出最合适的r值,所以这种算法的复杂度肯定相当高。不过我们仅仅是为了探究如何取得最优算法,所以具体实现就不再赘述了。
总结
通过以上的讨论,我们从一般的思维——不折叠盘子,出发去找多柱汉诺塔的最优解,但是结果并没有成功——盘子多时有可能柱子没有充分利用。后来通过前人提出的Frame算法引申出多柱汉诺塔算法,并大致描述了多柱汉诺塔算法的双重嵌套递归结构——算法问题的递归以及算法本身的递归实现。这种罕见的递归程序结构给我们在算法设计方面开阔了新的视野,希望不久的将来能找到更好地算法设计方法来解决多柱汉诺塔的问题。
参考文献
1.《四柱汉诺塔之初步探究》杨楷 徐川( 北京大学计算机科学与技术系, 北京, 100871) 北京大学学报( 自然科学版) , 第40 卷, 第1 期, 2004 年1 月

时间: 2024-09-17 04:28:09

多柱汉诺塔最优算法设计探究的相关文章

C语言实现汉诺塔游戏_C 语言

操作就是:A B 号码A的塔顶一层放在号码B的塔顶.如1(空格) 3 回车. 话说有人能把我这C的代码添加到QT界面框架上去么?  代码写的不好 ,维护性不够,只能玩8层的,写完以后发现很难拓展,软件工程,设计模式有待提高.... 里面提示输入等级的装B用了,没有实现,大家随便输入个个位数就可以玩了. stackfunc.c #include"STACK.h" #include<stdio.h> extern ceng CENG[SIZE]; //数据入栈 void pus

汉诺塔游戏的设计

汉诺塔问题是最经典的递归问题,笔者就该问题设计了这个游戏,由用户交互 游戏和自动演示两部分组成,支持撤销功能.选关.自动完成等功能. 首先 建立了类CMap,该类主要实现用户每一步的操作和画图显示功能,记录的时候只 须记录每组盘子的个数和盘子的矩形.代码和注释如下: //记录 每一步的盘子的情况 class CMap { public: //每组 盘子的个数 int iCount[3]; //3组盘子里面,每个盘子的位 置,用矩形表示 RECT *Rect[3]; //构造函数 CMap() {

汉诺塔问题的最终解决

问题的提出:约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到大顺序串着由64个圆盘构成的塔.目的是将最左边杆上的盘全部移到右边的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面. *问题分析与算法设计 这是一个著名的问题,几乎所有的教材上都有这个问题.由于条件是一次只能移动一个盘,且不允许大盘放在小盘上面,所以64个盘的移动次数是: 18,446,744,073,709,551,615 这是一个天文数字,若每一微秒可能计算(并不输出)一次

初学者c语言动态汉诺塔帮忙注释

问题描述 初学者c语言动态汉诺塔帮忙注释 #include #include #define N 1000 void gotoxy(int x, int y); // 声明gotoxy函数 void colorxy(int x, int y); //声明colorxy函数 void hanoi(int n,char a,char b,char c); //声明hanoi函数 void move(int n,char a,char b); //声明move函数 void Print(); //声明

c#汉诺塔的递归算法与解析_C#教程

从左到右 A  B  C 柱 大盘子在下, 小盘子在上, 借助B柱将所有盘子从A柱移动到C柱, 期间只有一个原则: 大盘子只能在小盘子的下面. 如果有3个盘子, 大中小号, 越小的越在上面, 从上面给盘子按顺序编号 1(小),2(中),3(大), 后面的原理解析引用这里的编号. 小时候玩过这个游戏, 基本上玩到第7个,第8个就很没有耐心玩了,并且操作的动作都几乎相同觉得无聊.  后来学习编程, 认识到递归, 用递归解决汉诺塔的算法也是我除了简单的排序算法后学习到的第一种算法. 至于递归,简单来说

c语言-汉诺塔C语言编程问题,求帮忙

问题描述 汉诺塔C语言编程问题,求帮忙 Description 大家都听说过汉诺塔吧?有n个圆盘由小到大排列,套在a柱上,每次只能移动一个圆盘,而且只能大的在下,小的在上,让你把a柱上的圆盘移到b柱,给你一个多余的c柱,问你最少移动多少次才能完成任务. Input 输入有多组数据,每组包括一个整数n(n<=10000000),表示初始状态下有n个圆盘,当输入的n为0时,程序结束,n为负的情况不作处理. Output 对每个输入,对应一行输出,每行输出包括一个整数,即移动的最小次数,因为数目非常大

乐在其中:无所不能用SQL挑战经典游戏汉诺塔

苏旭晖,网名 newkid ITPUB开发版资深版主,SQL开发专家 编辑手记:感谢苏旭晖先生授权我们独家转载其系列精品文章,也欢迎大家向"Oracle"社区投稿. SQL是一门非常灵活的语言,专家们用其挑战一切看似不可能. 问题来源 汉诺塔是源自印度神话里的玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按大小顺序摞着64片黄金圆盘.  大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上.并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个

【找回数学的感觉】1 再版汉诺塔等

尤其在学过函数式编程之后,更加觉得想在计算机技术上上一个台阶必须得有非常扎实的数学基础.然而太多学生大学开始就慢慢淡忘了数学,和高中比起来根本不是一个境界.于是我决心开设这样一个系列,我每天都会练习,也会更新上博客,也希望大家能够每天练习,毕竟每天都有人推送题目^_^ 独立思考是一个非常好的习惯,也希望大家能够拥有它,我虽然会在题目后搭上答案,但肯定不如你通过自己的思考学到的多,而且我写下来的肯定也不如各自思考大脑中想到的多. 第一题 我们的目标是将A中的整个塔移到C中,每次只移动一个圆盘,且较

算法-汉诺塔是怎么思考出来的

问题描述 汉诺塔是怎么思考出来的 本人菜鸟,看到这个算法的时候觉得好巧妙,但是自己怎么也想不出来啊... 解决方案 所谓递归,其实人类是做不到的,因为递归嵌套多了,人就乱了,有点像是盗梦空间里的深层梦境limbo,到那就几乎回不到现实了. 但是计算机不像人类想法那么多,尤其是基于过程的设计,你教我怎么走我就怎么走,不会混乱,但是你得给我定个边界条件,要不我还是回不来. 汉诺塔的确是一个经典的递归问题,而人类做的最伟大的一步在于,把f(n)的问题转化为f(n-1),推本溯源,总要找到一个origi