選修了cs的AI課,開始有點不適應,只能用matlab硬着頭皮上了,不過matlab代碼全網僅此一份,倒有點小自豪。
一、練習題目
分別用寬度優先、深度優先、貪婪算法和 A*算法求解“羅馬利亞度假問題”。具體地圖我這里不給出了,有興趣的可以去搜索。即找到從初始地點 Arad到 目的地點 Bucharest 的一條路徑。
要求:分別用文件存儲地圖和啟發函數表,用生成節點數比較以上四種算法在同一問題求解時的效率,列表給出結果。
附:羅馬尼亞度假問題圖(圖1.1)
圖1.1 羅馬尼亞度假問題
1.2 題目分析
本題要求分別用寬度優先、深度優先、貪婪算法和 A*算法求解“羅馬利亞度假問題”。羅馬尼亞度假問題本質上屬於“圖類”問題,該地圖上共有20個地點,要求從Arad出發,到達Bucharest,從圖中搜索到達路徑,並比較四種方法的優缺點。因此主要的數據結構可以采用圖存儲的方法,搜索方法題目已經給出。
二、數據結構
2.1 圖結構
圖:由有窮、非空點集和邊集合組成,簡寫成G(V,E),其中,G表示一個圖,V表示圖中的頂點,E表示圖中的邊。在本題,頂點為20個羅馬尼亞城市,邊則為相鄰城市之間的距離。邊之間有方向,圖為有向圖,無方向的圖為無向圖。本題所用的圖為無向圖。
盡管二維數組比較占用內存,但是由於MATLAB對矩陣運算非常方便,運算速度也很快,我采用二維數組的方法存儲鄰接矩陣。對20個地點編號1-20,其中Arad為3號地點,對邊采用數值的方法,例如3號到4號距離為75,則令矩陣中點(3,4)的值為75。並令自身距離為0,不相鄰的點之間也設為0。
2.2 隊列結構
隊列(Queue):是只允許在一端進行插入操作,在另一端進行刪除操作的線性表。隊列也是一種特殊的線性表,是一種先進先出的線性表。允許插入的一端稱為表尾,允許刪除的一端稱為表頭。我們將在廣度優先搜索中使用到這個結構存儲已搜索過的節點。其結構如圖2.2所示。
圖2.2 隊列結構圖
2.3 棧結構
棧(Stack):也是一種線性存儲結構,棧中的數據元素遵守“先進后出”(First In Last Out)的原則,簡稱FILO結構。只能在棧頂進行插入和刪除操作。我們將在深度優先搜索中使用到這個結構存儲已搜索過的節點。其結構如圖2.3所示。
圖2.3 棧結構圖
三、算法思想
3.1 寬度優先
寬度優先搜索算法(又稱廣度優先搜索)其別名又叫BFS( Breadth First Search)。屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。算法采用隊列的數據結構,所有因為展開節點而得到的子節點都會被加進一個先進先出的隊列中。其鄰居節點尚未被檢驗過的節點會被放置在一個被稱為 open 的隊列,而被檢驗過的節點則被放置在被稱為 closed 的容器中(open-closed表)算法自始至終一直通過已找到和未找到頂點之間的邊界向外擴展,首先搜索和s距離為k的所有頂點,然后再去搜索和S距離為k+l的其他頂點。算法流程圖如圖3.1所示。
圖3.1 DFS算法流程圖
3.2 深度優先
深度優先搜索方法,又稱DFS(Depth First Search),和樹的先序遍歷比較類似。假設初始狀態是圖中所有頂點均未被訪問,則從某個頂點v出發,首先訪問該頂點,然后依次從它的各個未被訪問的鄰接點出發深度優先搜索遍歷圖,直至圖中所有和v有路徑相通的頂點都被訪問到。 若此時尚有其他頂點未被訪問到,則另選一個未被訪問的頂點作起始點,重復上述過程,直至圖中所有頂點都被訪問到為止。算法流程圖如圖3.2所示。
圖3.2 BFS算法流程圖
3.3 貪婪方法
貪婪算法(又稱貪心算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。為了解決問題,需要尋找一個構成解的候選對象集合,起初,算法選出的候選對象的集合為空。接下來的每一步中,根據選擇函數,算法從剩余候選對象中選出最有希望構成解的對象。如果集合中加上該對象后不可行,那么該對象就被丟棄並不再考慮;否則就加到集合里。每一次都擴充集合,並檢查該集合是否構成解。
本題中具體實現方法為,先進行深度搜索,但是不急進入堆棧操作,而是存儲當前所有搜索到的點的距離,選擇距離最短的點,並放棄搜索其他同一深度的點,進入堆棧操作。算法流程圖如圖3.3所示。
圖3.3 貪婪算法流程圖
3.4 A*方法
A*搜尋算法俗稱A星算法。A*算法是比較流行的啟發式搜索算法之一,被廣泛應用於路徑優化領域。它的獨特之處是檢查最短路徑中每個可能的節點時引入了全局信息,對當前節點距終點的距離做出估計,並作為評價該節點處於最短路線上的可能性的量度。
本題中的實現方法為,同貪婪類似,A*就相當於有一個智慧的老人為搜尋的對象打分,搜索過程中將距離和打分值相加,作為新的距離即可。其算法流程圖如3.4所示。
圖3.3 A*算法流程圖
四、關鍵代碼
4.1 BFS方法
while tail~=head %判斷 i=queue(tail); %取點 for j=1:20 %搜索所有適合的節點 if A(i,j)>=1 && isempty(find(flag==j,1)) queue(head)=j; head=head+1; %數數 flag=[flag j]; %擴容 result=[result;i,j,A(i,j)]; %記錄 end end tail=tail+1; %隊列增加 end
4.2 DFS方法
while top~=0 %判斷 pre_len=length(stack); %記錄棧長度 i=stack(top); %取棧頂 for j=1:20 if A(i,j)>=1 && isempty(find(flag==j,1)) top=top+1; stack(top)=j; flag=[flag j]; re=[re;i,j,A(i,j)]; %記錄
break; end end if length(stack)==pre_len %如果棧未增加,則出棧 stack(top)=[]; top=top-1; end end
4.3 貪婪方法
while top~=0 pre_len=length(stack); i=stack(top); record=[]; for j=1:20 if A(i,j)>=1 && isempty(find(flag==j,1)) %記錄所有相鄰節點 record=[record;i,j,A(i,j)] end end if isempty(record) break end [s,k]= min(record(:,3,:)) %取距離最小節點 i=record(k,1,:); j=record(k,2,:); if isempty(find(flag==j,1)) top=top+1; stack(top)=j; flag=[flag j]; re=[re;i,j,A(i,j)]; end if length(stack)==pre_len stack(top)=[]; top=top-1; end end
4.4 A*方法
絕大部分與貪婪算法相同,只是更新了距離值。
for j=1:20 if A(i,j)>=1 && isempty(find(flag==j,1)) F(i,j)=A(i,j)+H(j); record=[record;i,j,F(i,j)]; %啟發值 end end if isempty(record) break end
4.5 反向尋址
所有的算法均采用相同的反向尋址方法。
while (1) x=find(re(:,2,:)==m) %找到到達目的地所有的經過地 m=re(x,1,:) %迭代法反向尋找來的路徑 if 1-isempty(x) lujin=[city{re(x,1,:)},lujin]; juli=juli+re(x,3,:) else break end end
4.6 讀取EXCEL
city={'Oradea','Zerind','Arad','Timisonra','Lugoj','Mehadia','Dobreta','Craiova','Rimmicu Vikea','Sibiu',... 'Fagaras','Pitesti','Bucharest','Giurglu','Uiziceni','Hirsova','Eforie','Vaslui','Lasi','Neamt'}; %存儲城市名 filename = 'mymap.xlsx'; sheet = 1; xlRange = 'C3:V22'; map = xlsread(filename,sheet,xlRange); %讀取excel map(isnan(map)) = 0; %將不相鄰的點之間也設為0
五、運行結果
BFS方法的運行結果顯示路徑為:{'Arad' 'Sibiu' 'Fagaras' 'Bucharest'}
DFS方法的運行結果顯示路徑為:{ 'Arad' 'Zerind' 'Oradea' 'Sibiu' 'Rimmicu Vikea' 'Craiova' 'Pitesti' 'Bucharest'}
貪婪方法的運行結果顯示路徑為:{ 'Arad' 'Zerind' 'Oradea' 'Sibiu' 'Rimmicu Vikea' 'Pitesti' 'Bucharest'}
A*方法的運行結果顯示路徑為:{ 'Arad' 'Sibiu' 'Rimmicu Vikea' 'Pitesti' 'Bucharest'}
比較見表5.1
表5.1 四種算法的運行結果
算法 |
生成節點數 |
求解時間 |
距離 |
BFS方法 |
11 |
3.725s |
450 |
DFS方法 |
12 |
3.057s |
762 |
貪婪方法 |
7 |
3.606s |
575 |
A*方法 |
7 |
3.049s |
418 |
注:求解時間包括計時函數自用時間
六、比較結論
得益於MATLAB高速的矩陣運算能力,四種方法均在3-4秒之間完成,速度相差不大,但是在生成節點數上,DFS方法搜索了12個節點最多,貪婪方法和A*方法均為7最少。比較四種搜索方法得到的搜索路徑,有啟發值的A*方法搜索到的路徑距離最短,為418,其次是寬度優先搜索,距離為450,距離最長的路徑是由DFS方法產生,為762,貪婪方法為575。通過比較我們可以得出如下結論:
- 四種搜索方法在處理小型網絡的搜索問題時,速度相差不大。
- 貪婪方法和A*方法生成節點數較少,理論上能夠更快搜索出到達路徑,在處理大型圖的問題時,會表現得比較明顯。
- 貪婪方法每一步都是選擇當前狀態下的最優解進行搜索,很容易陷入局部最優,從而使得搜索時間延長。
- 盡管BFS方法和DFS方法都一定可以找到路徑,但是BFS方法搜索到的路徑距離要明顯優於DFS方法。
---恢復內容結束---