更多数学趣题:走迷宫
===》點我返回目錄《===
我們都走過迷宮,在彎彎曲曲的小道上尋找出口,迷茫中夾帶驚喜。剛開頭都是亂走,碰到死胡同后又折回來,慢慢地有了一套辦法,確保不遺漏可能的路徑,還有些人知道使用左手/右手法則。
我們用程序來走走迷宮。我們這兒定義的迷宮是簡單形式的,一個N*N矩形,四周有外墻,有一個入口,一個出口,保證有解,可以走的范圍從[0,0]到[N-1,N-1],假定[0,0]為入口,[N-1,N-1]為出口。
計算機里面,我們可以采用不遺漏可能路徑的辦法,就是深度優(yōu)先搜索。作為預(yù)備知識,先介紹一下深度優(yōu)先搜索和廣度優(yōu)先的概念。
深度優(yōu)先搜索DFS的思路是從起點開始,對每一個可能的分支路徑深入到不能再深入為止。而廣度優(yōu)先搜索BFS的思路是從起點開始,先把臨近的節(jié)點走完,再對臨近節(jié)點同樣走。比如:
?
我們對上面的圖,如何一個不漏地數(shù)出來?
按照深度優(yōu)先,從a開始,接下來是臨近點b,然后對b同樣操作,到d,再到e。最后成為一個串:abdeghicf。
按照廣度優(yōu)先,從a開始,接下來是b,然后下一格臨近點c,這樣第一層的臨近點走完了,就對b和c同樣操作。最后成為一個串:abcdefghi。
詳細的討論在講到數(shù)據(jù)結(jié)構(gòu)的時候再說。
深度優(yōu)先搜索是一種思路,保證一個不漏,其實是一種很笨的辦法,但是很有用,因為經(jīng)常會碰到?jīng)]有什么好辦法的場景,我們就利用計算機的快進行窮舉搜索。我們具體編程也有不同的選擇,下面介紹遞歸的辦法進行實現(xiàn)。
我們先看怎么定義迷宮。我們認為迷宮是一個N*N格的矩形,有墻有空,我們可以用二維數(shù)組表格這些格,1表示墻,不能走,0表示空,可以走。假定左上角為入口,右下角為出口。
在Python中,定義二維數(shù)組不難,但是有小小的陷阱在其中。最直接的定義辦法是array2=[[0,0,0],[0,0,0],[0,0,0]],這樣給出一個3*3的二維數(shù)組。但是作為一個有臉面的程序員,一般不會這么寫程序,總是不想這么寫死,而是想著用更加通用的方式。這個二維數(shù)組里面的一維數(shù)組是[0,0,0],可以縮寫成array2=[[0]*3,[0]*3,[0]*3]。再看一下,這三個一維數(shù)組都是一樣的嘛,所以就會進一步縮寫成array2=[[0]*3]*3。
注意了,陷阱來了,上面看似定義好了一個3*3的二維數(shù)組,你現(xiàn)在給它賦值:array2[0][0]=1,再看二維數(shù)組內(nèi)容,竟然成了:[[1, 0, 0], [1, 0, 0], [1, 0, 0]]。每一個一維數(shù)組的第一個數(shù)都成了1。這就要用引用與值來解釋了,一維數(shù)組本身是對象引用,*3會解釋成把[0,0,0]這個一維數(shù)組引用三遍,修改它的值,別的引用都變了。
正確的定義方式是array2= [ [0] * 3 for i in range(3)]。所以,我們這樣定義一個6*6的迷宮:
Mazewidth = 6Maze = [[0] * (Mazewidth) for i in range(Mazewidth)]我們程序還要用一個同樣的數(shù)組來記錄走過的路徑。Route = [[0] * (Mazewidth) for i in range(Mazewidth)]。我們反過來,用1表示走過的路徑。
深度優(yōu)先很好理解,相當(dāng)于用臨近節(jié)點替換當(dāng)前節(jié)點繼續(xù)操作下去。過程為:
(1) 從入口節(jié)點開始,設(shè)為當(dāng)前狀態(tài);
(2) 按照一定次序(右下左上)選擇到下一個節(jié)點,新節(jié)點設(shè)為當(dāng)前狀態(tài),遞歸;
(3) 判斷當(dāng)前狀態(tài)是否和前面的重復(fù),如果重復(fù)則回到上一個狀態(tài),按照一定次序(右下左上)產(chǎn)生它的另一狀態(tài);
(4) 判斷當(dāng)前狀態(tài)是否為目標狀態(tài),如果是目標,則找到一個解答,結(jié)束算法。
我們用一個遞歸函數(shù)move(Maze,Route,i,j),Maze為迷宮,Route為走過的路徑,i,j為當(dāng)前節(jié)點。
按照我們的假定,右下角為出口,判斷目標很簡單:
????if i == Mazewidth-1 and j == Mazewidth-1:Route[i][j] = 1return下一個節(jié)點的選擇,按照右下左上,就是改變節(jié)點坐標為j+1,i+1,j-1,i-1。如探索右邊的格子,可以這樣做:
????if Route[i][j+1] == 0:?#右邊的格子空Route[i][j+1] = 1?#記錄路徑j(luò) = j+1move(Maze,Route,i,j)?#遞歸自然,我們還不要忘記不能撞墻,還不要越界。所以提供如下一個函數(shù):
def issafe(Maze,i,j):if i >= 0 and i < Mazewidth and j >= 0 and j < Mazewidth ?and Maze[i][j] == 0:return 1return 0把這些組合起來,得到如下走迷宮的函數(shù):
def move(Maze,Route,i,j):if i == Mazewidth-1 and j == Mazewidth-1:Route[i][j] = 1for row in Route:?#打印路徑print(' '.join([str(elem) for elem in row]))returnif issafe(Maze,i,j+1) == 1 and Route[i][j+1] == 0:?#rightRoute[i][j+1] = 1j = j+1move(Maze,Route,i,j)else:if issafe(Maze,i+1,j) == 1 and Route[i+1][j] == 0:?#downRoute[i+1][j] = 1i = i+1move(Maze,Route,i,j)else:if issafe(Maze,i,j-1) == 1 and Route[i][j-1] == 0:?#leftRoute[i][j-1] = 1j = j-1move(Maze,Route,i,j)else:if issafe(Maze,i-1,j) == 1 and Route[i-1][j] == 0:?#upRoute[i-1][j] = 1i = i-1move(Maze,Route,i,j)else:return好,程序完成了,測試一下:
Mazewidth = 6Maze = [[0] * (Mazewidth) for i in range(Mazewidth)]Route = [[0] * (Mazewidth) for i in range(Mazewidth)]#Presetting the mazeMaze[1][0] = 1Maze[1][1] = 1Maze[1][2] = 1Maze[1][3] = 1Maze[1][4] = 1Maze[2][3] = 1Maze[2][4] = 1Maze[3][1] = 1Maze[4][1] = 1Maze[4][2] = 1Maze[4][3] = 1Maze[4][4] = 1Maze[4][5] = 1for row in Maze:print(' '.join([str(elem) for elem in row]))print('-----------')Route[0][0] = 1move(Maze,Route,0,0)?
運行結(jié)果為:
0 0 0 0 0 01 1 1 1 1 00 0 0 1 1 00 1 0 0 0 00 1 1 1 1 10 0 0 0 0 0-----------1 1 1 1 1 10 0 0 0 0 11 1 1 0 0 11 0 1 1 1 11 0 0 0 0 01 1 1 1 1 1?
上面是迷宮,下面是走過的路徑。
總結(jié)
以上是生活随笔為你收集整理的更多数学趣题:走迷宫的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网易Python爬虫:爬取网易科技频道文
- 下一篇: android 连接tftp 服务器