一、介紹a*演算法
學過資料結構的人都知道,圖的搜尋演算法主要有深度優先演算法和廣度優先演算法。廣度優先是從初始節點一層一層向下找,直到找到目標為止。深度優先是按照一定的順序前查詢完節點的乙個分支,再查詢另乙個分支,以至找到目標為止。在廣度優先中,整個搜尋好似乙個圓形向外展開,直到到達目標節點,這樣求解雖然一定能找到最優解,但節點展開的數量是和距離成級數增加的, 這對計算機記憶體和cpu資源要求非常大,運算效率太低,可以說這個演算法並不實用。
這時可考慮啟發式搜尋演算法。而 a* 演算法實際就是一種啟發式搜尋。
所謂啟發式搜尋,就是利用乙個估價函式評估每次的的決策的價值,決定先嘗試哪一種方案, 這樣可以極大的優化普通的廣度優先搜尋。舉例來說,從出發點(a)到目的地(b)的最短距離是固定的,我們可以寫乙個函式 judge() 估計(a)到(b)的最短距離,如果程式已經嘗試著從出發點(a) 沿著某條路線移動到了(c) 點,那麼我們認為這個方案的(a)到(b)間的估計距離為(a) 到(c) 實際已經行走了的距離(h)加上用 judge() 估計出的(c) 到(b) 的距離。 如此,無論我們的程式搜尋展開到哪一步,都會算出乙個評估值, 每一次決策後,將評估值和等待處理的方案一起排序,然後挑出待處理的各個方案中最有可能是最短路線的一部分的方案展開到下一步,一直迴圈到物件移動到目的地,或所有方案都嘗試過卻沒有找到一條通向目的地的路徑則結束。
由此可見,在啟發式搜尋中,對距離的估價是十分重要的。採用了不同的估價策略可以有不同的效果。我們先看看估價是如何表示的。
啟發中的估價是用估價函式表示的,如:
f(n) = g(n) + h(n)
其中f(n)是節點n的估價函式,g(n)實在狀態空間中從初始節點到n節點的實際代價,h(n)是從n到目標節點最佳路徑的估計代價。在這裡主要是h(n)體現了搜尋的啟發資訊,因為g(n)是已知的。如果說詳細點,g(n)代表了搜尋的廣度的優先趨勢。
在啟發式搜尋中的估價函式非常的重要,如何保證一定能找到最短路徑呢?充要條件是, 估價函式算出的兩點間的距離必須小於等於實際距離。這個可以從數學上嚴格證明。有如果你的估價函式不滿足這點,就只能叫做 a 演算法,並不能保證最後的結果是最優的,但它可能速度非常的快,所以在不一定要求最優解的問題中,也有實用性。 但無疑,滿足上敘充要條件的 a* 演算法中,估計值越接近真實值的估價函式就做的越好。
二、a*演算法偽**
如圖有如下的狀態空間:(起始位置是a,目標位置是p,字母後的數字表示節點的估價值)
搜尋過程中設定兩個表:open和closed。open表儲存了所有已生成而未考察的節點,closed表中記錄已訪問過的節點。演算法中有一步是根據估價函式重排open表。這樣迴圈中的每一步只考慮open表中狀態最好的節點。具體搜尋過程如下:
1)初始狀態:
open=[a5];closed=;
2)估算a5,取得搜有子節點,並放入open表中;
open=[b4,c4,d6];closed=[a5]
3)估算b4,取得搜有子節點,並放入open表中;
open=[c4,e5,f5,d6];closed=[b4,a5]
4)估算c4;取得搜有子節點,並放入open表中;
open=[h3,g4,e5,f5,d6];closed=[c4,b4,a5]
5)估算h3,取得搜有子節點,並放入open表中;
open=[o2,p3,g4,e5,f5,d6];closed=[h3,c4,b4,a5]
6)估算o2,取得搜有子節點,並放入open表中;
open=[p3,g4,e5,f5,d6];closed=[o2,h3,c4,b4,a5]
7)估算p3,已得到解;
看了具體的過程,再看看偽程式吧。演算法的偽程式如下:
best_first_search()
for (每乙個x的子節點y)
//還沒有排序
else if (y在open表中)
else //y在close表中
}將x節點插入close表中;
按照估價值將open表中的節點排序;}}
} 三、a*演算法實現(一)
void astarpathfinder::findpath(int sx, int sy, int dx, int dy)
path = bestnode;
}//生成子節點函式:
void astarpathfinder::generatesuccessors(node *bestnode, int dx, int dy)
void astarpathfinder::generatesucc(node *bestnode,int x, int y, int dx, int dy)
}else
//在closed表中嗎?
// if equal to null then not in open list, else it returns the node in old
if ( (old=checkclosed(tilenums)) != null )
}//不在open表中也不在close表中
else}
四、a*演算法實現(二)
/* 雲風的求解最短路徑** (cloud wu's pathfinding code)*/
#define mapmaxsize 100 //地圖面積最大為 100x100
#define maxint 8192 //定義乙個最大整數, 地圖上任意兩點距離不會超過它
#define stacksize 65536 //儲存搜尋節點的堆疊大小
#define tile_num(x,y) ((y)*map_w+(x)) //將 x,y 座標轉換為地圖上塊的編號
#define tile_x(n) ((n)%map_w) //由塊編號得出 x,y 座標
#define tile_y(n) ((n)/map_w)
// 樹結構, 比較特殊, 是從葉節點向根節點反向鏈結
typedef struct node *tree;
struct node ;
typedef struct node2 *link;
struct node2 ;
link queue; // 儲存沒有處理的行走方法的節點
tree stack[stacksize]; // 儲存已經處理過的節點 (搜尋完後釋放)
int stacktop;
unsigned char map[mapmaxsize][mapmaxsize]; //地圖資料
int dis_map[mapmaxsize][mapmaxsize]; //儲存搜尋路徑時,中間目標地最優解
int map_w,map_h; //地圖寬和高
int start_x,start_y,end_x,end_y; //地點,終點座標
// 初始化佇列
void init_queue()
// 待處理節點入佇列, 依靠對目的地估價距離插入排序
void enter_queue(tree node,int f)
q=(link)malloc(sizeof(*q));
assert(queue);
q->f=f,q->node=node,q->next=p;
father->next=q;}
// 將離目的地估計最近的方案出佇列
tree get_from_queue()
// 釋放申請過的所有節點
void freetree()}
// 估價函式,估價 x,y 到目的地的距離,估計值必須保證比實際值小
int judge(int x,int y)
// 嘗試下一步移動到 x,y 可行否
int trytile(int x,int y,tree father)
h=father->h+1;
if (h>=dis_map[y][x]) return 1; // 如果曾經有更好的方案移動到 (x,y) 失敗
dis_map[y][x]=h; // 記錄這次到 (x,y) 的距離為歷史最佳距離
// 將這步方案記入待處理佇列
p=(tree)malloc(sizeof(*p));
p->father=father;
p->h=father->h+1;
p->tile=tile_num(x,y);
enter_queue(p,p->h+judge(x,y));
return 0;}
// 路徑尋找主函式
void findpath(int *path)
x=tile_x(root->tile);
y=tile_y(root->tile);
if (x==end_x && y==end_y) break; // 達到目的地成功返回
child=trytile(x,y-1,root); //嘗試向上移動
child&=trytile(x,y+1,root); //嘗試向下移動
child&=trytile(x-1,y,root); //嘗試向左移動
child&=trytile(x+1,y,root); //嘗試向右移動
if (child!=0)
pop_stack(); // 如果四個方向均不能移動,釋放這個死節點
}// 回溯樹,將求出的最佳路徑儲存在 path 中
for (i=0;root;i++)
path[i]=-1;
freetree();}
void printpath(int *path)}
int readmap()
void showmap()
KMP演算法初學
kmp演算法是一種改進的模式匹配演算法,與樸素的模式演算法 即模式串與主串逐一匹配,匹配失敗模式串整體右移一位再次逐一匹配至完全匹配成功 相比,主串中指標無需回溯,時間複雜度為o n m n為主串長,m為模式串長 樸素為o n m 一 next陣列kmp演算法中多了乙個next陣列,要用kmp演算法...
初學KMP演算法
從書上看完kmp之後發現還是有部分不懂,發現kmp比樸素演算法強在不用次次回溯,可以通過向後延伸來進行比較,另外,kmp的演算法改進版nextval還不是特別理解,明天繼續看。簡述求解next 先寫出maxl 通過標出標號對t串進行標記從1開始 頭和尾重合幾個maxl就寫幾 就像這樣,當序號為1時,...
初學floyd演算法
從浙江大學資料結構課程上初學floyd多源最短路演算法。下面記錄下我的理解 dk i j 記為在編號k頂點的加入下,i j的最短路徑,所以假設有n個元素,k為從0開始一直到n 1。從d0一直到dn 1 i j 一直遞推就給了i到j的最短距離。一開始的d矩陣可以初始化為鄰接矩陣,沒有變相鄰的位置就可移...