用递归的方式求解汉诺塔问题-Java

汉诺塔的由来

这部分来自百度百科

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

不管这个传说的可信度有多大,如果考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动次数是f(n).显然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1。此后不难证明f(n)=2^n-1。n=64时,
假如每秒钟一次,共需18446744073709511615秒。

这表明移完这些金片需要5845.42亿年以上,而地球存在至今不过45亿年,太阳系的预期寿命据说也就是数百亿年。真的过了5845.42亿年,不说太阳系和银河系,至少地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭。

移动方案

汉诺塔实际移动起来很费时,但作为新时代的少年,我们可以通过程序来求解每一步的移动策略。

本文所使用的方式是递归求解 从 A 移动到 C 的问题,我们先来整理下思路:从三种基本情况说起。

情况一:1pcs 从 A 移动到 C,B 做缓冲柱

image

只有一步,从 A 移动一片到 C

情况二:2pcs 从 A 移动到 C,B 做缓冲柱

image

需要:

  • 从 A 移动一片到 B,用做缓冲
  • 从 A 移动一片到 C
  • 从 B 移动一片到 C

情况三:3pcs 从 A 移动到 C,B 做缓冲柱

image

需要:

  • 1:从 A 移动一片到 C
  • 2:从 A 移动一片到 B
  • 3:从 C 移动一片到 B
  • 4:从 A 移动一片到 C
  • 5:从 B 移动一片到 A
  • 6:从 B 移动一片到 C
  • 7:从 A 移动一片到 C

当总片数达到3片时已经有些小复杂了,而我们通过上面的7个步骤可以发现:

  • 步骤1,2,3是把A 柱顶部的2pcs,从 A 移动到了 B,C 做缓冲柱
  • 步骤4正常移动
  • 步骤5,6,7是把 B 柱上的2pcs,从 B 移动到 C,A 做缓冲柱

从特殊情况到一般

针对情况三的3pcs,我们可以这么总结:

  • 假设目的是从 A 移动到 C,缓冲为 B
  • S1:需要先将 A 除最底部的1pcs外的所有圆盘移动到 B 缓冲
  • S2:再将 A 最后1pcs 移动到 C
  • S3:最后再将 B 中的所有圆盘移动到 C

假设 移动的方法为 MOVE(pcs,start,end,buffer),当 pcs=n时:

  • S1: MOVE(n-1,A,B,C)
  • S2: MOVE(1,A,C,B)
  • S3: MOVE(n-1,B,C,A)

S1可能存在的疑问

S1一次移动那么多盘子,怎么处理?

盘子这么多,我也不知道怎么处理。继续拆分吧,分治策略就是这样,拆分到可以解决为止。其实步骤S1在一共只有3pcs 的情况下只是从 A 移动到 B 2pcs,这种2pcs情况其实已经可以解决了不是?

源码

public class Hanoi {
    
    
    public static void main(String[] args) {
    
    
        move(3,"A","C","B");
    }
    private static void move(int pcs, String start, String end, String buffer) {
    
    
        if (pcs == 1) {
    
    
            System.out.println("从" + start + "移动一片到" + end);
        } else if (pcs == 2) {
    
    
            System.out.println("从" + start + "移动一片到" + buffer);
            System.out.println("从" + start + "移动一片到" + end);
            System.out.println("从" + buffer + "移动一片到" + end);
        } else {
    
    
            move(pcs - 1, start, buffer, end);
            System.out.println("从" + start + "移动一片到" + end);
            move(pcs - 1, buffer, end, start);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/lijie2664989/article/details/106903643