汉诺塔问题是一个经典的“重复问题“(recurrent problem),解法也中所周知,最少移动步骤是2^n - 1。算法
然而,我发现,对这个问题进行进一步的研究也是挺有意思的。code
本篇博客目前阐述三个Hanoi相关的三个问题(基本问题,扩展问题,变种问题)。博客
基本问题:数学
n个盘子,ABC三个地点,将A上的n个盘子移动到C上。最少的步骤是多少?效率
AC[n] = AB[n-1] + AC[1] + BC[n-1]扩展
由于AC[n] = BC[n] = AB[n] := T(n)二进制
因此以上等式能够写成T(n) = T(n-1) + 1 + T(n-1),从而获得T(n) = 2^n - 1方法
扩展问题移动
由基本问题的推导步骤,咱们知道,要在2^n - 1步内将n个盘子从A移动到C,那么每一步都是固定的!时间
由此,产生如下问题:
通过k步移动后,每一个盘子的状态是什么?即,每一个盘子是在A,B仍是C上?
解法1: 对于这个问题,咱们固然能够模拟K步移动来解的答案,可是,这是很是没有效率的。由于K有可能很大,是2的指数级增加东西,因此,这种解法很是愚蠢!
咱们须要获得的信息是每一个盘子的状态,而不是每一步的具体移动步骤和状态,因此,咱们需求的信息是相对较少的,也就必然有比解法1要好的多的解法。理想情况是O(n)时间复杂度,由于不可能比O(n)更小了,你须要要对看一看每一个盘子。
从基本问题的推导中,咱们能够看到,汉诺塔问题的解决,不过是在不断的重复和交换“起始点“ ”中间点“ 和 “目的点“。咱们以src, med, 和dst来标志他们。由此,咱们很容易想到,咱们的解法也能够是“重复"+"交换".
个人思路是先考察简单状况,好比盘子是2个,3个的状况,获得一个先验的结论和体会。有兴趣的朋友能够试一下。
在考察完简单状况后,结合咱们的大方向"重复"+"交换",咱们很容易想到如下解法(若是你本身动手考察过简单状况的话,会比较容易理解):
K = (a[n-1] a[n-2] ... a[1] a[0])的二进制表示. 其中a[i] = 0或者1. (这一步须要花费O(n)时间)
for i <- n-1 to 0
if a[i] == 0
p[i] = src; swap(med, dst);
if a[i] == 1
p[i] = dst; swap(src, med);
结束。
p[n-1] ... p[0]中的值就是各个盘子的状态,算法时间复杂度为O(n).
变种问题:
变种问题是,若是盘子的移动只能从A->B->C或者C->B->A,AC之间不能直接移动盘子,这个汉诺塔的问题会怎么样呢?咱们一样考察以上两个问题,即最少步骤和第K步状态。
最小步骤的推导方法和基本汉诺塔问题一致,在此不赘述了,能够获得结论是3^n - 1.
关于第K步状态,有点不同。若是咱们和基本汉诺塔问题的K步问题同样的方法来考察这个问题的话,是能够获得结论的,可是,思路复杂而且耗时长(不信的朋友能够试试)。
如下介绍一种nb的思路来解决这个问题,这个思路我本身没想出来,是参考了一些资料才获得的。
用3进制Gray Code来模拟移动过程!!!!!
朋友!你能想到么?增长了附加的条件后,数学模型居然更加简单了!
因而移动过程就是Gray Code从00000 增长到 222222 , 因而恰好能够用0 1 2来表征状态!!!有木有!!!
第K步的状态能够用3进制来表示后,设一个reverse flag(表征某位是从0-1-2仍是2-1-0)。因而即可以用简单的数学模型+重复+[每步reverse flag]来解决问题!时间复杂度为O(n).
3进制gray code!!
路漫漫其修远兮,吾将上下而求索!