最后一次更新于 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).