最後一次更新於 2019/07/09
算法
對於只有三個塔的漢諾塔問題咱們有個基本規則:函數
根據以上規則,漢諾塔的算法能夠寫成如下幾個步驟:code
第一步: 將 N-1 個圓盤從初始塔移動到中間塔。orm
第二步: 再將該圓盤從初始塔移動到目的塔。遞歸
第三步: 再將剩下的 N-1 個圓盤從中間塔移動到目的塔。
(對每一個圓盤都進行上述操做那麼這個思路能夠看做是遞歸)源碼
在 Erlang 中,咱們能夠將不一樣塔中的圓盤表達成 [{tower1, [5,4,3,2,1]},{tower2,[]},{tower3,[]}] 的形式。it
用戶惟一須要作的事情就是傳遞初始的圓盤總數。這個數字將被列表轉換成升序的數字形式。io
此函數以下所示:form
produce(1) -> [1]; % 若是當前數字爲1,就直接返回1,遞歸結束。 produce(N) -> produce(N-1) ++ [N]. % 將爲遞歸的數值放於當前數值以前。
舉個例子,若是總數爲5,通過 produce()
函數運行後的結果爲[1,2,3,4,5]。但這不是咱們想要的結果。
有人可能會提議把 produce(N-1) ++ [N]
語句掉換成 [N] ++ produce(N-1)
不就好了嗎。是的,若是按當前的要求看效果已經達到了。
然而實際上,咱們每次都會移動塔頂最上方的圓盤,它的值必定是列表中最小的。若是在這裏使用降序排列的話咱們獲取到的數值就會是5而不是1。
所以,reverse()
函數應該做爲一個額外調用的方法來寫。class
此函數以下所示:
reverse_tower([H|T],M)-> reverse_tower(T,[H|M]); % 將新頭圓盤添加到列表中,列表將自動反轉。 reverse_tower([],M)-> M. % 若是列表爲空說明全部圓盤已排好序。 reverse_tower(T)-> reverse_tower(T,[]). % 初始列表。
在列表轉換以後,咱們能夠開始經過上述算法解決問題。
該算法經過如下函數實現:
%% ================================================================================================================== %% 此函數用於返回不一樣塔的狀態和須要移動的圓盤。 %% ================================================================================================================== check_status(T, Start, End)-> % 使用 _ 變量在模式匹配中做爲通配符。 [{_,Tower1List},{_,Tower2List},{_,Tower3List}] = T, Status = if Start == tower1, End == tower2 -> % 添加新的圓盤到塔2並丟掉塔1最上方的圓盤。 Number = hd(Tower1List), [{tower1,tl(Tower1List)},{tower2,[Number|Tower2List]},{tower3,Tower3List}]; Start == tower1, End == tower3 -> % 添加新的圓盤到塔3並丟掉塔1最上方的圓盤。 Number = hd(Tower1List), [{tower1,tl(Tower1List)},{tower2,Tower2List},{tower3,[Number|Tower3List]}]; Start == tower2, End == tower1 -> % 添加新的圓盤到塔1並丟掉塔2最上方的圓盤。 Number = hd(Tower2List), [{tower1,[Number|Tower1List]},{tower2,tl(Tower2List)},{tower3,Tower3List}]; Start == tower2, End == tower3 -> % 添加新的圓盤到塔3並丟掉塔2最上方的圓盤。 Number = hd(Tower2List), [{tower1,Tower1List},{tower2,tl(Tower2List)},{tower3,[Number|Tower3List]}]; Start == tower3, End == tower1 -> % 添加新的圓盤到塔1並丟掉塔3最上方的圓盤。 Number = hd(Tower3List), [{tower1,[Number|Tower1List]},{tower2, Tower2List},{tower3,tl(Tower3List)}]; % 添加新的圓盤到塔2並丟掉塔3最上方的圓盤。 true -> Number = hd(Tower3List), [{tower1,Tower1List},{tower2, [Number|Tower2List]},{tower3,tl(Tower3List)}] end, {Number, Status}. % 返回一個包含當前移動圓盤的值和新的狀態標識的元組。 %% ================================================================================================================== %% 該函數用於將圓盤從起始塔轉移到目的塔。 %% 完成移動後,更新狀態的表示。 %% ================================================================================================================== move(T, Start, End)-> % 得到被移動圓盤的值和新的狀態表示。 {Number, NewStatus} = tool:check_status(T, Start, End), io:format("-------------------------------------------~nMove No.~p disk: ~p -------> ~p ~n", [Number, Start, End]), % 打印出新的狀態表示。 display_towers(NewStatus), % 返回新的狀態。 NewStatus. %% ================================================================================================================== %% 該函數使用尾遞歸解決圓盤轉移問題。 %% ================================================================================================================== solve(1, Init, Aux, Dest, T)-> move(T, Init, Dest); % 最上方的圓盤能夠直接移動。 solve(N, Init, Aux, Dest, T) when N > 1-> % 將 N-1 個圓盤從初始塔移動到中間塔。 ResetStatus = solve(N-1, Init, Dest, Aux, T), % 再將該圓盤從初始塔移動到目的塔。 ResetStatusAgain = move(ResetStatus, Init, Dest), % 再將剩下的 N-1 個圓盤從中間塔移動到目的塔 solve(N-1, Aux, Init, Dest, ResetStatusAgain).