A*算法笔记

16 篇文章 0 订阅

我们平常所见的深度优先和广度优先搜索算法都是直接搜索算法,这种是盲目式的搜索,直到搜索时遇到终点,或者搜索整个地图结束。而A*算法则是启发式搜索,这种搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标,它是通过启发函数引导算法的搜索方向。
A*算法路径代价值计算:F = G + H; (启发函数
F:从起点经过当前点到到终点的代价值;
G:从起点到当前点的实际代价;
H:从当前点到终点的估计代价;两种方法计算: 欧几里(两点间的直线距离)
                                                                             曼哈顿(x轴的距离 + y轴的距离);
在下面的例子中,为方便计算,横向和纵向的移动代价为 10 ,对角线的移动代价为 14 。
开启列表:存放待处理节点,
关闭列表:存放不需要再次检查的节点
(1)把起始点的格子位置加入到开启列表,然后开始循环从开启列表中每次找到F值最小的格子(第一次就是起始点),此时将格子从开启列表取出,放到关闭列表,代表此格子已经被访问。因为在本次循环中被取出,我们称它为当前格。
(2)然后我们遍历当前格的周围8个格子,如果不在开启列表,就添加到开启列表,并计算每一个格子的F,G,H的值,同时把当前格设置为它的父节点(即用来记录这个格子的前一个格子)。
(3)如果当前格的周围8个格子中,某些点已经在开启列表中,需要重新检查当前的路劲是否比原来计算的好,则重新计算G值:新G值为 = 当前点的G值 + 当前格到这个格的代价值(10或14)。如果新G值比原来的小,则把当前格设为这个格子的父节点。
(4)最后我们在开启列表中查找,如果终点已在当前开启列表中,说明找到,结束循环,如果开启列表为空了,还没找到终点
说明终点不可达。
伪代码流程分析:

例子:

步骤:(1)第一次将起点43添加到开启列表。
           (2)开始循环,从当前开启列表中找到F最小的点(这里只有起始点,当然是起始点),添加到关闭列表。
           (3)找43周围的8个点,因为都还不在开启列表,而且都可达,所以都添加到开启列表,计算每个点F,G,H的值,同时把43设置为它们的父节点。
           (4)此时检查终点是否在开启列表,不在,进行下一轮循环。
           (5)从开启列表中取出F值最小的点,这里是34。
           (6)找34周围的8个点,找到23,24还不在开启列表,而且都可达,所以添加到开启列表,计算他们的F,G,H的值,同时把34设置为它们的父节点。至于34其他周围的点,如果已经在开启列表了,那么重新计算一下G值,判断34到这个点这条路径的G值是否小于原来的G值,如果小于则更新父节点为34,并重新赋值F,G,H的值。
            (7)同理,每次循环检查终点是否在开启列表,若不在,继续进行下一轮循环,直到终点在开启列表或开启列表为空。

主要代码:

Point* Astar::findPath(Point &startPoint, Point &endPoint, bool isIgnoreCorner)
{
    //把起始点添加到开启列表
    openList.push_back(new Point(startPoint.x, startPoint.y));
    do
    {
        auto curPoint = getLeastFpoint();//找到F值最小的点
        openList.remove(curPoint); //从开启列表中删除
        closeList.push_back(curPoint);//放到关闭列表
        //找到当前周围八个格中可以通过的格子(1.可走且没有在关闭列表)
        auto surroundPoints = getSurroundPoints(curPoint, isIgnoreCorner);
        for (auto &target : surroundPoints)
        {
            //对某一个格子,如果它不在开启列表中,加入到开启列表,设置当前格为其父节点,计算F G H
            if (!isInList(openList, target))
            {
                target->parent = curPoint;
 
                target->G = calcG(curPoint, target);
                target->H = calcH(target, &endPoint);
                target->F = calcF(target);
 
                openList.push_back(target);
            }
            //对某一个格子,它在开启列表中,计算G值, 如果比原来的大, 就什么都不做,否则设置它的父节点为当前点,并更新G和F
            else
            {
                int tempG = calcG(curPoint, target);
                if (tempG < target->G)
                {
                    target->parent = curPoint;
 
                    target->G = tempG;
                    target->F = calcF(target);
                }
            }
            //查找结束点是否已经在开启列表中,如果在,这时候路径被找到。
            Point *resPoint = isInList(openList, &endPoint);
            //如果返回不为空,表示找到终点,则通过终点的parent一直反推得出路径
            if (resPoint)
                return resPoint;
        }
    }while (!openList.empty());//当开启列表为空时,跳出循环
 
    return NULL;
}

一些辅助函数:

int Astar::calcG(Point *temp_start, Point *point)
{
    //如果是斜边,extraG为14,否则为10
    int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2;
    //如果是初始节点,则其父节点是空
    int parentG = point->parent == NULL ? 0 : point->parent->G;
    //当前点的G值 = 父节点的G值 + 额外的G值
    return parentG + extraG;
}
 
int Astar::calcH(Point *point, Point *end)
{
    //欧几里距离:两点之间的直线距离
    //return sqrt((double)(end->x - point->x)*(double)(end->x - point->x) + (double)(end->y - point->y)*(double)(end->y - point->y))*kCost1;
    //曼哈顿距离:水平距离+垂直距离
    return (abs(end->x - point->x) + abs(end->y - point->y)) * kCost1;
}
 
int Astar::calcF(Point *point)
{
    //F = G + H
    return point->G + point->H;
}
 
Point* Astar::getLeastFpoint()
{
    //将第一个点作为F值最小的点,进行比较,最终拿到F值最小的点
    if (!openList.empty())
    {
        auto resPoint = openList.front();
        for (auto &point : openList)
        if (point->F<resPoint->F)
            resPoint = point;
        return resPoint;
    }
    return NULL;
}

总结:

A*算法
开启列表:存放待处理节点,
关闭列表:存放不需要再次检查的节点
F = G + H,选取F值最低的那个方格
F:每个可能试探点的估值
G:表示从起始搜索点到当前点的代价
H:它表示启发式搜索中最为重要的一部分,即当前结点到目标结点的估值。
h(n)的一般计算方法:假设当前节点(x1,y1),目标节点的位置是(x2,y2),则可以用两种方法计算H,欧几里得距离h(n) = sqrt((x2-x1) *(x2-x1) + (y2-y1) *(y2-y1)),曼哈顿距离h(n) = abs(x2-x1) + abs(y2-y1)。

A*算法的改进
第一次访问到该节点时,可能并不是最短路径,但在之前的做法中,已经将该节点放入closed list中,此后不在考虑。更好的做法是及时动态更新节点信息。也就是说,对closed list中的节点也考虑新的估值,若小与原估值,则放入open list中重新处理。
对open list,算法中需要经常访问,查找最小f值节点,而open list比较大,因此应该考虑效率问题。可以使用效率高的排序、查找方法,例如二叉堆,这里我们使用小根堆。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值