A star Algorithms
A* 算法的使用重点在于一个估值函数f的设计:
f = g + h
g = 从起点A,沿着产生的路径,移动到网格上指定方格的移动耗费。
h = 从网格上那个方格移动到终点B的预估移动耗费。
A*算法之所以称之为Heuristic Search,不同于DFS(Depth First Search),BSF(Breadth First Search),它使用了h这个启发式函数。h函数经常被称为启发式的,主要的原因是因为它只是个猜测。我们没办法事先知道路径的长度,因为路上可能存在各种障碍(墙,水,等等)。
一种具有f=g+h策略的启发式算法能成为A*算法的充分条件是:
1、搜索树上存在着从起始点到终了点的最优路径。
2、问题域是有限的。
3、所有结点的子结点的搜索代价值>0。
4、h=<h* (h*为实际问题的代价值)。
当此四个条件都满足时,一个具有f=g+h策略的启发式算法能成为A*算法,并一定能找到最优解。
对于一个搜索问题,显然,条件1,2,3都是很容易满足的,而条件4: h<=h*是需要精心设计的,由于h*显然是无法知道的,所以,一个满足条件4的启发策略h就来的难能可贵了。
且h距离h*的呈度不能过大,否则h就没有过强的区分能力,算法效率并不会很高。对一个好的h的评价是:h在h*的下界之下,并且尽量接近h*。 ---v_JULY_v
h距离一般可以使用三种方法:
1. Manhattan: h = x + y
2. Euclidean: h = hypot(x, y)
3. Chebyshev: h = max(x, y)
---(x,y)是当前点距离目标点之间的坐标距离差
也可以定义f = g + W * h;
---W权重越大heuristic function越重要
算法最为核心的过程,就在每次选择下一个当前搜索点时,是从所有已探知的但未搜索过点中可能是不同层,亦可不在同一条支路上),选取f值最小的结点进行展开。而所有“已探知的但未搜索过点”可以通过一个按f值升序的队列(即优先队列)进行排列。这样,在整体的搜索过程中,只要按照类似BFS的算法框架,从优先队列中弹出队首元素(f值),对其可能子结点计算g、h和f值,直到优先队列为空(无解)或找到终止点为止。 ---v_JULY_v
示例:
#include <iostream>
#include <queue>
#include <vector>
bool visited[19][38]; //定义是否被访问
short int map[19][38] = {
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1},
{0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,1,1,1,0,1,1,1,0,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,0,1,1,1,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,0,0,0,1,1,1,1,0,0,1,1,1,1},
{1,1,1,1,0,0,1,1,1,1,1,1,1,0,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1},
{1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0},
{1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0},
{1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,1,1},
{1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1},
{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
};
int g_startx = 0;
int g_starty = 0;
int g_endx = 15;
int g_endy = 36;
struct node
{
int f,g,h; int x,y; node *p; // p指向parent
Point node():x(0),y(0),f(0),g(0),h(0),p(NULL){} };
struct Node
{
//It is convenient to use priority queue
node *p_node;
bool operator<(const Node &f_n) const
{
//define method of priority queue
return p_node->f > f_n.p_node->f;
}
};
vector<node *> g_vector;
bool inmap(int x, int y) //坐标是否在有效范围内
{
if(x >= 0 && y >= 0 && x <= 18 && y <= 37)
return true;
return false;
}
int heuristic(int x, int y) //heuristic function
{
//The goal of * 10 is convenient to calculate
//return 0;
//Manhattan
return (int)10 * (fabs((double)(g_endx-x)) + fabs((double)(g_endy - y)));
//Chebyshev
//return (int)10 * max(fabs((double)(g_endx-x)),fabs((double)(g_endy - y)));
}
void retrace(node *n) //找寻回路
{
while(n)
{
map[n->x][n->y] = 3;
if(n->x == 0 && n->y == 0)
{
return;
}
n = n->p;
}
}
bool wall(int x, int y,int xx,int yy) //判断夹墙效果
{
//if child point is jacketed wall point return bool
if(map[x+dirs[0][0]][y+dirs[0][1]] == 0 && map[x+dirs[2][0]][y+dirs[2][1]] == 0 &&
xx == x+dirs[1][0] && yy == y+dirs[1][1] )
{
return true;
}
else if(map[x+dirs[4][0]][y+dirs[4][1]] == 0 && map[x+dirs[2][0]][y+dirs[2][1]] == 0 &&
xx == x+dirs[3][0] && yy == y+dirs[3][1] )
{
return true;
}
else if(map[x+dirs[4][0]][y+dirs[4][1]] == 0 && map[x+dirs[6][0]][y+dirs[6][1]] == 0&&
xx == x+dirs[5][0] && yy == y+dirs[5][1] )
{
return true;
}
else if(map[x+dirs[0][0]][y+dirs[0][1]] == 0 && map[x+dirs[6][0]][y+dirs[6][1]] == 0 &&
xx == x+dirs[7][0] && yy == y+dirs[7][1] )
{
return true;
}
else
return false;
}
void Astar() //采用优先级队列保存节点,以node.f 作为判断标准
{
priority_queue<Node > pq;
Node l_Node;
node *l_startnode = new node;
g_vector.push_back(l_startnode);
l_startnode->x = g_startx;
l_startnode->y = g_starty;
l_startnode->g = 0;
l_startnode->h = heuristic(l_startnode->x ,l_startnode->y);
l_startnode->f = l_startnode->g + l_startnode->h;
l_startnode->p = NULL;
l_Node.p_node = l_startnode;
pq.push(l_Node);
int px,py;
int cx,cy;
cx = cy = px = py = 0;
node *l_parent;
while(!pq.empty())
{
l_Node = pq.top();
pq.pop();
l_parent = l_Node.p_node;
px = l_parent->x;
py = l_parent->y;
if(px == g_endx && py == g_endy)
{
retrace(l_parent);
return;
}
else if(!visited[px][py])
{
visited[px][py] = true;
node *tmp;
for(int i = 0; i < 8; i++)
{
cx = px + dirs[i][0];
cy = py + dirs[i][1];
if(i % 2 == 1)
{
if(wall(px,py,cx,cy)) //夹墙点放弃
continue;
}
if(inmap(cx,cy) && map[cx][cy] && !visited[cx][cy]) // 保存未被访问过、非墙壁、在地图中的相邻节点
{
tmp = new node();
g_vector.push_back(tmp);
tmp->x = cx;
tmp->y = cy;
if(i%2 == 1) //if abs(dirs[i][0]) == 1 && abs(dirs[i][1] == 1) The distance of two point is 14
{
tmp->g = l_parent->g + 14;
}
else
{
tmp->g = l_parent->g + 10;
}
tmp->h = heuristic(tmp->x,tmp->y);
tmp->f = tmp->g + tmp->h;
tmp->p = l_parent;
l_Node.p_node = tmp;
pq.push(l_Node);
}
}
}
}
}
void deleteNode() //free node memory
{
vector<node *>::iterator start = g_vector.begin();
node *tmp;
while(start != g_vector.end())
{
tmp = *start;
start = g_vector.erase(start);
delete tmp;
}
}
int main()
{
Astar();
for(int x = 0; x < 19; x++)
{
for(int y = 0; y < 38; y++)
{
if(x == g_startx && y == g_starty)
{
cout<<" S ";
continue;
}
if(x == g_endx && y == g_endy)
{
cout<<" T ";
continue;
}
if(map[x][y] == 3 )
cout<<" /3 ";
/*else if(visited[x][y] )
cout<<" /2 ";*/
else if(map[x][y] == 0)
cout<<" x ";
else
cout<<" ";
}
cout<<endl;
}
deleteNode();
return 0;
}
参考:
A* Pathfinding for Beginners http://www.policyalmanac.org/games/aStarTutorial.htm
A formal basis for the heuristic determination of minimum cost paths in graphs(论文)
A*搜索算法 -- http://blog.csdn.net/v_JULY_v/archive/2010/12/23/6093380.aspx
Path Finding http://code.google.com/p/mycodeplayground/