A*算法,A*(A-Star)算法是一種靜態路網中求解最短路最有效的方法。估價值與實際值越接近,估價函數取得就越好。
A*[1](A-Star)算法是一種靜態路網中求解最短路最有效的方法。
公式表示為: f(n)=g(n)+h(n),
其中 f(n) 是從初始點經由節點n到目標點的估價函數,
g(n) 是在狀態空間中從初始節點到n節點的實際代價,
h(n) 是從n到目標節點最佳路徑的估計代價。
保證找到最短路徑(最優解的)條件,關鍵在於估價函數h(n)的選取:
估價值h(n)<= n到目標節點的距離實際值,這種情況下,搜索的點數多,搜索範圍大,效率低。但能得到最優解。
如果 估價值>實際值,搜索的點數少,搜索範圍小,效率高,但不能保證得到最優解。
工具/原料
DEVC++或 VC 6.0
方法/步驟
估價值與實際值越接近,估價函數取得就越好
例如對於幾何路網來說,可以取兩節點間歐幾理德距離(直線距離)做為估價值,即f=g(n)+sqrt((dx-nx)*(dx-nx)+(dy-ny)*(dy-ny));這樣估價函數f在g值一定的情況下,會或多或少的受估價值h的制約,節點距目標點近,h值小,f值相對就小,能保證最短路的搜索向終點的方向進行。明顯優於Dijkstra算法的毫無方向的向四周搜索。
conditions of heuristic
Optimistic (must be less than or equal to the real cost)
As close to the real cost as possible
詳細內容:
創建兩個表,OPEN表保存所有已生成而未考察的節點,CLOSED表中記錄已訪問過的節點。
算起點的估價值;
將起點放入OPEN表;
A star算法在靜態路網中的應用,以在地圖中找從開始點 s 到e點的最短行走路徑為例:
首先定義數據結構
#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 {
int h;
int tile;
TREE father;
};
typedef struct node2 *LINK;
struct node2 {
TREE node;
int f;
LINK next;
};
LINK queue; // 保存沒有處理的行走方法的節點
TREE stack[STACKSIZE]; // 保存已經處理過的節點 (搜索完後釋放)
int stacktop;
char map[][6]={{'x','x','x','x','x','x'},
{'x','e',' ',' ',' ','x'},
{'x','x',' ','x',' ','x'},
{'x','x',' ',' ',' ','x'},
{'x','x','x','x','s','x'},
{'x','x','x','x','x','x'} };//地圖數據
int dis_map[MAPMAXSIZE][MAPMAXSIZE];//保存搜索路徑時,中間目標地最優解
int map_w,map_h;//地圖寬和高
int start_x,start_y,end_x,end_y; //地點,終點座標
// 路徑尋找主函數
void findpath(int *path)
{
//printf("%d,%d,%d,%d",start_x,start_y,end_x,end_y);
TREE root;
int i,j;
stacktop=0;
for (i=0;i
for (j=0;j
dis_map[i][j]=MAXINT;
init_queue();
root=(TREE)malloc(sizeof(*root));
root->tile=tile_num(start_x,start_y);
root->h=0;
root->father=NULL;
enter_queue(root,judge(start_x,start_y));
for (;;) {
int x,y,child;
TREE p;
root=get_from_queue();
if (root==NULL) {
*path=-1;
return;
}
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]=root->tile;
root=root->father;
//printf("pathis %d",path[i]);
}
path[i]=-1;
freetree();
}
// 估價函數,估價 x,y 到目的地的距離,估計值必須保證比實際值小
int judge(int x,int y)
{
int distance;
distance=abs(end_x-x)+abs(end_y-y);
return distance;
}
// 嘗試下一步移動到 x,y 可行否
int trytile(int x,int y,TREE father)
{
TREE p=father;
int h;
if (map[y][x]=='x') return 1; // 如果 (x,y) 處是障礙,失敗
while (p) {
if (x==tile_x(p->tile) && y==tile_y(p->tile)) return 1; //如果 (x,y) 曾經經過,失敗
p=p->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;
}
打開c語言編譯器,輸入我們的運行代碼,編譯,運行如下,打印出地圖如下圖:
點任意鍵進行運行找靜態路網
說明:找到路後會存到一個數組中去,我們為了顯示這個過程可以運用打印函數打印出來代碼如下
void printpath(int *path)
{
int i;
//printf("-44444444444444");
for (i=0;path[i]>=0;i++) {
gotoxy(tile_x(path[i])+1,tile_y(path[i])+1);
printf("-");
Sleep(2000);
}
printf("\n");
printf("\n");
printf("走迷宮完成");
}
整個程序的代碼如下:
#include
#include"stdio.h"
#include
#include"assert.h"
#include"stdlib.h"
#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/*designde by [email protected]*/ node {
int h;
int tile;
TREE father;
};
typedef struct /*designde by [email protected]*/node2 *LINK;
struct node2 {
TREE node;
int f;/*designde by [email protected]*/
LINK next;
};
LINK queue; // 保存沒有處理的行走方法的節點
TREE stack[STACKSIZE]; // 保存已經處理過的節點 (搜索完後釋放)
int stacktop;
char map[][6]={{'x','x','x','x','x','x'},
{'x','e',' ',' ',' ','x'},
{'x','x',' ','x',' ','x'},
{'x','x',' ',' ',' ','x'},
{'x','x','x','x','s','x'},
{'x','x','x','x','x','x'} };//地圖數據
int dis_map/*designde by [email protected]*/[MAPMAXSIZE][MAPMAXSIZE];//保存搜索路徑時,中間目標地最優解
int map_w,map_h;//地圖寬和高
int start_x,start_y,end_x,end_y; //地點,終點座標
void gotoxy(int x ,int y)
{
HANDLE a;
COORD zb;
zb.X =x-1;
zb.Y =y-1;
a= GetStdHandle(STD_OUTPUT_HANDLE/*designde by [email protected]*/);
SetConsoleCursorPosition(a,zb);
}
// 初始化隊列
void init_queue()
{
queue=(LINK)malloc(sizeof(*queue));
queue->node=NULL;
queue->f=-1;
queue->next=(LINK)/*designde by [email protected]*/malloc(sizeof(*queue));
queue->next->f=MAXINT;
queue->next->node=NULL;
queue->next->next=NULL;
}
// 待處理節點入隊列, 依靠對目的地估價距離插入排序
void enter_queue(TREE node,int f)
{
LINK p=queue,father,q;
while(f>p->f) {
father=p;
p=p->next/*designde by [email protected]*/;
assert(p);
}
q=(LINK)malloc(sizeof(*q));
assert(queue);
q->f=f,q->node=node,q->next=p;
father->next=q;
}
// 將離目的地估計最近的方案出隊列
TREE get_from_queue()
{
TREE bestchoice=queue->next->node;
LINK next=queue->next->next;
/*designde by [email protected]*/free(queue->next);
queue->next=next;
stack[stacktop++]=bestchoice;
assert(stacktop
return /*designde by [email protected]*/bestchoice;
}
// 釋放棧頂節點
void pop_stack()
{
free(stack[--stacktop]);
}
// 釋放申請過的所有節點
void freetree()
{
int i;
LINK p;
for (i=0;i
free(stack);
while /*designde by [email protected]*/(queue) {
p=queue;
free(p->node);
queue=queue->next;
free(p);
}
}
// 估價函數,估價 x,y 到目的地的距離,估計值必須保證比實際值小
int judge(int x,int y)
{
int distance;
distance=abs(end_x-x)+abs(end_y-y);
return distance;
}
// 嘗試下一步移動到 x,y 可行否
int trytile(int/*designde by [email protected]*/ x,int y,TREE father)
{
TREE p=father;
int h;
if (map[y][x]=='x') return 1; // 如果 (x,y) 處是障礙,失敗
while (p) {
/*designde by [email protected]*/if (x==tile_x(p->tile) && y==tile_y(p->tile)) return 1; //如果 (x,y) 曾經經過,失敗
p=p->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)
{
//printf("%d,%d,%d,%d",start_x,start_y,end_x,end_y);
TREE root;
int i,j;
stacktop=0;
for (i=0;i
for (j=0;j
dis_map[i][j/*designde by [email protected]*/]=MAXINT;
init_queue();
root=(TREE)malloc(sizeof(*root));
root->tile=tile_num(start_x,start_y);
root->h=0;
root->father=NULL;
enter_queue(root,judge(start_x,start_y));
for (;;) {
int x,y,child;
TREE p;
root=get_from_queue();
if (root==NULL) {
*path=-1;
return;
}
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]=root->tile;
root=root->father;
//printf("pathis %d",path[i]/*designde by [email protected]*/);
}
path[i]=-1;
freetree();
}
void printpath(int *path)
{
int i;
//printf("-44444444444444");
for (i=0;path[i]>=0;i++) {
gotoxy(tile_x(path[i])+1,tile_y(path[i])+1);
printf("-");
Sleep(2000);
}
printf("\n");
printf("\n");
printf("走迷宮完成");
}
void readmap()
{ printf("走迷宮,s是起始點 e是終點 按任意鍵開始");
getchar();
//FILE *f;
int i,j;
//f=fopen("2.c","r");
//assert(f);
//scanf("%d%d",&map_w,&map_h);
map_w=map_h=6;
for (i=0;i
//fgets(&map[i][0],map_w+1,f);
//fclose(f);
start_x=-1,end_x=-1;
for (i=0;i
for (j=0;j/*designde by [email protected]*/
if (map[i][j]=='s') map[i][j]=' ',start_x=j,start_y=i;
if (map[i][j]=='e') map[i][j]=' ',end_x=j,end_y=i;
}
assert(start_x>=0 && end_x>=0);
//printf("%d,%d,%d,%d",start_x,start_y,end_x,end_y);
}
void showmap()
{
int i,j;
system("cls");
for (i=0;i
gotoxy(1,i+1);
for (j=0;j
if (map[i][j]!=' ') printf("x");
else printf(" ");
}
gotoxy(end_x+1,end_y+1);
printf("e");
gotoxy(start_x+1,start_y+1);
printf("s");
}
int main()
{
system("title 曉博 A*算法試驗程序");//設置cmd窗口標題
printf("…………歡迎使用曉博 A*算法試驗程序,Designed by [email protected] 河南財經政法大學…………");
int path[MAXINT];
readmap();
showmap();
Sleep(2000);
findpath(path);
printpath(path);
getchar();
system("pause");
return 0;
}
注意事項
為了使的程序可以慢慢的打印出來動態的運行效果 加了Sleep(2000);