1. 程式人生 > >圖解漢諾塔問題(遞迴求解)

圖解漢諾塔問題(遞迴求解)

漢諾塔:漢諾塔(Tower of Hanoi)源於印度傳說中,大梵天創造世界時造了三根金鋼石柱子,其中一根柱子自底向上疊著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。                        --引用維基百科

單看這個問題描述有點讓人抓瞎,這是當然,無論多麼簡單的問題描述,在沒有建立具體地模型之前都是讓人不知所云的,僅僅用生活中的語言去描述一些數學或演算法問題往往會讓聽者產生理解偏差,這也和每個的理解能力和思維方式有很大關係,這就顯示出數學的強大了,數學讓問題不再模糊,引數和公式組成的模型讓問題不再有理解偏差和誤區,只可惜數學沒學好,看來以後還得回來把高數、概率論這些給補回來。

說了一些題外話,下面來對漢諾塔問題進行解釋和建立模型


    這是示意圖,a是起始柱,c是目標柱,b起到中轉作用

    在進行轉移操作時,都必須確保大盤在小盤下面,且每次只能移動一個圓盤,最終c柱上有所有的盤子且也是從上到下按從小到大的順序。

    很多時候看到這些蛋疼和矯情操作就覺得數學家真是思維清奇、智慧如海,嗯還有吃飽了撐的,某兄臺邪魅地一笑,道:直接把c和a交換位置不就行了,我想這位兄臺以後或成大器或被人打死。

    問題看起來並不複雜,當a柱子上只有一個盤子時只要把那個盤子直接移到c就行了,

    有兩個盤子的話把1號盤先移到b柱,在把2號盤移到c柱,最後把b柱上的1號盤移到c柱就行了,

    但現在我們要移動的是64個盤子,要是我們自己手動操作,那畫面會很美,閒著無聊的人可以試試,推薦去網上找找漢諾塔的小遊戲,這也可以幫你加深對這個問題的理解。

下面我用圖來描述64個盤子的轉移流程

  

    這裡我們先把上方的63個盤子看成整體,這下就等於只有兩個盤子,自然很容易了,我們只要完成兩個盤子的轉移就行了,好了現在我們先不管第64個盤子,假設a柱只有63個盤子,與之前一樣的解決方式,前62個盤子先完成移動目標。

    嗯,就這樣一步步向前找到可以直接移動的盤子,62,61,60,......,2,1,最終,最上方的盤子是可以直接移動到c柱的,那就好辦了,我們的2號盤也能完成向c柱的轉移,這時c柱上時已經轉移成功的2個盤,於是3號盤也可以了,一直到第64號盤。

    下面是我用C#實現的程式碼:

using System;
using System.Collections.Generic;

namespace 漢諾塔問題_遞迴解決
{
   
    class Program
    {       
        static void Main(string[] args)
        {
            HanNuo(64, 'a', 'b', 'c');

            Console.ReadKey();
        }

        /// <summary>
        /// 漢諾塔問題解決方法
        /// </summary>
        /// <param name="n">漢諾塔的層數</param>
        /// <param name="a">承載最初圓盤的柱子</param>
        /// <param name="b">起到中轉作用的柱子</param>
        /// <param name="c">移動到的目標柱子</param>
        static void HanNuo(int n, char a, char b, char c)
        {
            if (n == 1)   //這也是遞迴的終止條件
            {
                Console.WriteLine("將盤子[{0}]從 {1} -----> {2}", n, a, c); //控制檯輸出每次操作盤子的動向
            }
            else
            {
                HanNuo(n - 1, a, c, b);      //將a柱子上的從上到下n-1個盤移到b柱子上
                Console.WriteLine("將盤子[{0}]從 {1} -----> {2}", n, a, c);
                HanNuo(n - 1, b, a, c);      //將b柱子上的n-1個盤子移到c柱子上
            }
        }
    }
}

       程式碼很簡潔,可能對於遞迴不是很理解的同學覺得有些吃力,下面我來具體解釋下遞迴的流程。

       當n=64時,前63個要想辦法成功移動到b柱上,64號是Boss,他不管上面的63個小弟用什麼辦法,我可以先等著,前面63個小弟可以利用我的c柱,於是64號在等著上面63號完成移到b柱,現在63是臨時老大,他也想去c柱,於是他命令前62號移到b柱,他等著,62號也採取之前兩個的做法,於是這個命令一直往前傳,沒辦法,上面被壓著自己也沒法動啊。

        終於到了1號,他是現在唯一能動的,於是1號移動到了b柱,好了,2號可以到c柱,2第一個到目的地,心裡十分激動,我都到c柱,舒服。不過當他看到a柱上的3號時,猛然一驚,我還有個上司,好吧得完成任務啊,於是讓1號移到c柱,3號可以到b柱了,之後1號和2號在想辦法到b柱,於是1,2,3號在b柱,4號一看很滿意,但我得到b柱啊,嗯,1,2,3號你們按照剛才的辦法到c柱,空出b柱給我。唉,接著折騰,後面的5號一直到63號都是這麼折騰的,終於前63號移動到b柱,64號直接跑到了c柱,他覺得這些小弟辦事效率真不行,不過他還是招呼小弟到c柱。

     於是剩下在b柱的63個小弟還要再幹一遍他們在a柱上乾的事,這裡來看,1號操作的頻率是最高的,而64號只要移動一次就行了,要是在現實中讓人這麼去幹,估計早就被逼瘋了。

      如果真要解釋程式碼的每一步執行過程,那會很亂,兩層遞迴,最後自己也會被繞的暈頭轉向,這個時候只要理解遞迴最終的解決的問題是什麼就行了,中間的事交給程式,遞迴可以很繞也可以很直接,我們按照最直接的理解就行了。

  最後關於遞迴演算法,這中解決問題的方法也只有計算機才喜歡,我們雖然看著程式碼很簡單,但真要深入理解也是很費腦細胞的,不過遞迴確實有中數學上的簡潔美和邏輯美。