脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - 使用 Go 语言实现汉诺塔(Hanota)算法

使用 Go 语言实现汉诺塔(Hanota)算法

2022-04-18 22:35马哥Linux运维子沐爱扫地(译) Golang

我最近重温了一下《猩球崛起》这部电影。在电影中,凯撒就玩了河内塔游戏。你还有印象吗?其实独自一人玩一些游戏是好难的(译者不知作者为何这么说,难道是无聊嘛?),今天我们就用 Golang 来实现一下汉诺塔游戏。

使用 Go 语言实现汉诺塔(Hanota)算法

游戏起源

相传最早发明这个问题的人是法国数学家爱德华·卢卡斯(Edouard Lucas)。

在世界中心的贝拿勒斯(印度北部)圣殿中,有三根宝石针插入了一个黄铜盘中。在印度教主神梵天(Brahma)创世时,将其中一根针上从下到上装配了 64 个金片,这也就是所谓的汉诺塔。

无论白天黑夜,总会有一位僧人按照接下来的规则移动这些金片:一次只移动一片,无论在哪根针上,小片必须在大片之上。

比丘们预言,当所有的金片从梵天所的装配宝石针上移到另一个宝石针上时,世界将在雷霆中毁灭,梵天塔、寺庙和众生也将灭亡。

这个传说有很多变种,虽不知道是谁创作的,但其留下的数学问题非常经典。

遗留下来的数学知识:金片数量与移动步数的关系是 2ⁿ–1

1 个金片需要的步数是 2 的 1 次方减 1

2 个金片需要的步数是 2 的 2 次方减 1

3 个金片需要的步数是 2 的 3 次方减 1

n 个金片需要的步数是 2 的 n 次方减 1

如果传说属实,修士们需要2⁶⁴-1步移动才能完成这个任务;假设他们每秒移动一片黄金,则需要 5849 亿年才能完成。整个宇宙只有 137 亿年,宇宙毁灭还为时尚早。。。

游戏规则分析

假设这个游戏中有 3 个柱子,即 A、B 和 C。需要移动的是珠子,其中一个柱子上已经有 N 个有序的珠子,最大的在底部,珠子按顺序越来越小。另外 2 个是空柱子。

基本条件:

  • 一次只能移动一颗珠子
  • 小珠子一定要在大珠子上面

初始状态如下图所示:

使用 Go 语言实现汉诺塔(Hanota)算法

最终目标是将柱子上的所有珠子移到另一根柱子上。如下所示:

使用 Go 语言实现汉诺塔(Hanota)算法

游戏实现思路

  1. 放空你的大脑,先想想最简单粗暴的解决逻辑:将珠子视为一个整体。
  2. 若要满足大珠子在下面的基本条件,一定要把 A 上最大的珠子清空,再把这个最大的珠子放在 C 柱上。(假设珠子数量为 N)
  3. 如果要将其移动到 C 柱,首先要实现的必须是把 N-1 个珠子全部移到 B 柱上,

这样才能把第 N 个珠子(也就是最大的珠子)移到 C 柱上。

  1. 把 N-1 珠子移到 B 柱子上,因为大珠的在下,小珠在上,所以这 N-1 个珠子在 B 柱上是有序的。
  2. 最后,将这 N-1 个珠子从 B 柱移动到 C 柱,完成最终目标。

实现第一步:将 A 上的 N-1 个珠子移动到 B。

为什么先把 N-1 移到 B 上?因为你的最终实现是将所有的珠子从 A 移到 C,并且顺序不能改变。只能大的在下,小的在上。

那么必须先将最大的珠子移到 C,否则条件不成立。要将最大的珠子从 A 移到 C,必须腾出 A 上最大的珠子,也就是必须把最大珠子上面的所有珠子全部移走。

而你只有 3 根柱子,C 上不能有其他珠子,否则不符合条件,因此这 N-1 颗珠子只能放在 B 上,并且它们会依旧井然有序。

使用 Go 语言实现汉诺塔(Hanota)算法

第二步将 A 上的第 N 个珠子(最大的珠子)移动到 C。

这很简单,只需一步将最大的珠子从 A 移动到 C。如下所示。

使用 Go 语言实现汉诺塔(Hanota)算法

第三步将 B 上的 N-1 个珠子移动到 C。

提示:要实现将 N-1 个珠子移动到 C,是不是先找到其中最大的珠子,然后先移动最大的珠子?所以这里的话实际上变成了重复第一步和第二步,从这 N-1 个珠子中找出最大的一个,移到 C,然后重复下去。

第三步其实相当于改变了要求。假设 K = N - 1。

这时 B 柱有 K 个珠子,A 柱是空的,C 柱有最大的珠子,所以 B 柱有 K 个珠子就相当于它是空的。

第一步将 B 上的 K-1 个珠子移动到 A。

第二步将 B 上的第 K 个珠子移动到 C。

第三步将 A 上的 K-1 个珠子移动到 C

如下所示。

使用 Go 语言实现汉诺塔(Hanota)算法

首先找到剩余的珠子中最大的一个(在该演示中是 4 号)。然后移动它。

使用 Go 语言实现汉诺塔(Hanota)算法

循环重复以上步骤,直到只剩下最后一颗(最小的)珠子,直接移动到 C,游戏结束。

辅助柱

什么是辅助柱?假设您现在拥有要在 A 上被移动的所有珠子,同时目标是将其移动到 C,那么 B 是这 N-1 个珠子的辅助柱。因为他们只能暂时留在这里,否则不符合游戏规则。

这里需要先找到辅助支柱,先别想怎么实现,先理清逻辑。

要实现从 A 到 B 的移动,那么 C 就是辅助柱。

要实现从 A 到 C 的移动,那么 B 是辅助支柱。

要实现从 B 到 C 的移动,那么 A 就是辅助柱。

Golang 实现

从上面的分析可以看出,这其实是一个循环重复的操作,和递归很像,并且都可以用递归来实现。

要使用递归,有两个必要条件

  1. 求递归公式
  2. 找到退出条件

在这个游戏中,退出条件是在只有一颗珠子的情况下直接移动到C柱。

那么递归公式是什么呢?根据以上逻辑分析,可以分解为三个步骤:

  • 第一步,将 {这 N-1 个珠子} 从A移动到B
  • 第二步,将 {第 N 个珠子} 从A移动到C
  • 第三步,将 {其余 N-1 个珠子} 从 B 移到 C

以下是用 Golang 实现的伪代码

package main import "fmt" // Record the number of game steps var count int = 0 func main() { beadNum := 5 // This is the initial number of beads fmt.Printf("This is a Hannukah game with %d beads \n\r", beadNum) hanoi(beadNum, "A", "B", "C") fmt.Printf("Game over: %d steps spent in total \n\r", count)
} // Hannukah game func hanoi(beadNum int, pillarA string, pillarB string, pillarC string) { if beadNum == 1 { // If there is only one bead, move from A to C, game over move(beadNum, pillarA, pillarC)
    } else { // Step 2: move all the plates above N (that is, N-1 judgments) from A to B. At this time, C is the transfer station hanoi(beadNum-1, pillarA, pillarC, pillarB) // Step 2: move the Nth plate from A to C move(beadNum, pillarA, pillarC) // Step 3: move the remaining n-1 disks on B from B to C. At this time, A is the transfer station hanoi(beadNum-1, pillarB, pillarA, pillarC)
    }
} // Move the beads func move(beadNum int, pillarFrom string, pillarTo string) { count += 1 fmt.Printf("Step %d: bead of %d from %s move to %s \n\r", count, beadNum, pillarFrom, pillarTo)
}

感谢您阅读本文,如果您认为文章写得好,请关注我。

原文地址:https://mp.weixin.qq.com/s/sFX-cKXCQlnyYSVMgHjX1A

延伸 · 阅读

精彩推荐
  • GolangGo语言range关键字循环时的坑

    Go语言range关键字循环时的坑

    今天小编就为大家分享一篇关于Go语言range关键字循环时的坑,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来...

    benben_20154202020-05-23
  • Golang深入浅析Go中三个点(...)用法

    深入浅析Go中三个点(...)用法

    这篇文章主要介绍了深入浅析Go中三个点(...)用法,需要的朋友可以参考下...

    踏雪无痕SS6472021-11-17
  • Golanggo语言获取系统盘符的方法

    go语言获取系统盘符的方法

    这篇文章主要介绍了go语言获取系统盘符的方法,涉及Go语言调用winapi获取系统硬件信息的技巧,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    无尽海3862020-04-24
  • GolangGO语言字符串处理Strings包的函数使用示例讲解

    GO语言字符串处理Strings包的函数使用示例讲解

    这篇文章主要为大家介绍了GO语言字符串处理Strings包的函数使用示例讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加...

    Jeff的技术栈6882022-04-14
  • GolangGo语言实现自动填写古诗词实例代码

    Go语言实现自动填写古诗词实例代码

    这篇文章主要给大家介绍了关于Go语言实现自动填写古诗词的相关资料,这是最近在项目中遇到的一个需求,文中通过示例代码介绍的非常详细,需要的朋...

    FengY5862020-05-14
  • GolangGo语言基础单元测试与性能测试示例详解

    Go语言基础单元测试与性能测试示例详解

    这篇文章主要为大家介绍了Go语言基础单元测试与性能测试示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助祝大家多多进步...

    枫少文7812021-12-05
  • GolangGolang实现四种负载均衡的算法(随机,轮询等)

    Golang实现四种负载均衡的算法(随机,轮询等)

    本文介绍了示例介绍了Golang 负载均衡的四种实现,主要包括了随机,轮询,加权轮询负载,一致性hash,感兴趣的小伙伴们可以参考一下...

    Gundy_8442021-08-09
  • GolangGolang 语言极简类型转换库cast的使用详解

    Golang 语言极简类型转换库cast的使用详解

    本文我们通过 cast.ToString() 函数的使用,简单介绍了cast 的使用方法,除此之外,它还支持很多其他类型,在这没有多多介绍,对Golang 类型转换库 cast相关知...

    Golang语言开发栈6112021-12-02