本文提供了A-star 算法的伪代码,可以对正在编码实现A-star的读者提供帮助。参照本文提供的伪代码可以快速编程实现A-star。
1、伪代码中的符号说明:
current节点:每次迭代时,Open_list中F值最小的那个节点
G:G值,从“当前节点” current 移动到起点start的代价
H:H值,预计从“当前节点”current移动到终点end的预估代价(估算成本)
F:F值,从起点start到终点end路过current节点的路径的估算总代价
F = G + H
neighbor节点:current节点的所有合法的neighbor节点(即:忽略掉那些不可到达的节点、已被放入Close_list中的节点,以及其它非法情形下的节点)
G值用于标识“与’当前节点’(current)相邻的”那些节点(neighbor)中,哪一个节点到起点start的代价最小。G值最小的那个neighbor即为到起点start的代价最小的那个节点。
F值用于确定下一个将要处理的“当前节点”current,F值最小的节点即为下一个将要处理的“当前节点”
2、算法基本过程
(1)将Open_list中F值最小的“当前节点”current自身移入Close_list, 将“与current节点相邻的所有合法节点neighbor”移入Open_list.即:
Open_list.erase(current)
Close_list.insert(current)
(2)Open_list中的所有节点将被遍历,从中找出F值最小的那个节点,作为下一次迭代时的current节点。因此,上一步中将”current自身移入Close_list”是为了避免其反复被迭代而无法迭代到其它的节点。
*CASE 1: 在每次的迭代中,如果某个(某些)neighbor是新加入Open_list的,则该neighbor的父节点就是current节点,该neighbor的G值等于其父节点(即current节点)的G值加上该neighbor到其父节点的代价P。公式如下:
neighbor.Father = current
neighbor.G = G(current) + P(current_to_neighbor)
neighbor.H = neighbor到终点end的预估代价
neighbor.F = neighbor.G + neighbor.H
Open_list.insert(neighbor)
*CASE 2: 如果某个(某些)neighbor早已存在于open表,则G(neighbor)必定已存在,它衡量了从该neighbor到起点start的代价。那么,到底是该代价G(neighbor)更小?还是neighbor经过current节点到达起点start的代价G’(neighbor)更小?此时需要先计算G’(neighbor)。计算的方法与上面计算G值的方法相同,即:
G’(neighbor) = current.G + P(current_to_neighbor)
若G(neighbor)小于G’(neighbor),说明该neighbor无需经过当前current节点即可以更小的代价到达起点start,此时什么都不需要做。若G(neighbor)大于G’(neighbor),说明要想从该neighbor节点以更小的代价到达起点start,则必须经过当前节点current,此时则需要将该neighbor的父节点改成current节点,其G值也应该改成G’(neighbor)的值。
3、A-star 伪代码:
start.Father = null
start.G = 0
start.H = start 到end的预估代价
start.F = start.G + start.H
Open_list.insert( start )
While( !Open_list.empty() || Open_list.find(end) == Open_list.end()) //迭代访问Open_list中的所有元素,直到Open_list为空,或者end节点存在于Open_list中时,才停止迭代
{
auto current = Open_list.get_Element_With_Minimum_F_Value() //每次迭代开始时都选择F值最小的那个节点作为current节点
Auto allNeighbors[ ] = getAllNeighborNodes( current ) //获取current节点的所有合法的neighbor节点(即忽略掉那些不可到达的节点、已被放入Close_list中的节点,以及其它非法情形下的节点)
Open_list.erase(current) //将current节点从Open_list移到Close_list,避免while循环变成死循环
Close_list.insert(current)
For( auto neighbor in allNeighbors[ ] ) //遍历current节点的所有neighbor节点,确保neighbor节点的G值尽可能小(即neighbor节点到start节点的代价尽可能小)
{
auto neighbor_G_whith_current = current.G + P(current_to_neighbor)
If( open_list.find(neighbor) == open_list.end() ) //如果当前neighbor节点不在open_list则将其放入open_list
{
neighbor.Father = current
neighbor.G = neighbor_G_whith_current
neighbor.H = neighbor到终点end的预估代价
neighbor.F = neighbor.G + neighbor.H
Open_list.insert(neighbor)
}
Else //如果当前neighbor节点已存在于open_list,并且它通过current节点到达start节点的G值小于其本身原有的G值,则将这个neighbor节点的Father节点改成current节点,并刷新G值、H值、F值。否则什么都不做。
{
If( neighbor_G_whith_current < neighbor.G)
{
neighbor.Father = current
neighbor.G = neighbor_G_whith_current
neighbor.H = neighbor到终点end的预估代价
neighbor.F = neighbor.G + neighbor.H
}
}
}
}
Auto it = Open_list.find(end)
If( it != Open_list.end())
{
//求路成功,输出结果
Cout << “Route from end to start:” << endl
For(Auto pNodePointer = &(*it); null != pNodePointer->parent; pNodePointer = pNodePointer->parent)
{
Cout << pNodePointer->parent->nodeID << endl
}
}
Else
{
//求路失败,从start节点到end节点不存在路径
Cout << “No route from start to end!!!” << endl;
Return false;
}
Return true