算法学习(一)——河内之塔算法分析与学习
【前面的话】
我已大学毕业,但是我感觉没有好好学习算法,所以现在开始学习,不知道现在会不会太晚了,但是我想每天花点时间,多少学习一些相关算法,这也算是对于大学没有好好学习算法的一种补偿。
【问题背景】
1883年法国数学家 Edouard Lucas曾提及这个故事,据说创世纪时Benares有一座波罗教塔,是由三支钻石棒(Pag)所支撑,开始时神在第一根棒上放置64个由上至下依由小至大排列的金盘(Disc),并命令僧侣将所有的金盘从第一根石棒移至第三根石棒,且搬运过程中遵守大盘子在小盘子之下的原则,若每日仅搬一个盘子,则当盘子全数搬运完毕之时,此塔将毁损,而也就是世界末日来临之时。
【数学描述】
有ABC三根柱子,A柱子上面有64个大小从上往下依次递增的盘子,现在需要把A柱子上面的64个盘子移动到C上面,遵守条件是移动过程中,大盘子必须在小盘子下面,可以借助B柱子。求至少需要移动了多少步。
已知:A,B,C三根柱子,A柱子上面有64个盘子。
条件:移动过程中大盘子必须在小盘子下面,可以借助B柱子。
求解:至少需要多少步。
【算法分析】
1, 分治思想:
- 定义:治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。
- 本题:本题是一个典型的具有分治思想的算法题目:
分治第一步:我们把n个盘子可以想象为n-1个盘子和第n个盘子,如果要想把n个盘子从A移动到C上面,那么移动最少的方法一定是第n个盘子直接从A到C,然后再把剩余的n-1个盘子从B移动到C上面,那么这个问题就变成了如何在最少的步骤内将n-1个盘子从B上面移动到C上面,可以借助A柱子;
分治第二步:我们把n-1个盘子可以想象为n-2个盘子和第n-1个盘子,如果要想把n-1个盘子从B移动到C上面,那么移动最少的方法一定是第n-1个盘子直接从B到C,然后再把剩余的n-2个盘子从A移动到C上面,那么这个问题就变成了如何在最少的步骤内将n-2个盘子从A上面移动到C上面,可以借助B柱子;
分治第三步:依次类推。
省略。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
分治最后一步:最后一次就是直接将第一个盘子移动到C上面,这个第一个盘子不一定在A柱子上面(上面因为从第二步中可以看出最后一次移动盘子是在A,B柱子上面变化的)。
2, 递归思想:
- 定义:递归算法是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。
- 本题:本题的实现也是通过递归实现。在上面进行分治思想讲解的时候,就已经可以看出来这个算法可以通过递归来解决的。
Hanoi(n,A,B,C)//函数实现将n个盘子从A移动到C上,可以借助B;
{
Hanoi(n-1,A,C,B) ;//实现将n-1个盘子从A移动到B上,可以借助C;
move(A,C);//将倒数最后一个盘子从A移动到C,这里的A,C不代表柱子A,C是可以赋值成为其他柱子;
Hanoi(n-1,B,A,C) ;//实现将n-1个盘子从B移动到C上,可以借助A;
}
【语言分析】
如果看了上面还没有理解,那么下面进行一下语言上的分析:
现在主持和尚想要把64个盘子从A搬到C,可以借助B,分步讨论:
第一个和尚称为64:64想,我只做一步:
1.我只要把第64个盘子从A搬到C,前面的63个盘子让和尚63先搬到B,然后我把第64个盘子搬到C,然后他再把其余的63个盘子搬到C,这样和尚64就轻松加愉快的完成了他的任务;
和尚64所做事情:A → C(和尚64只关心第64个盘子)
第二个和尚称为63:63想,我只做两步:
1.1我只要把第63个盘子从A搬到B,前面的62个盘子让和尚62先搬到C,然后我把第63个盘子搬到B,然后他再把其余的62个盘子搬到B,这样和尚63就轻松加愉快的完成了他的第一步;
1.2我只要等和尚64完成第64个盘子从A到C,然后我就让和尚62把其余的62个盘子从B搬到A,然后我把第63个盘子搬到C,然后让和尚62把其余的62个盘子从A搬到C,这样和尚63就轻松加愉快的完成了他的任务;
和尚63所做事情:A → B → C(和尚63只关心第63个盘子)
第三个和尚称为62:62想,我只做四步:
1.1.1我只要把第62个盘子从A搬到C,前面的61个盘子让和尚61先搬到B,然后我把第62个盘子搬到C,然后他再把其余的61个盘子搬到C,这样和尚62就轻松加愉快的完成了他的第一步;
1.1.2我只要等和尚63完成第63个盘子从A到B,然后我就让和尚61把其余的61个盘子从C搬到A,然后我把第62个盘子搬到B,然后让和尚61把其余的61个盘子从A搬到B,这样和尚62就轻松加愉快的完成了他的第二步;
1.2.1我只要等和尚64完成第64个盘子从A到C,然后我就让和尚61把其余的61个盘子从B搬到C,然后我把第62个盘子搬到从B搬A,然后让和尚61把其余的61个盘子从C搬到A,这样和尚62就轻松加愉快的完成了他的第三步;
1.2.2我只要等和尚63完成第63个盘子从B到C,前面的61个盘子让和尚61先从A 搬到B,然后我把第62个盘子搬到C,然后他再把其余的61个盘子搬到C,这样和尚62就轻松加愉快的完成了他的任务;
和尚62所做事情:A → C → B → A → C
(和尚62只关心第62个盘子)
如图所示:
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
依次类推,从和尚64开始,每个和尚所要移动的次数都在以2的次幂增加。
总结:
现在从上面的文字分析来看,如果在看到我编写的序号的话,就可以看出递归调用的过程了。64个和尚的权力从第64到第1是逐次递减,所以和尚64发布自己的命令以后,依次每个和尚都在发布自己的命令,直到和尚1号,和尚1号搬的次数是2的63次方,所以总共搬的次数是2^0+2^1+2^1+…+2^63=2^64-1
所以现在可以类推出个盘子需要移动:(2^n) -1
【图示理解】
通过前面的学习,对于这道算法应该有了一个较好的理解了,但是如果有时还是有一些想不清楚的话,可以通过自己动手画执行过程来进行理解,下面以n=3和n=2为例子来画一些画一下执行过程:
n=2:
n=3:
【代码描述】
最近在学写java,就顺便用java写了:
1 import java.util.*; 2 public class Heneizhita { 3 static int countnumber = 0;//定义全局静态变量,统计移动次数 4 public static void main(String []args){ 5 System.out.println("请输入盘子数量:"); 6 Scanner in = new Scanner(System.in); 7 int n=in.nextInt(); 8 hanoi(n,'A','B','C'); 9 System.out.println(countnumber); 10 } 11 private static void hanoi(int n, char A, char B, char C) { 12 if(n==1){//出递归的条件 13 System.out.println("移动第"+n+"从"+A+"到"+C); 14 countnumber++; 15 } 16 else{ 17 hanoi(n-1,A,C,B);//实现将n-1个盘子从A移动到B上,可以借助C 18 System.out.println("移动第"+n+"从"+A+"到"+C); 19 hanoi(n-1,B,A,C);//实现将n-1个盘子从B移动到C上,可以借助A 20 countnumber++; 21 } 22 } 23 }
【结束语】
这篇文章可以算是写完了,感觉还是可以学习到很多东西,现在才开始注重自己思维条理性,注重思考,注重合理表达自己的思维,希望越来越好。加油。
看到一句话:分享在最后——找出在工作中在某一方面(技术,组织能力等)强过你的人,并去靠近他们。试试经常性的跟他们一起吃午饭或喝咖啡来汲取他们大脑中的大量知识,你的事业甚至是你的人生都会走向出乎意料的更好——TT