大家好,我是小鴨醬,博客地址為:http://www.cnblogs.com/xiaoyajiang
這是大二時候的數學模型畢業課程設計,我選擇了自己研究盲人穿越迷宮的問題。當然后來再在網上查了這個問題研究比較成熟了,但是自己研究出來了也是小有成果的。
當時是用C++實現的,而且用了不必要的遞歸來進行。
現簡化為簡單的循環,並使用python實現了。
以下進行我自己設計算法的思路設計,最后附上現在的python源碼
“@”表示牆,“·”表示迷宮中可行路線上的空格,走過的路線用“X”表示。本問題只研究12×12規模且只有一個入口和一個出口(迷宮也許會不通)的迷宮。如何從入口走到出口?
算法分析
問題一, 迷宮構造
首先,我用二維字符型數組來表示迷宮的牆和可行路
其次,可由系統產生0到1的隨機數來控制每一個位置產生牆的概率,當然,如果要使概率可變動,那也無非是傳遞一個值而已。為使問題簡單化,我將產生牆的概率設置為30%。
第三,將第一行和最后一行全部設置為牆,第一列和最后一列均只有一個可行路。
問題二, 穿越方式
分析
這類問題解決的方法應該很多,但就我現在掌握的以及現在的靈感,我只能想到蠻力的辦法。
解決
一直沿着穿越者的右手邊的牆壁走,如若此迷宮是通的,那一定能找到出口!
用遞歸的辦法,我只需要研究當前的位置到下一步的過程就可以了。
問題三, 方向控制
分析
1、我設置的是二維數組,其動態必須通過數組的下標來控制
2、對於穿越者來說,他的方向只有前后左右,人在其中必定是不知道自己的坐標的
3、有了方向才不能迷失,這對於我們的穿越者也同樣如此,每一次作的決策就是選擇方向,即向左轉90°還是不轉還是向右轉90°,這里規定穿越者一次只能選擇其中一種,向左轉和向右轉的次數從最開始就累計,且一次向左轉和一次向右轉可抵消為不轉
4、由於迷宮是隨機構造的,所以也有可能出現類似漩渦一樣的路徑,所以累計的向左轉或者向右轉的次數也有可能超過3次,即在空間存在轉的角度超過360°,那么就需要研究同一方向上的轉動次數的規律。
解決
根據以上分析,需要將做決策時穿越者的狀態,具體的說就是穿越者面向的方向,用坐標絕對位置來表示出來。
規定初始位置人的朝向是坐標方位向右用sum=0表示;
每一次穿越者向自己的左邊轉一次,sum就減1;
每一次穿越者向自己的右邊轉一次,sum就加1;
將同一方向上的數字進行研究,可以發現其內在的規律:
絕對向右的數字有…-12,-8,-4,0,4,8,12…=4n
絕對向上的數字有…-13,-9,-5,-1,3,7,11…=4n-1
絕對向左的數字有…-14,-10,-6,-2,2,6,10…=4n-2
據對向下的數字有…-11,-7,-3,1,5,9,13…=4n+1
由上觀察,我發現余數sum%4與方向是對應的,就是說余數可以作為方向的判斷標志,根據C++對%的定義和用法,發現余數與被除數的符號一致,所以這樣每個方向上有兩個余數(絕對向右的除外,因為其余數為0),且它們都區別於其他方向上的余數。
這樣容易得出——
設人的當前坐標為a[i][j]
sum%4=0時,人絕對向右,此時若向前走一步,則為a[i][j+1]
sum%4= 3 or -1 時,人絕對向上,此時若向前走一步,則為a[i-1][j]
sum%4= -3 or 1 時,人絕對向下,此時若向前走一步,則為a[i+1][j]
sum%4= -2 or 2 時,人絕對向左,此時若向前走一步,則為a[i][j-1]
問題四,決策分析
過程決策
分析1
假設前面的路都沒有走過
這是核心問題。
這里分析時假定此時人的當前絕對方位是面向右的。
1、 當前穿越者位置特征分析
可以肯定,當前位置特征必定是穿越者的右邊是一堵牆
2、
穿越者前
1、 前面環境分析
根據排列組合知識可知,有4種情況,即
1 2 3 4
X· X@ X @ X·
@@ @@ @· @·
人前有路,路右有牆 人前有牆,牆右有牆 人前有牆,牆右有路 人前有路,路右有路
解決1
根據分析,決策可分為三類,即將2和3合並
1 2 3
X· X @ X @ X·
@@ @@ @· @·
針對以上3種情況,可有與之相對應的3種決策
對於1,前進一步
對於2,向左轉
對於3,前進一步,向右轉,前進一步
這樣,可以看到經過這樣的決策,可使得人的右面是牆,便可重復進行上面的決策,遞歸便有作用了。整個穿越過程,便是以上過程的循環。
分析2與解決2
如果前面的路走過,即前方有“X”,將其與“.”一同看待處理。
問題五,決策分析
邊界決策
分析
根據問題四,可對過程進行決策,但對於遞歸問題,還有關鍵的一點就是邊界分析決策。可以注意到,此問題由於是隨機產生的迷宮,所以存在不通路的情況,也就是可能會走回出發點。雖然結果也許我們認為是不同的,但是對於數組來說,都是越界。
解決
每一次都對其進行越界判斷。如果下一步的坐標經判斷是越界的,就不再需要進行問題四的操作了,就可以直接輸出二維數組了,我們的問題也就解決了!
python實現如下:
1 #!usr/bin/python 2 #Filename: maze.py 3 import random 4 a = [['#']*12 for i in range(12)] 5 turn = [[0,1],[1,0],[0,-1],[-1,0]]#right,down,left,up; turn[i+1] means you turn right 6 for i in range(1,11): 7 for j in range(1,11): 8 if random.random() > 0.3:#the wall is generated with the prosibility of 0.3 9 a[i][j] = ' ' 10 a[1][1] = a[10][11] = a[10][10] = ' '#the maze is generated completely 11 raw = 1#current raw number 12 column = 0#current column number 13 face = 0#0 means right, 1 means down, 2 means left, 3 means up 14 a[raw][column] = 'X' 15 while column < 11: 16 if face == 3: 17 r_face = 0 18 else: 19 r_face = face + 1 20 if face == 0: 21 l_face = 3 22 else: 23 l_face = face - 1 24 ft_r = raw + turn[face][0] 25 ft_c = column + turn[face][1] 26 ftrt_r = ft_r + turn[r_face][0] 27 ftrt_c = ft_c + turn[r_face][1] 28 if a[ft_r][ft_c] == '#': 29 face = l_face 30 elif a[ftrt_r][ftrt_c] == '#': 31 raw = ft_r 32 column = ft_c 33 a[raw][column] = 'X' 34 else: 35 raw = ftrt_r 36 column = ftrt_c 37 a[ft_r][ft_c] = a[raw][column] = 'X' 38 face = r_face 39 if column == 0: 40 print "The maze is death!\n" 41 break 42 for i in range(0,12): 43 for j in range(0,12): 44 print a[i][j],' ', 45 print '\n'
