搜尋演算法是利用電腦的高效能來有目的的窮舉一個問題的部分或所有的可能情況,從而求出問題的解的一種方法。搜尋過程實際上是根據初始條件和擴充規則構造一棵解答樹並尋找符合目標狀態的節點的過程。
所有的搜尋演算法從其最終的演算法實現上來看,都可以劃分成兩個部分──控制結構和產生系統,而所有的演算法的最佳化和改進主要都是通過修改其控制結構來完成的。現在主要對其控制結構進行討論,因此對其產生系統作如下約定:
Function ExpendNode(Situation:Tsituation;ExpendWayNo:Integer):TSituation;
表示對給出的節點狀態Sitution採用第ExpendWayNo種擴充規則進行擴充,並且返回擴充後的狀態。
(本文所採用的演算法描述語言為類Pascal。)
一、回溯演算法
回溯演算法是所有搜尋演算法中最為基本的一種演算法,其採用了一種“走不通就迴轉”思想作為其控制結構,其相當於採用了先根遍曆的方法來構造解答樹,可用於找解或所有解以及最優解。具體的演算法描述如下:
[非遞迴演算法]
CODE: |
|
<Type> Node(節點類型)=Record Situtation:TSituation(當前節點狀態); Way-NO:Integer(已使用過的擴充規則的數目); End <Var> List(回溯表):Array[1..Max(最大深度)] of Node; pos(當前擴充節點編號):Integer; <Init> List<-0; pos<-1; List[1].Situation<-初始狀態; <Main Program> While (pos>0(有路可走)) and ([未達到目標]) do Begin If pos>=Max then (資料溢出,跳出主程式); List[pos].Way-NO:=List[pos].Way-No+1; If (List[pos].Way-NO<=TotalExpendMethod) then (如果還有沒用過的擴充規則) Begin If (可以使用當前擴充規則) then Begin (用第way條規則擴充當前節點) List[pos+1].Situation:=ExpendNode(List[pos].Situation, List[pos].Way-NO); List[pos+1].Way-NO:=0; pos:=pos+1; End-If; End-If Else Begin pos:=pos-1; End-Else End-While; |
|
[遞迴演算法]
CODE: |
|
Procedure BackTrack(Situation:TSituation;deepth:Integer); Var I :Integer; Begin If deepth>Max then (空間達到極限,跳出本過程); If Situation=Target then (找到目標); For I:=1 to TotalExpendMethod do Begin BackTrack(ExpendNode(Situation,I),deepth+1); End-For; End; |
|
範例:一個M*M的棋盤上某一點上有一個馬,要求尋找一條從這一點出發不重複的跳完棋盤上所有的點的路線。
評價:回溯演算法對空間的消耗較少,當其與分枝定界法一起使用時,對於所求解在解答樹中層次較深的問題有較好的效果。但應避免在後繼節點可能與前繼節點相同的問題中使用,以免產生迴圈。
二、深度搜尋與廣度搜尋
深度搜尋與廣度搜尋的控制結構和產生系統很相似,唯一的區別在於對擴充節點選取上。由於其保留了所有的前繼節點,所以在產生後繼節點時可以去掉一部分重複的節點,從而提高了搜尋效率。這兩種演算法每次都擴充一個節點的所有子節點,而不同的是,深度搜尋下一次擴充的是本次擴充出來的子節點中的一個,而廣度搜尋擴充的則是本次擴充的節點的兄弟節點。在具體實現上為了提高效率,所以採用了不同的資料結構.
[廣度搜尋]
CODE: |
|
<Type> Node(節點類型)=Record Situtation:TSituation(當前節點狀態); Level:Integer(當前節點深度); Last :Integer(父節點); End <Var> List(節點表):Array[1..Max(最多節點數)] of Node(節點類型); open(總節點數):Integer; close(待擴充節點編號):Integer; New-S:TSituation;(新節點) <Init> List<-0; open<-1; close<-0; List[1].Situation<- 初始狀態; List[1].Level:=1; List[1].Last:=0; <Main Program> While (close<open(還有未擴充節點)) and (open<Max(空間未用完)) and (未找到目標節點) do Begin close:=close+1; For I:=1 to TotalExpendMethod do(擴充一層子節點) Begin New-S:=ExpendNode(List[close].Situation,I); If Not (New-S in List) then (擴充出的節點從未出現過) Begin open:=open+1; List[open].Situation:=New-S; List[open].Level:=List[close].Level+1; List[open].Last:=close; End-If End-For; End-While; |
|
[深度搜尋]
CODE: |
|
<Var> Open:Array[1..Max] of Node;(待擴充節點表) Close:Array[1..Max] of Node;(已擴充節點表) openL,closeL:Integer;(表的長度) New-S:Tsituation;(新狀態) <Init> Open<-0; Close<-0; OpenL<-1;CloseL<-0; Open[1].Situation<- 初始狀態; Open[1].Level<-1; Open[1].Last<-0; <Main Program> While (openL>0) and (closeL<Max) and (openL<Max) do Begin closeL:=closeL+1; Close[closeL]:=Open[openL]; openL:=openL-1; For I:=1 to TotalExpendMethod do(擴充一層子節點) Begin New-S:=ExpendNode(Close[closeL].Situation,I); If Not (New-S in List) then (擴充出的節點從未出現過) Begin openL:=openL+1; Open[openL].Situation:=New-S; Open[openL].Level:=Close[closeL].Level+1; Open[openL].Last:=closeL; End-If End-For; End; |
|
範例:迷宮問題,求解最短路徑和可通路徑。
評價:廣度搜尋是求解最優解的一種較好的方法,在後面將會對其進行進一步的最佳化。而深度搜尋多用於只要求解,並且解答樹中的重複節點較多並且重複較難判斷時使用,但往往可以用分支定界或回溯演算法代替。