利用棧實現迷宮的求解
問題: 這是實驗心理學中的一個經典問題,心理學家把一隻老鼠從一個無頂蓋的大盒子的入口處趕進迷宮。迷宮中設定很多隔壁,對前進方向形成了多處障礙,心理學家在迷宮的唯一出口處放置了一塊乳酪,吸引老鼠在迷宮中尋找通路以到達出口。
求解思想:回溯法是一種不斷試探且及時糾正錯誤的搜尋方法。下面的求解過程採用回溯法。從入口出發,按某一方向向前探索,若能走通(未走過的),即某處可以到達,則到達新點,否則試探下一方向; 若所有的方向均沒有通路,則沿原路返回前一點,換下一個方向再繼續試探,直到所有可能的通路都探索到,或找到一條通路,或無路可走又返回到進入點。
在求解過程中,為了保證在到達某一點後不能向前繼續行走(無路)時,能正確返回前一點以便繼續從下一個方向向前試探,則需要用一個棧儲存所能夠到達的每一點的下標及從該點前進的方向。
需要解決的四個問題:
1.表示迷宮的資料結構:設迷宮為m 行n 列,利用maze[m][n]來表示一個迷宮,maze[i][j]=0 或1; 其中:0 表示通路,1 表示不通,當從某點向下試探時,中間點有8 個方向可以試探,(見圖3.4)而四個角點有3 個方向,其它邊緣點有5 個方向,為使問題簡單化我們用maze[m+2][n+2]來表示迷宮,而迷宮的四周的值全部為1。這樣做使問題簡單了,每個點的試探方向全部為8,不用再判斷當前點的試探方向有幾個,同時與迷宮周圍是牆壁這一實際問題相一致。
Map = [
[1,1,1,1,1,1,1,1,1,1],
[1,0,1,1,1,0,1,1,1,1],
[1,1,0,1,0,1,1,1,1,1],
[1,0,1,0,0,0,0,0,1,1],
[1,0,1,1,1,0,1,1,1,1],
[1,1,0,0,1,1,0,0,0,1],
[1,0,1,1,0,0,1,1,0,1],
[1,1,1,1,1,1,1,1,1,1]
],
如圖3.4 表示的迷宮是一個6×8 的迷宮。入口座標為(2,2),出口座標為(9,7)
2.試探方向:在上述表示迷宮的情況下,每個點有8 個方向去試探,如當前點的座標(x,y),與其相鄰的8 個點的座標都可根據與該點的相鄰方位而得到,如圖3.5 所示。因為出口在(m,n),
因此試探順序規定為:從當前位置向前試探的方向為從正東沿順時針方向進行。為了簡化問題,方便的求出新點的座標,將從正東開始沿順時針進行的這8 個方向的座標增量放在一個結構數組move [ 8 ]中,在move 數組中,每個元素有兩個域組成,x:橫座標增量,y:縱座標增量。move 數組如圖3.6 所示。Move 數組定義如
Dir = [{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,1},{-1,0},{-1,1}],
3.棧的設計:當到達了某點而無路可走時需返回前一點,再從前一點開始向下一個方向繼續試探。因此,壓入棧中的不僅是順序到達的各點的座標,而且還要有從前一點到達本點的方向。
Path_stack : 【{X,Y,Dir}。。。】
4. 如何防止重複到達某點,以避免發生死迴圈:一種方法是另外設定一個標誌數組mark[m][n],它的所有元素都初始化為0,一旦到達了某一點( i , j )之後,使mark[i][j] 置1,下次再試探這個位置時就不能再走了。另一種方法是當到達某點(i , j)後使maze[i][j] 置-1,以便區別未到達過的點,同樣也能起到防止走重複點的目的,本書採用後者方法,演算法結束前可恢複原迷宮。
lists:member({X+DX,Y+DY},FindList)
解決以上問題寫代碼就簡單如下:
find_path()-> Map = [ [1,1,1,1,1,1,1,1,1,1], [1,0,1,1,1,0,1,1,1,1], [1,1,0,1,0,1,1,1,1,1], [1,0,1,0,0,0,0,0,1,1], [1,0,1,1,1,0,1,1,1,1], [1,1,0,0,1,1,0,0,0,1], [1,0,1,1,0,0,1,1,0,1], [1,1,1,1,1,1,1,1,1,1] ], Dir = [{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,1},{-1,0},{-1,1}], Path_stack = stack_init(), %%從點2,2 開始八方向找點 find_path1({2,2},Dir,Path_stack,Map,{9,7},[]).find_path1({X,Y},DirL,Path_stack,Map,{FX,FY},FindPos1)-> FindPos = [{X,Y}|FindPos1], case get_right_pos_dir(DirL,FindPos,{X,Y},Map,1) of {DX,DY,Dir}-> NewPath_stack = stack_in(Path_stack,{X,Y,Dir}), case {FX,FY} == {DX,DY} of %%每次找出可走點與最終點對應 true-> NewPath_stack; _ -> find_path1({DX,DY},DirL,NewPath_stack,Map,{FX,FY},FindPos) end; false-> NewPath_stack = stack_out(Path_stack), {TX,TY,_TDir} = stack_get(NewPath_stack), find_path1({TX,TY},DirL,NewPath_stack,Map,{FX,FY},FindPos) end.%%get_right_pos_dir(DirL,findPos,SPos,Map,1)get_right_pos_dir([],_,_,_,_)-> false;get_right_pos_dir([{DX,DY}|DirL],FindList,{X,Y},Map,Count)-> case lists:nth(X+DX,lists:nth(Y+DY,Map) ) of 1-> get_right_pos_dir(DirL,FindList,{X,Y},Map,Count+1); _-> case lists:member({X+DX,Y+DY},FindList) of false-> {X+DX,Y+DY,Count}; _ -> get_right_pos_dir(DirL,FindList,{X,Y},Map,Count+1) end end.