今天终于把汉诺塔搞懂了,以前总是一知半解
汉诺塔
汉诺塔的由来
汉诺塔是源自印度神话里的玩具。
上帝创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上安大小顺序摞着64片黄金圆盘。
上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
有语言说,这件事完成时宇宙会在一瞬间闪电式毁灭。也有人相信婆罗门至今还在一刻不停地搬动着圆盘。
汉诺塔与宇宙寿命
如果移动一个圆盘需要1秒钟的话,等到64个圆盘全部重新落在一起,宇宙被毁灭是什么时候呢?
让我们来考虑一下64个圆盘重新摞好需要移动多少次吧。1个的时候当然是1次,2个的时候是3次,3个的时候就用了7次......这实在是太累了
因此让我们逻辑性的思考一下吧。
4个的时候能够移动最大的4盘时如图所示。
到此为止用了7次。
接下来如下图时用1次,在上面再放上3个圆盘时还要用7次(把3个圆盘重新放在一起需要的次数)。
因此,4个的时候是
“3个圆盘重新摞在一起的次数”+1次+“3个圆盘重新摞在一起需要的次数”
=2x“3个圆盘重新摞在一起的次数”+1次
=15次。
那么,n个的时候是
2x“(n-1)个圆盘重新摞在一起的次数”+1次。
由于1 个的时候是1次,结果n个的时候为(2的n次方减1)次。
1个圆盘的时候 2的1次方减1
2个圆盘的时候 2的2次方减1
3个圆盘的时候 2的3次方减1
4个圆盘的时候 2的4次方减1
5个圆盘的时候 2的5次方减1
........
n个圆盘的时候 2的n次方减1
也就是说,n=64的时候是(2的64次方减1)次。
因此,如果移动一个圆盘需要1秒的话,
宇宙的寿命=2的64次方减1(秒)
用一年=60秒x60分x24小时x365天来算的话,大约有5800亿年吧。
据说,现在的宇宙年龄大约是150亿年,还差得远呢。
汉诺塔问题在数学界有很高的研究价值,
而且至今还在被一些数学家们所研究,
也是我们所喜欢玩的一种益智游戏,
它可以帮助开发智力,激发我们的思维。
递归程序解法(move1和move2两种解法的结果完全一样):
StringBuilder sb;
long count;
private void button3_Click(object sender, EventArgs e)
{
sb = new StringBuilder();
count = 0;
int N = int.Parse(this.textBox2.Text);
this.move2(N, "A", "B", "C");
this.textBox1.Text = count.ToString() + "," + (Math.Pow(2, N) - 1).ToString() + "\r\n" + sb.ToString();
}
private void button4_Click(object sender, EventArgs e)
{
sb = new StringBuilder();
count = 0;
int N = int.Parse(this.textBox2.Text);
this.move1(N, "A", "B", "C");
this.textBox1.Text = count.ToString() + "," + (Math.Pow(2, N) - 1).ToString() + "\r\n" + sb.ToString();
}
//把N个金片从A通过B移动到C
private void move1(int N, string 源, string 经过, string 目标)
{
++count;
if (N == 1)
{
//打印N从A移动到C
sb.Append(源 + "-" + 目标 + "\r\n");
}
else
{
move1(N - 1, 源, 目标, 经过); //把N-1个金片从A通过C移动到B,
//打印N从A移动到C,因为上一步已把上面所有金片搬走
sb.Append(源 + "-" + 目标 + "\r\n");
move1(N - 1, 经过, 源, 目标); //解决N-1个金片从B通过A到C的问题,因为此时N-1个金片在B上
}
}
private void move2(int N, string 源, string 经过, string 目标)
{
if (N < 1)
return;
if (N == 1)
{
//打印N从A移动到C
sb.Append(源 + "-" + 目标 + "\r\n");
++count;
}
else
{
move2(N - 1, 源, 目标, 经过); //把N-1个金片从A通过C移动到B,
//打印N从A移动到C,因为上一步已把上面所有金片搬走
sb.Append(源 + "-" + 目标 + "\r\n");
++count;
move2(N - 2, 经过, 目标, 源);
sb.Append(经过 + "-" + 目标 + "\r\n");
++count;
move2(N - 2, 源, 经过, 目标);
}
}