我想很多人第一次學習遞歸的時候,老師或者書本上可能會舉漢諾塔的例子。
但是今天,我們討論的重點不是簡單的漢諾塔算法,而是三柱漢諾塔的延伸。先來看看經典的三柱漢諾塔。
?
一、三柱漢諾塔(Hanoi_Three):
我想大家對于三柱漢諾塔的理解以及算法的實現應該是很熟練了。
我在這里簡單的過一遍三柱漢諾塔的算法思想:
有A、B、C三根柱子,A柱上有n個盤子,現在需要將A上所有的盤子轉移到C上,請給出搬運次數最少的步驟。
?
算法思想:
1、將A上n-1個盤子以C為緩存,全部轉移到 B 柱上。
2、將A上留下的第n個盤子,直接轉移到 C? 柱上。
3、將B上的n-1個盤子,以A為緩存,全部轉移到 C 柱上。
?
很容易得到算法的遞歸方程為:T(n)=2*T(n-1)+1,不難算出步數是T(n)=2^n-1。
?
具體的代碼如下:
[cpp]?view plaincopy
void?Move(?char?x,?char?y?)?? {?? ????printf("%c?-->?%c\n",?x,?y);??? }?? ?? void?Hanoi_Three(?int?n,?char?a,?char?b,?char?c?)?? {?? ????if(?n?<=?0?)?? ????????return?;?????? ?????? ????step++;??????? ?????? ????if(?n?==?1?)?? ????{?? ????????Move(?a,?c?);????? ????????return?;?????? ????}?? ????else?? ????{?? ????????Hanoi_Three(?n-1,?a,?c,?b);??????? ????????Move(?a,?c?);????????????????????? ????????Hanoi_Three(?n-1,?b,?a,?c);??????? ????}?? }??
二、四柱漢諾塔(Hanoi_Four):
?
當柱子為四根時,對于只是將盤子全部轉移到另一根柱子上這個目的來說,是大大降低了難度,而且算法的復雜度也大大降低了。但是,這個時候,如果要你找到一個最優的、步驟最少的實現方法,可以說難度是提升了一個數量級。
有些人可能會質疑,為什么,我用三柱漢諾塔的思想不是很優化了嗎?
別急,且讓我慢慢向你道來。
?
先來看看這種‘看上去很合理’的解法:
假設,A,B,C,D,分別為:源位置,緩存,緩存,目的位置。
因為三柱的時候,我們是將A的前n-1個盤子放到B上緩存,然后將第n個盤子放到C柱上。
現在的情況好很多,有兩個可以緩存的柱子,因此,看上去移動起來更加方便,原來B上需要緩存的n-1個盤子,現在可以只是n-2個盤子,而將第n-1個盤子放到C上緩存。
?
具體的流程如下(非最優解法):
1、從A借助C、D將 n-2個盤子移動到B上。
2、將第n-1個盤子移動到C上。
3、將第n個盤子移動到D上。
4、將第n-1個盤子移動到D上。
5、從B借助A、C將 n-2個盤子全部移動到D上。
?
看上去,非常完美,筆者也一度覺得這個思想沒有破綻,甚至我還自以為找到了k根柱子漢諾塔的通用方法(想當然的將B柱上緩存的數量從n-2個,改為n-(k-2)個盤子)。直到我看了這篇文章:多柱漢諾塔最優算法設計探究。
??? 雖然我們想到讓盤子盡量不發生重疊來保證步數的最少,但是這并不能絕對保證。或許在盤子較少的情況下是可行的,但是盤子增多時,那些多余的只有一個盤子的柱子是可以加以利用的(可能的優化在這里)。雖然這么做加多了每次的移動步數,但是卻從另一個側面減少了遞歸的數量,因此我們需要從這里邊找一個平衡點。
?
下面我們來看看,1941年,美國的J. S. Frame,給出的四柱漢諾塔的算法思想,也叫Frame算法:
1、用4柱漢諾塔算法把A柱上部分的n- r個碟子通過C柱和D柱移到B柱上【F( n- r )步】。
2、用3柱漢諾塔經典算法把A柱上剩余的r個碟子通過C柱移到D柱上【2^r-1步】(參照上述三柱時的情況)。
3、用4柱漢諾塔算法把B柱上的n-r個碟子通過A柱和C柱移到D柱上【F(n-r)步】。
4、依據上邊規則求出所有r(1≤r≤n)情況下步數f(n),取最小值得最終解。
?
因此Frame算法的遞歸方程如下:
F(n)=min(2*F(n-r)+2^r-1),(1≤r≤n)。
?
大家有沒有發現,其實,這個算法思想跟我們之前認為合理的算法基本一致,差別只是在于他將我們的n-2個碟子緩存到B上,改為了將n- r個碟子轉移到B柱上。
差別即使核心,這個算法的核心,就是計算n個盤子的情況下,r為何值時,能夠使得算法最優。
?
找到了核心,我們現在的任務就明確了,就是對r值的計算。
這里給出了一個較笨的方法--枚舉(不知道各位有沒有其他方法)。就是將一定范圍內的n與r的所有取值帶入,得到滿足F(n)為最小值的r的值,記為K[n] = r;
具體的代碼如下:
[cpp]?view plaincopy
void?Init_K(void?)?? {?? ????int?i,?k;????? ????__int64?temp;????? ????__int64?m[Max+1]?=?{0};??????? ?????? ????for(?i?=?1;?i?<=?Max;?i++?)?? ????{?? ????????m[i]?=?INT_MAX;??????? ????????for(?k?=?1;?k?<=?i;?k++?)?? ????????{?? ????????????temp?=?2*m[i-k]?+?(__int64)pow(2,k)?-?1;?????? ????????????if(?temp?<?m[i]?)?? ????????????{?? ????????????????m[i]?=?temp;?????? ????????????????K[i]?=?k;????? ?????????????????? ????????????}?? ????????}?? ????}?? }??
得到各個n對于的r之后,算法將變的非常簡單,具體實現如下:
[cpp]?view plaincopy
#include?<stdio.h>?? #include?<math.h>?? #define?Max?100??? #define?INT_MAX?0xfffffffffffffff?? int?K[Max+1]?=?{0};??? int?step?=?0;????? ?? void?Hanoi_Four(?int?n,?char?a,?char?b,?char?c,?char?d?);????? void?Hanoi_Three(?int?n,?char?a,?char?b,?char?c?);???? void?Move(?char?x,?char?y?);?????? ?? void?Move(?char?x,?char?y?)?? {?? ????printf("%c?-->?%c\n",?x,?y);??? }?? ?? void?Hanoi_Three(?int?n,?char?a,?char?b,?char?c?)?? {?? ????if(?n?<=?0?)?? ????????return?;?????? ?????? ????step++;??????? ?????? ????if(?n?==?1?)?? ????{?? ????????Move(?a,?c?);????? ????????return?;?????? ????}?? ????else?? ????{?? ????????Hanoi_Three(?n-1,?a,?c,?b);??????? ????????Move(?a,?c?);????????????????????? ????????Hanoi_Three(?n-1,?b,?a,?c);??????? ????}?? }?? ?? void?Hanoi_Four(?int?n,?char?a,?char?b,?char?c,?char?d?)?? {?? ????if(?n?<=?0?)?? ????????return?;?????? ?????? ????if(?n?==?1?)?? ????{?? ????????step++;??? ????????Move(?a,?d?);????? ????????return?;?????? ????}?? ????else?? ????{?? ????????int?kn?=?K[n];???? ?????????? ????????Hanoi_Four(?n-kn,?a,?c,?d,?b?);??????? ????????Hanoi_Three(?kn,?a,?c,?d?);??????????? ????????Hanoi_Four(?n-kn,?b,?a,?c,?d?);??????? ????}?? }?? ?? void?Init_K(void?)?? {?? ????int?i,?k;????? ????__int64?temp;????? ????__int64?m[Max+1]?=?{0};??????? ?????? ????for(?i?=?1;?i?<=?Max;?i++?)?? ????{?? ????????m[i]?=?INT_MAX;??????? ????????for(?k?=?1;?k?<=?i;?k++?)?? ????????{?? ????????????temp?=?2*m[i-k]?+?(__int64)pow(2,k)?-?1;?????? ????????????if(?temp?<?m[i]?)?? ????????????{?? ????????????????m[i]?=?temp;?????? ????????????????K[i]?=?k;????? ?????????????????? ????????????}?? ????????}?? ????}?? }?? ?? int?main()?? {?? ????int?n;???? ????Init_K();????? ?? ????printf("Please?enter?the?number?of?the?Plates:?\n");?? ????while(?scanf("%d",?&n)?!=?EOF?)?? ????{?? ????????step?=?0;????? ????????Hanoi_Four(?n,?'A',?'B',?'C',?'D'?);?????? ?????????? ????????printf("**************************\nTotal?Step:?%d\n",?step?);???? ?? ????????printf("Please?enter?the?number?of?the?Plates:?\n");?? ????}?? ?????? ????return?0;????? }??
?
到這里,四柱漢諾塔的算法基本講完了。
有興趣的同學,可以繼續歸納多柱漢諾塔的實現方法,歡迎交流指導!
?
?
?很多時候,看似合理的背后,其實是一種思維定勢。。。
from:?http://blog.csdn.net/cyh_24/article/details/8075578
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的四柱加强版汉诺塔HanoiTower----是甜蜜还是烦恼的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。