最近要參加學校的APP比賽,我們組做的是一個3D迷宮的小APP,我負責的是迷宮的生成與尋路。
尋路算法選擇的是A*尋路算法,具體參考的是下面的這篇博客。
本文主要是談談自己對A*算法的理解,具體細節,上文鏈接以及講的很詳細了。
http://www.cnblogs.com/technology/archive/2011/05/26/2058842.html
關於A*算法的實現我是用的廣度優先搜索這種比較常見的實現模式。
那么關於這兩者之間的關系是怎么樣呢?
個人理解的是A*算法其實是一種帶有路徑信息的有策略的廣度優先搜索,我們平常用的廣度優先搜索,沒有帶有路徑信息,搜索起來是一種盲目的搜索。
例如,當尋路到A節點時,下一步是以A為中心,分別測試A周圍的8個點,並且又以它們為中心,繼續向外擴散,以期找到待尋找的點。
這樣的搜索帶有盲目性,測試了大量的無效的點。
A*算法在每個節點中加入了路徑的信息:
F:當前點的路徑信息,包含了到當前點到起點與到終點的信息。(F=G+H)
G:當前點到唯一的起點的路徑信息。
H:當前點到指定終點的距離信息。
那么我們怎么樣通過這些指定的路徑信息來避免廣度優先搜索的盲目選取下一個路徑點而得到一條最小路徑呢?
其實我們可以把這看成是一個最小路徑的“感染”問題。
F B C D
E A G H
I N K L
如上圖:
首先參照上文博客,水平方向移動一格路徑代價是10,斜方向移動一格路徑代價是14。
起點A的G值初始化0,這個當然,自己到自己的距離是0;其它點中的G初始化為INT_MAX;通過對A的廣度搜索,來設置各個節點的G值與父節點。
例:A的八個方向,FBCEGINK,選取出F最小的點,假設為G。
對G的八個方向,BCDAHNKL,我們同樣需要設定這8個方向的G值與父節點。這8個方向中與A重疊的有BCNK。
重疊意味着有兩條路徑,對於K來說,可以是A->K,也可以是A->G->K。
那么怎么樣選擇呢?
這時我們要判斷G的值,哪條路徑使得G的值最小,我們就選擇它。
G的值是起點A到本節點的路徑長度,這是一個累積的值,是前面各個G一路累積(感染)下來的,代表的是當前的路徑,我們選擇的肯定是使得K的G最小,這樣對整個最小路徑都是有利的,因為可以一直感染下去。
那么H的作用在哪里呢?
H僅僅是對路徑方向的一個大致的判斷,上圖中,若指定終點在A的右邊,那么EFI肯定不會被選中,這樣就不用盲目的8個方向都去判斷。
終上所述:
G和H的作用共同體現於F中,每次我們都是在廣度搜索中去尋找F最小的點,然后根據G的值,設置下一個廣度搜索中的節點的父節點,父節點永遠是使得子節點中的G值最小。(到起點A的路徑最短)。
自己寫的A*的核心函數代碼,測試curr節點的周圍8個節點,並且設置路徑及父節點信息,其余的部分則是廣度優先搜索的框架,這里不再寫出來。
1 void getSurrroundPoints(point& curr, list<point>& result) 2 { 3 int x = curr.m_x; //curr坐標信息
4 int y = curr.m_y; 5
6 for (int i = x-1;i <= x+1; i++) 7 { 8 for (int j = y - 1; j <= y + 1; j++) //8個方向
9 { 10 if (i == x && j == y) //循環到自己,跳過此次循環
11 continue; 12
13 if ( find_if(closeList.begin(), closeList.end(), pointEqualLocation(i, j)) == closeList.end() //當前節點還沒有被檢查過
14 &&canReach(i, j)) //當前節點不是障礙物
15 { 16 if (x == i || y == j) //上下左右四個方向 g=10
17 { 18 if ((curr.m_g + 10) >= point_ptr[i][j].m_g) 19 { 20 //start到(i,j)的 不必經過curr 21 //什么也不做,保留原來的路徑信息與父節點
22 } 23 else
24 { //start到(i,j)的 經過curr路徑會更小 25 //更新路徑信息 更改父節點
26 point_ptr[i][j].m_g = curr.m_g + 10; //更新節點中的G值
27 point_ptr[i][j].m_fx = curr.m_x; //設置父節點
28 point_ptr[i][j].m_fy = curr.m_y; 29
30 } 31 } 32 else //斜方向 g=14;
33 { 34 if ((curr.m_g + 14) >= point_ptr[i][j].m_g) 35 { 36 //start到(i,j)的 不必經過curr 37 //什么也不做,保留原來的路徑信息與父節點
38 } 39 else
40 { //start到(i,j)的 經過curr路徑會更小 41 //更新路徑信息 更改父節點
42 point_ptr[i][j].m_g = curr.m_g + 14; //更新節點中的G值
43 point_ptr[i][j].m_fx = curr.m_x; //設置父節點
44 point_ptr[i][j].m_fy = curr.m_y; 45
46 } 47 } 48 point_ptr[i][j].m_h = abs(endPoint_x - i) + abs(endPoint_y - j); //計算到終點的距離
49 point_ptr[i][j].m_f = point_ptr[i][j].m_g + point_ptr[i][j].m_h; //F=G+H
50
51 result.push_back(point_ptr[i][j]); //放到鏈表中 等待下次的檢查
52 } 53 } 54 } 55 }