汉诺塔问题介绍:
在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片,一次只移动一片,不管在哪根针上,小片必在大片上面。当所有的金片都从梵天穿好的那根针上移到另外一概针上时,世界就将在一声霹雳中消灭,梵塔、庙宇和众生都将同归于尽。
这个问题有一个优雅的递归解法,图2.4 描述了这个解法。为了把个盘子从木桩 1 移到木桩 3(借助木桩2),我们需要先把 个盘子递归地从木桩 1 移到木桩 2(借助木桩 3),然后直接把最大的盘子(第个盘子)从木桩 1 移到木桩 3 ,并且,最后把个盘子递归地从木桩 2 移到木桩 3(借助木桩 1 )。当然,如果,我们可以简单地把这个单独的盘子直接从一个木桩移到另一个木桩。
让我们把前面的一般性方案应用到汉诺塔问题上去。显然,我们可以选择盘子的数量作为输入规模的一个指标,盘子的移动也可以作为该算法的基本操作。可以清楚地看到,移动的次数只依赖于,因此,对于有下列递推等式:
当时,
另一个很明显的事实是初始条件,因此,对于移动次数我们建立了下面的递推关系:
当时,
我们还是使用反向替换法来解这个递推式:
替换
替换
左边前 3 个求和算式的模式预示着下一个算式将是:,对这个模式进行一般化处理,在做了 次替换以后,得到下式:
因为初始条件是在 的情况下确立的,所以必须让 ,我们有下列方程来解递推式(2.3):
这样得到了一个指数级的算法,即使 的值不算大,该算法的运行时间也会长得无法想像。这并不是因为这个算法不好。不难证明,对于这个问题来说, 这是可能提供的最高效的算法。事实上,是这个问题本身决定了它在计算上的难度。尽管如此,这个例子还是揭示了一个具有普遍意义的重要观点:
我们应该谨慎使用递归算法,因为它们的简洁可能会掩盖它们的低效率.
如果一个递归算法会不止一次地调用它本身,出于分析的目的,构造一棵它的递归调用树是很有用的。在这棵树中,节点相当于递归调用,我们可以用调用参数的值(或者是几个参数的值)作为节点的标记。对于汉诺塔这个例子来说,它的递归调用树在图2.5中给出。 通过计算树中的节点数,我们可以得到汉诺塔算法所做调用的全部次数:
这个数字就像我们预测的那样,和我们早先求得的移动次数是一致的。
那么好多人会问64个圆盘移动到底会花多少时间?那么古代印度距离现在已经很远,这64个圆盘还没移动完么?我们来通过计算来看看要完成这个任务到底要多少时间?
我们使用通项式: 。当时 。
我们假设移动一次圆盘为一秒,那么一年为31536000秒。那么18446744073709551615/31536000约等于584942417355天,换算成年为5845.54亿年。
目前太阳寿命约为50亿年,太阳的完整寿命大约100亿年。所以我们整个人类文明都等不到移动完整圆盘的那一天。
结论:汉诺塔问题通式如下