查看“︁汉诺塔”︁的源代码
←
汉诺塔
跳转到导航
跳转到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{Multiple issues| {{copyedit|time=2019-02-16T08:39:02+00:00}} {{Expand language|1=de|status=yes|2=he|status2=yes|time=2022-06-07T12:19:59+00:00}} }} {{NoteTA|T=zh-hans:汉诺塔;zh-hant:河內塔 |1=zh-hans:猿族崛起;zh-tw:猩球崛起;zh-hk:猿人爭霸戰:猩凶革命 |2=zh-hans:汉诺塔;zh-hant:河內塔 |3=zh-hans:递归;zh-hant:遞迴}} [[File:Tower_of_Hanoi.jpeg|thumb|right|常见玩具版汉诺塔有8个圆盘]] [[File:Tower of Hanoi.gif|thumb|right|3个圆盘的汉诺塔的移动]] [[File:Tower of Hanoi 4.gif|thumb|right|4个圆盘的汉诺塔的移动]] {{地區用詞3|zh-cn='''汉诺塔'''|zh-tw='''河內塔'''|equiv-hk=tw}}(Tower of Hanoi)是根据一个传说形成的數學问题: 有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆: #每次只能移动一个圆盘; #大盘不能叠在小盘上面。 提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。 问:如何移?最少要移动多少次? == 传说 == 最早發明這個問題的人是[[法國]]數學家[[爱德华·卢卡斯|愛德華·盧卡斯]]。 傳說[[越南]][[河内]]某間寺院有三根銀棒,上串 64 个金盤。寺院裡的僧侶依照一個古老的預言,以上述規則移動這些盤子;預言說當這些盤子移動完毕,世界就會滅亡。這個傳說叫做[[梵天]]寺之塔問題(Tower of Brahma puzzle)。但不知道是盧卡斯自創的這個傳說,還是他受他人啟發。 若傳說屬實,僧侶們需要 <math>2^{64} - 1</math>步才能完成這個任務;若他們每秒可完成一個盤子的移動,就需要 5849 億年才能完成。整個宇宙現在也不過 137 億年。 這個傳說有若干變體:寺院換成修道院、僧侶換成修士等等。寺院的地點眾說紛紜,其中一說是位于越南的河內,所以被命名為「河內-{}-塔」。另外亦有「金盤是[[創世]]時所造」、「僧侶們每天移動一盤」之類的背景設定。 ==解答== 如取 N=64,最少需移动 2<sup>64</sup>−1 次。即如果一秒钟能移动一块圆盘,仍需 5849 億年。目前按照[[宇宙大爆炸理論]]的推测,宇宙的年齡僅為 137 億年。 在真实玩具中,一般 N=8;最少需移动 255 次。如果 N=10,最少需移动 1023 次。如果N=15,最少需移动32767次;这就是说,如果一个人从 3 岁到 99 岁,每天移动一块圆盘,他最多仅能移动 15 块。如果 N=20,最少需移动 1048575 次,即超过了一百万次。 == 算法求解 == 解法的基本思想是递归。假设有 A、B、C 三个塔,A 塔有 <math>N</math> 块盘,目标是把这些盘全部移到 C 塔。那么先把 A 塔顶部的 <math>N - 1</math> 块盘移动到 B 塔,再把 A 塔剩下的大盘移到 C,最后把 B 塔的 <math>N - 1</math> 块盘移到 C。 如此递归地使用下去, 就可以求解。 ===遞迴解=== 在 Java語言中: <syntaxhighlight lang="cpp"> public class HW { public static java.util.Scanner sc = new java.util.Scanner(System.in); public static void Towers(int n,char a,char b,char c){ if(n==1){ System.out.println("移動"+n+"從"+a+"到"+c); } else{ Towers(n-1,a,c,b); System.out.println("移動"+n+"從"+a+"到"+c); Towers(n-1,b,a,c); } } public static void main(String[] args) { int n = sc.nextInt(); Towers(n,'A','B','C'); } } </syntaxhighlight> 以 [[C++]] 语言实现: <syntaxhighlight lang="cpp"> #include <iostream> using namespace std; void Towers(int n,char a,char b,char c){ if(n==1){ cout<<"Move disk "<<n<<" from"<<a<<" to "<<c<<endl; } else{ Towers(n-1,a,c,b); cout<<"Move disk "<<n<<" from"<<a<<" to "<<c<<endl; Towers(n-1,b,a,c); } } int main(int argc, char *argv[]) { int n; cin>>n; Towers(n,'A','B','C'); cout<< endl; } </syntaxhighlight> 以 [[Python]] 语言实现: <syntaxhighlight lang="python"> def hanoi(n, a, b, c): if n == 1: print(a, '-->', c) else: hanoi(n - 1, a, c, b) hanoi(1 , a, b, c) hanoi(n - 1, b, a, c) # 调用 if __name__ == '__main__': hanoi(5, 'A', 'B', 'C') </syntaxhighlight> 以 [[Erlang]] 语言求解: <syntaxhighlight lang='erlang'> -module(hanoi). -export([hanoi/1]). hanoi(N) when N>0 -> do_hanoi({N, 1, 3}). do_hanoi({0, _, _}) -> done; do_hanoi({1, From, To}) -> io:format("From ~p, To ~p~n", [From, To]); do_hanoi({N, From, To}) -> do_hanoi({N-1, From, by(From, To)}), do_hanoi({1, From, To}), do_hanoi({N-1, by(From, To), To}). by(From, To) -> [Result|_] = [1, 2, 3] -- [From, To], Result. </syntaxhighlight> 以 [[Haskell]] 语言实现: <syntaxhighlight lang='haskell'> hanoi :: Integer -> String -> String -> String -> [(String, String)] hanoi 0 _ _ _ = [] hanoi 1 from _ to = [(from, to)] hanoi x from buffer to = hanoi (x-1) from to buffer ++ hanoi 1 from buffer to ++ hanoi (x-1) buffer from to </syntaxhighlight> ====任意初始結構(arbitrary initial configuration)的解法==== 為了從任意初始結構中找尋最佳解(optimal solution),其演算法可以一般化(be generalized)如下: 以 Scheme 語言表示: <syntaxhighlight lang='scheme'> ; Let conf be a list of the positions of the disks in order of increasing size. ; Let the pegs be identified by the numbers 0, 1 and 2. (define (hanoi conf t) (let ((c (list->vector conf))) (define (move h f t) (vector-set! c h t) (printf "move disk ~s from peg ~s to peg ~s~n" h f t)) (define (third-peg f t) (- 3 f t)) (define (hanoi h t) (if (> h 0) (let ((h (sub1 h))) (let ((f (vector-ref c h))) (if (not (= f t)) (let ((r (third-peg f t))) (hanoi h r) (move h f t))) (hanoi h t))))) (hanoi (vector-length c) t))) </syntaxhighlight> 在 Java語言中: <syntaxhighlight lang="cpp"> public class Hanoi { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(hanoiFunc(7)); } public static int hanoiFunc(int i) { int sum = 0; if (i == 1) { sum += i; } else { sum = 2 * hanoiFunc(i - 1) + 1; } return sum; } } </syntaxhighlight> 在 C語言中: <syntaxhighlight lang='C'> int conf[HEIGHT]; /* Element conf[d] gives the current position of disk d. */ void move(int d, int t) { /* move disk d to peg t */ conf[d] = t; } void hanoi(int h, int t) { if (h > 0) { int f = conf[h-1]; if (f != t) { int r = 3 - f - t ; hanoi(h-1, r); move(h-1, t); } hanoi(h-1, t); } } </syntaxhighlight> == 图像解释 == 可以用无向图来表示汉诺塔, 在表示的时候会更加地直观和清晰, 虽然说理解上有一点难度。 现在规定, 每一个节点表示盘子的位置一种可能性, 每一条边表示一种移动的方法。 ''注: 这里不考虑在两个柱子之间的, 没有意义的, 来回移动的情况。'' 对于只有一个盘子的汉诺塔,可以表示为:<gallery> File:Tower of Hanoi 1-disk graph.svg|一个盘子的情况 </gallery> 对于有两个盘子的汉诺塔, 可以表示为: 相互连接的三个三角形, 组成了一个较大三角形的三个角。 每一个节点的第二个字母表示更大的盘子, 且最初时没有被移动。 对于每一个顶端的小三角形, 表示两个盘子的一种移动的方法:<gallery widths="220" heights="220"> File:Tower of Hanoi 2-disk graph.svg|两个盘子的汉诺塔 </gallery> 外围的三角形的每一个节点, 表示在一个柱子上盘子的所有分布可能。 对于 h+1 个盘子, 就可以"复制" h 个盘子时候的三角形图, 然后拼成一个新的大三角形图, 稍微改动一下, 这个大的三角形图就可以用来表示 h+1 个盘子时的情况了。 所以当有三个盘子时, 图形为:<gallery widths="440" heights="440"> File:Tower of Hanoi-3.svg|三个盘子的汉诺塔 </gallery> * a、b 和 c 表示三个柱子 * 按照从小到大的顺序, 从左到右地列出的盘子的位置。 最外面的三角形的边, 表示了盘子从一个柱子移动到另一个柱子最快的方式。 最大的三角形可以沿着中线分成三个次小的三角形, 就是上面由二级的汉诺塔组成三级的汉诺塔的逆向操作, 次小三角形相互之间的连线, 表示着最大的盘子的移动方式。 同理, 在这次三角形的也可以沿其中线分割成为三个次次三角形, 一样的, 次次小三角形相互之间的连线, 表示着次大的盘子的移动方式。 继续下去, 也就可以表示出一个汉诺塔的移动方式。 [[File:Hanoi-Graph-7.svg|缩略图|200x200像素|具有七个盘子的汉诺塔的图与七级的 [[謝爾賓斯基三角形|谢尔宾斯基三角形]] 的联系]] 通常,对于具有 n 个盘子的图, 有 <math>3^n</math>个节点; 每个节点都有三条边连接着其他节点, 但是在[[顶点 (图论)|顶点]]的的节点只却只有有两条边连接着其他节点。 所以说总是下都可以将最小的盘子移动到另外两个柱子中的一个, 对于多数情况, 是可以在两个柱子间移动一个盘子, 除了所有的盘子都在一个柱子上。 边角的节点表示着所有的盘子都在一个柱子的情况, 即可以在 a, b 或 c 柱上堆满盘子, 显然只要三种。 对于 <math>n+1</math>个盘子的图, 可以通过表示 <math>n</math>给盘子的图 "复制" 三份, 组合在一起的。 因此也就很方便地表示了每一层的汉诺塔移动方式, 每一个次小的三角形表示次小的盘子的所有可能的移动方式和放置状态, 次小的三角形之间的连接表示了大盘子的三种可能的移动方式。 所以图形有 <math>3^n + 1</math> 个节点, 基本都有三个与之相连接的边, 而[[顶点 (图论)|顶点]]只有两个。 在盘子数比较多的时候, 汉诺塔的图像就会开始和分形图比较相似了。 如果使用最麻烦的移动方式, 即不走重复的路(移动方式), 需要 <math>3^{64} - 1 </math> 次移动, 每秒移动一次, 需要的时间超过 <math>10^{23}</math>年。 在不考虑重复使用路径的情况下, 去除掉没有经过的边, 就可以表示出当有三个盘子时的最长路径 。 就是没有去做无意义 (多余的) 的, 将一个盘子在两个柱子间疯狂来回移动的情况下, 去除没有使用的移动方法, 就可以得到当有三个盘子时的最麻烦的移动方式<gallery widths="440" heights="440"> File:Tower of Hanoi 3-disk graph - longest path.svg|三个盘子的汉诺塔 - 通路 </gallery> 顺便一提,这个最长的非重复路径, 可以通过清除掉从 a 到 b 的路径得到。 也可以得出三个盘子的汉诺塔图的 [[哈密頓路徑問題|哈密顿回路]]:<gallery widths="440" heights="440"> File:Tower of Hanoi-4 Longest Cycle.svg|三个盘子的汉诺塔的哈密顿回路 </gallery> 该图较为清楚地表达了: * 对于任意的全部盘子在一根柱子的情况下, 将所有盘子移动到另一个柱子的最短路径只有一个。 * 对于任意的两个盘子分布情况之间转换的时候, 只有一个或者两个不同的最短路径。 * 对于任意的盘子分布情况, 都有一个或者两个将所有盘子移动到任意一个柱子上的最长不相交路径。 * 对于任意的两个盘子分布情况之间转换的时候, 只有一个或者两个不同的最长不相交路径。 * 设 <math>N_h</math>是将有 <math>h</math>个盘子的塔, 将所有盘子从一个柱子移动到另一个柱子的非相交路径的数量(一开始盘子都在一个柱子上)。 可以得出: ** <math>N_{1} = 1</math>N 1 = 2, ** <math>N_{h + 1} = (N_h )^2 + (N_h )^3</math>。 这里的 <math>N_{h} \in \{2, 12, 1872, 6563711232, ....\}</math>, 详细在 [[oeis:A125295|A125295]]上。 ==多塔汉诺塔问题== *在有 3 个柱子时,所需步数的公式较简单,但对于 4 个柱子以上時,情形複雜許多。Frame 及 Stewart 在 1941年 時分別提出了基本上相同的一套演算法<ref>{{cite journal |first1=B.M. |last1=Stewart |first2=J.S. |last2=Frame |title=Solution to advanced problem 3819 |journal=American Mathematical Monthly |volume=48 |issue=3 |pages=216–9 |doi=10.2307/2304268 |date=March 1941 |jstor=2304268}}</ref>,Bousch 則在 2014年 證明了Frame-Stewart演算法在 4 個柱子時就是最佳解<ref>{{cite journal |first= T.|last=Bousch|title=La quatrieme tour de Hanoi|journal=Bull. Belg. Math. Soc. Simon Stevin|volume= 21|year=2014|pages=895–912}}</ref>,但此演算法在 5 個柱子以上是否是最佳解仍是未知。 Frame-Stewart 演算法本質上也是递归的,可簡單敘述如下: *令<math>f(n,k)</math>为在有 <math>k </math> 个柱子时,移动n个圆盘到另一柱子上需要的步数,则: ::对于任何移动方法,必定会先将 <math>m(1\le m\le n-1)</math>个圆盘移动到一个中间柱子上,再将第 <math>n</math>到第 <math>n - m</math>个圆盘通过剩下的 <math>k - 1</math>个柱子移到目标柱子上,最后将 <math>m </math>个在中间柱子上的圆盘移动到目标柱子上。这样所需的操作步数为 <math>2f(m,k)+f(n-m,k-1)</math>。 ::进行[[最优化]],易得: <math>f(n,k)=\mathrm{min}_{m\in [1,n-1]}\; (2f(m,k)+f(n-m,k-1))</math> 。 ==流行文化== 2011年電影《[[猿人爭霸戰:猩凶革命]]》曾出現河內塔以測試[[猩猩]]的[[智商]]。其后续电影《[[猩球崛起2]]》中也有类似的场景。 ==參見== * [[九連環]] * [[遞迴 (電腦科學)|遞迴]] == 參考來源 == {{reflist}} == 外部連結 == {{Commons category|Tower of Hanoi}} * {{mathworld|title=Tower of Hanoi|urlname=TowerofHanoi}} * [https://web.archive.org/web/20161130063440/http://marcin-chwedczuk.github.io/assets/apps/hanoi/index.html Tower of Hanoi animation] [[Category:智力游戏]] [[Category:数学问题]] [[Category:河內市文化]]
该页面使用的模板:
Template:Cite journal
(
查看源代码
)
Template:Commons category
(
查看源代码
)
Template:Mathworld
(
查看源代码
)
Template:Multiple issues
(
查看源代码
)
Template:NoteTA
(
查看源代码
)
Template:Reflist
(
查看源代码
)
Template:地區用詞3
(
查看源代码
)
返回
汉诺塔
。
导航菜单
个人工具
登录
命名空间
页面
讨论
不转换
查看
阅读
查看源代码
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
工具
链入页面
相关更改
页面信息