AStar算法总结与实现(附Demo)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

AStar算法总结与实现(附Demo)
关于A Star Algorithm
A star算法最早可追溯到1968年,在,是把启发式⽅法(heuristic approaches)如BFS,和常规⽅法如Dijsktra算法结合在⼀起的算法。

有点不同的是,类似BFS的启发式⽅法经常给出⼀个近似解⽽不是保证最佳解。

然⽽,尽管A star基于⽆法保证最佳解的启发式⽅法,A star却能保证找到⼀条最短路径。

公式表⽰为:f(n)=g(n)+h(n)
f(n)是节点n从初始点到⽬标点的估价函数
g(n)是在状态空间中从初始节点到n节点的实际代价
h(n)是从n到⽬标节点最佳路径的估计代价
观察A*寻路算法的运⾏轨迹
假设起点为A(浅蓝⾊的个字)终点为B(深蓝⾊的格⼦)
红⾊代表该格⼦为障碍物
地图为20x20的格⼦
显⽰FGH值的格⼦代表经过A*算法搜索并⽣成路径的格⼦
有透明度变化的格⼦代表该格⼦有被搜索。

绿⾊格⼦代表的是搜索完成后A*得到的最优的路径
A直接抵达B的情况下
A越过直线障碍到达B
A越过U型障碍到达B
B为障碍物所包围着,A到达不了B的情况下
总结与思考
由4组图可以得到
1.A*的消耗是⼀个及其不稳定的过程,消耗的最⼩值不低于直线路径上的消耗,消耗的最⼤值不⾼于遍历整张地图的消耗。

2.A*的消耗主要在搜索的搜索格⼦,以及对其FGH的操作上。

3.由1,2可以得出,在对运⾏速率和效率有要求的场景下,A*可能不是⼀个⽐较好选择。

算法步骤
横向纵向的格⼦的单位消耗为10,对⾓单位消耗为14。

定义⼀个OpenList,⽤于存储和搜索当前最⼩值的格⼦。

定义⼀个CloseList,⽤于标记已经处理过的格⼦,以防⽌重复搜索。

开始搜索寻路
1.将起点加⼊OpenList
2.从OpenList中弹出F值最⼩的点作为当前点
3.获取当前点九空格(除去⾃⼰)内所有的⾮障碍且不在CloseList内的邻居点
4.遍历上⼀步骤得到的邻居点的集合,每个邻居点执⾏以下逻辑
如果邻居点在OpenList中
计算当前值的G与该邻居点的G值
如果G值⽐该邻居点的G值⼩
将当前点设置为该邻居点的⽗节点
更新该邻居点的GF值
若不在
计算并设置当前点与该邻居点的G值
计算并设置当前点与该邻居点的H值
计算并设置该邻居点的F值
将当前点设置为该邻居点的⽗节点
5.判断终点是否在OpenList中,如果已在OpenList中,则返回该点,其⽗节点连起来的路径就是A*搜索的路径。

如果不在,则重复执⾏2,3,4,5。

直到找到终点,或者OpenList中节点数量为0。

Tip:判定结束的有两种
第⼀种是以OpenList中有终点节点或者OpenList中没有节点
第⼆种是CLoseList中有终点节点或者......
第⼀种要⽐第⼆种运算次数要少许多,但在最短路径的的处理上,第⼆种要⽐第⼀种要精准,是相对精准。

图解算法
(7,10)为起点,(11,10)为终点,(9,11)(9,10)(9,9)为障碍点。

1.当前点为(7,10)
2.当前点为(8,9)
2.当前点为(8,11)
当前点为(6,10)
这⾥是最容易忽视的地⽅,因为A*的启发搜索的实现就是靠搜索F值最⼩的节点来实现,所以是会出现这种背离⽬标的搜索。

当前点为(7,9)
当前点为(7,11)
当前点为(9,8)
当前点为(10,9)
当OpenList中出现终点节点时,则结束此次搜索
如果有想看更复杂的条件下的搜索轨迹,或者
总结与思考
A的消耗有很⼤的不确定性。

消耗跟地图的复杂程度成正⽐,跟相对距离的长短成正⽐。

有⼀个极端的情况,当终点位置为障碍点包围时,即A Star找不到终点坐标,A会遍历该地图此障碍区以外的所有区域。

关键逻辑的代码实现
1.A*寻路算法的主逻辑
Point start = ...;
Point end = ...;
bool isIgnoreCorner = ...;
OpenList.Add(start);
while (OpenList.Count != 0)
{
stepSearch(start, end, isIgnoreCorner);
if (OpenList.Get(end) != null)
return OpenList.Get(end);
}
return OpenList.Get(end);
2.单次搜索所执⾏的逻辑
//找出F值最⼩的点
var tempPoint = OpenList.PopMinPoint();
//OpenList.RemoveAt(0);
CloseList.Add(tempPoint);
var alivePoints = GetGridAlivePoint(tempPoint, isIgnoreCorner);
for (int i = 0; i < alivePoints.Count; i++)
{
Point p = alivePoints[i];
if (OpenList.Exists(p))
{
//计算G值, 如果⽐原来的⼤, 就什么都不做, 否则设置它的⽗节点为当前点,并更新G和F
FoundPoint(tempPoint, p);
}
else
{
//如果它们不在开始列表⾥, 就加⼊, 并设置⽗节点,并计算GHF
NotFoundPoint(tempPoint, end, p);
}
}
3.当邻居点在OpenList点中时的处理逻辑
var G = CalcG(tempStart, point);
if (G < point.G)
{
point.ParentPoint = tempStart;
//因为每次取值,都是使⽤F值,所以我觉的可以不更新G值
//point.G = G;
point.F = point.H + G;
}
4.当邻居点不在OpenList点中时的处理逻辑
point.ParentPoint = tempStart;
point.G = CalcG(tempStart, point);
point.H = CalcH(end, point);
point.CalcF();
OpenList.Add(point);
5.最基础的逻辑也是最重要的逻辑之⼀,计算G值
计算G值只适⽤于相邻的两个点
int G = (Math.Abs(point.X - start.X) + Math.Abs(point.Y - start.Y)) == 2 ? 14:10;
int parentG = point.ParentPoint != null ? point.ParentPoint.G : 0;
return G + parentG;
5.最基础的逻辑也是最重要的逻辑之⼀,计算H值
同G值,这⾥只计算直线上的消耗,不处理对⾓。

int step = Math.Abs(point.X - end.X) + Math.Abs(point.Y - end.Y);
return step * 10;
应⽤与思考
1.A* 在游戏中多有应⽤,怪物AI,计算玩家⾏⾛的路径,⼀些辅助⼯具⽐如游戏机器⼈玩家的策略⽅案等应⽤。

但因为其消耗的极其不稳定,所以不会作为⾸选,在游戏中如果⼤量的应⽤这种逻辑,JPS(Jump Search Point),或者JPS+(JPS的优化版本)
2.A*在AR和⾃动驾驶领域也有应⽤。

⽐如有些AR的应⽤是基于SLAM算法进⾏场景实时建模,然后在⽣成的模型当中,搜索⼀条有效的路径。

A Star在这种场景中有很强的应⽤空间。

3.A Star的消耗主要是不断的搜索⽣成新的节点,不断的遍历计算。

其优化思路⼀般也是围绕这两个点,减少搜索次数,优化遍历⽅案。

我个⼈觉得JPS(Jump Point Search )就是把A Star优化做到⼀定程度的结果。

4.第⼀篇关于A Star⽂章是在1968年,第⼀篇关于JPS的⽂章是在2011年。

在这段时间A Star处于什么样的⼀个地位,在这期间A Star⼜经历了什么样的演变,⼜演变出多少种在其基础之上优化的算法。

在我看来刚出世时的A Star是⼀种算法,⼀种⼯具,在经历种种反复的推敲之后,俨然成为了⼀种思想,⼀种在未知领域寻找最优解的思想。

传送门
参考与引⽤。

相关文档
最新文档