關於Search和Uninformed Search

圖中的算法多的要命,大多數都是搜索算法吧,因爲其應用實在太廣了。就比如說機器人眼中的世界其實就是一張graph,graph的大小的有限的(這跟分配給它的memory有關),機器人就根據現有的graph信息邊計算邊決策,然後邊走邊獲取新graph信息,再重新計算再決策再走……這是一個迭代過程,直到到達目的地爲止,汽車導航儀也是類似的。我覺得這過程有點像你用一個英雄去打敵方老窩,在地圖還不是完全可見的情況下,你得親自控制着他,看好路,根據情況選擇對應的戰鬥策略。……哈哈扯遠了,這個其實算Informed Search了,也就是帶有決策的啓發式搜索了(我們將在下一篇中介紹)。我們先來介紹簡單的Uninformed Search。

 

什麼叫Search?Search就是在所有Search space(狀態空間——往往能用樹或圖來表示出)中找到你想要的那個狀態。

Uninformed Search,顧名思義就是消息不靈通的search,被矇蔽的search,也可以叫做Blind Search(盲目搜索)或者Brute-force Search(也就是傳說中的暴力解法)。

 

我們先來看兩個搜索的例子。

 

 

如圖,我們從A處進入這個空間,我們想要到達J處。當然我們人眼是肯定一眼就看穿路線了,但讓機器應該怎麼做?

計算機勢必得先把這張地圖信息存起來,格式只能是計算機能實現的數據結構。

 

 

 

在這棵樹(也可以叫圖)上,我們把每一個節點視作一個搜索狀態,每一條邊視作一次搜索動作,只有能從初始節點到達目標節點就算搜索成功了。經典的算法莫過於深度優先搜索和廣度優先搜索了(我們將待會兒介紹)。

 

我們再來看一個例子。

有一種兩人遊戲叫Nim Game,規則是:在一根柱子上放N個大小相同的環,兩個人輪流從上面取環下來,規定每人每次最多隻能取連續的m個環,不能不取,誰取到最後一個環就算輸。

假定現在N=6, m=3,兩人遊戲過程如圖。想想看先取者和後取者誰的勝率較高?進一步再想想看先取者和後取者有沒有最有把握勝的策略。

 

 

 

我們將這個問題中所有的狀態空間用一棵樹來表示出。

 

所有的節點中的數字表示剩下的環的個數,剩下1則表示接下來取的那個人肯定輸了,圖中的邊則表示取下的環的個數。

可見,總共有13種不同的遊戲過程,先取者贏的概率爲7/13,後取者贏的概率爲6/13。更進一步,如果要制定最有把握勝的策略的話,這件事只要交給計算機去做好了。先把所有狀態空間都存起來,每當對手取完後,就找到取完那個狀態的節點,然後遍歷那個節點的子樹以計算出勝率最大的下一步操作。

這裏已經略微涉及到人機博弈的概念了(這個我還沒有研究過),我想基礎的東西肯定是大同小異的,只是複雜度提高了。大名鼎鼎的“深藍”挑戰國際象棋冠軍,它裏面存有了200萬張不同的棋局,配有200多個獨立的計算器(應該也是ALU吧),不管對手怎麼動,它都能在它的庫存中找到對應的最佳解法。

 

下面我們開始介紹真正的Uninformed Search方法吧。

 

1.  Depth-First Search(DFS)深度優先

 

經典算法啊,想必大家都懂的,這裏我只想提一下它的非遞歸實現方法。

還是要使用一個Stack

(1)把根節點入棧

(2)Pop一下,訪問該節點,然後把它的右子女和左子女(不爲NULL)依次Push進去

(3)重複(2),直到找到目標節點或棧爲空(沒有找到)。

爲了避免環的出現而使算法陷入死循環,可以引入一個visited數組。

顯然,DFS算法雖然一定能找到出現在圖中的節點,但效率很低。若要找F,DFS就相當於把整個圖都遍歷了一遍。

 

2.  Depth-Limited Search(DLS)

DLS其實是DFS的變體,它限制住了訪問的深度,在DFS算法的第(2)步中,若該節點當前深度大於限制深度,那就不再把它的子女Push進去了。

爲了實現此算法,又多開闢了一個深度Stack,它與節點Stack同步進行操作,Push節點的時候也把當前的深度Push進去,Pop同理。

顯然,DLS未必能找到目標節點。

 

3.  Iterative Deepening Search(IDS)

IDS其實又是DLS的變體了,它通過depth的增量(初始值和增量大小可視情況而定),迭代進行DLS算法,直到找到目標節點。

 

顯然當目標節點很深的情況下,IDS算法的效率肯定不如直接DFS的。但當深度很大,而且標節點處於中間位置或較淺位置時,或者深度未知的情況下,就能體現出IDS算法的價值了。

 

4.  Breadth-First Search(BFS)廣度優先

也是經典啊,通過隊列實現,不多說了。只說下,BFS也可以像DFS一樣引入一個visited數組,不是爲了避免死循環,而是可以避免有環的情況下重複訪問相同的節點,以加速搜索。

 

5.  Uniform-Cost Search(UCS)

這個算法適用與帶權圖中,爲了找出最短路徑。

如圖,找出從A到E的最短路徑。

 

我們需要一個優先隊列(也就是堆)來實現,裏面存放節點和當前到達該節點總共的Cost,按Cost來把堆調整爲最小堆。

(1)初始節點插入空堆

(2)出堆,訪問該節點,把它所有的子女(不爲NULL)依次入堆(Cost=當前節點的Cost + 與子女邊的權)

(3)重複(2),直到出堆的節點即爲目標節點,或堆爲空了(沒有找到)。

注意,因爲是堆,所以每次入堆出堆操作堆內都會進行調整。

 

這裏我們再來討論一種backtracking技術,因爲問題是通過此算法,我們只能找到最短路徑的長度是多少,而不知道這條路徑經過了哪些節點。其實最簡單的方法就是隻要讓每個節點記住它的parent就可以了。

我們需要的數據結構如下

struct Node

{

int node_id;

int current_total_cost;  // current_total_cost = 父節點current_total_cost + 邊上的權

Node* parent;

}

每次取當前節點的子女時,將parent指針指向當前節點就OK了。只要將賦值好的Node插入堆中就行了。

當算法結束時,取出的最後一個節點爲目標節點,然後通過它的parent指針以及parent的parent指針(迭代)一路可以找到初始節點(初始節點的parent當然爲NULL),然後按正向輸入節點id就OK了。

 

6.  Bidirectional Search 雙向搜索

顧名思義,就是從兩端同時搜索。但它有個前提條件,必須知道目標節點在哪(有指針指向它)。

 

它的基本思想是兩端都採用BFS搜索,直到兩條路徑碰頭。但現實中,若都採用BFS搜索,往往是不會碰頭的,即使碰頭的話,效率也非常低。

現實中的圖往往復雜到這樣

 

所以一般兩端都採用一種Informed Search叫A* Search(我們將在下篇中介紹)

 

Summary

Algorithm

Time Complexity

Space Complexity

Derivative

DFS

O(bm)

O(bm)

 

DLS

O(bl)

O(bl)

DFS

IDS

O(bd)

O(bd)

DLS

BFS

O(bd)

O(bd)

 

UCS

O(bd)

O(bd)

BFS

BIDIBidirectional 

O(bd/2)

O(bd/2)

BFS(兩端採用BFS)

b, branching factor

d, tree depth of the solution

m, tree depth

l, search depth limit

 

綜上這些算法,除了UCS(通過優先隊列來選擇當前Cost最小的節點優先訪問)帶有一點策略性,(BIDI當兩端採用A* Search時也具有策略性),其他算法都是不具備決策能力的盲目式搜索,當數據量很大時,顯然是低效的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章