写在前面

哈哈哈,好久没更新啦,最近事情很多,所以这个A*算法的实现项目就拖了很久了。然后这两天我在停更的时候也想了很多关于之后博客的内容形式,我想以专栏的形式进行博客更新,每个项目都是一个独立的专栏,这样不但方便事后回溯,而且也方便总结。

关于专栏内容我也有了新的想法,因为我这两天也看了很多我之前写的代码,发现根本看不懂了,所以我就想着从现在开始给我的代码都写技术文档,然后附在相应算法介绍的后面,这样也方便二次开发。

言归正传,这个专栏呢主要介绍A*算法,本篇文章主要介绍算法的伪代码逻辑和程序执行结果。

程序结果

这里对于图形化界面进行一个简单的介绍:

绿色:待搜索节点;
浅灰色:搜索过程中的最小Fn数值点;
红色:起点;
绿色:终点;
黄色:最终的生成路径。

算法逻辑

A*算法是从BFS和Dijkstra算法演变过来的,可以认为是上述2个算法的改进,这里有一个网站写的特别好,推荐给大家:https://www.redblobgames.com/

A*算法可以看成局部最优化,随着从起点开始探索,在已探索的坐标点集合里面找到最小的代价坐标点做为下一个搜索点,循环往复直到终点,最终生成路径。这里面有很多可以探究的地方,比如最小代价如何确定呢?

那么A*采用的办法是:
F(n) = H(n) + G(n)
F就是我们需要找的最小代价,而H是待搜索坐标点到终点的预计距离,G是待搜索坐标点的已花费代价。

首先从H开始讲吧,既然是预计距离,计算方法就比较简单了,直接将待搜索坐标点与终点坐标做线性运算即可得到结果。

G的计算可以看成是待搜索坐标点的父节点的代价加上父节点到待搜索节点的花费即可得出待搜索节点的G值。

根据逻辑可以得到如下的伪代码:

ASTAR()
{
    INIT START
    INIT END
    INIT_VEC OPENSET
    INIT_VEC FATHERSET
    INIT_VEC OBSTACLE

    OPENSET.ADD(START)
    ADD_OPENSET(START)

    WHILE(!IS_OPENSET_EMPTY()){
        MIN_FN_POINT = FIND_MIN_FN(OPENSET)
        IF(MIN_FN_POINT == END){BREAK}
        SEARCH_ENV_BLOCK(MIN_FN_POINT)
        OPENSET.DELETE(MIN_FN_POINT)
        FATHERSET.ADD(MIN_FN_POINT)
    }

    GET_TRAJECTORY()
}

SEARCH_ENV_BLOCK(MIN_FN_POINT)
{
    FOR(AUTO_P : MIN_FN_POINT){
        COST = MIN_FN_POINT.G + CALCU_G(MIN_FN_POINT, AUTO_P)
        IF((AUTO_P IN FATHERSET) || (AUTO_P IN OBSTACLE)){CONTINUE}
        IF(AUTO_P NOT IN OPENSET){
            OPENSET.ADD(AUTO)
        }ELSE IF(AUTO_P.G > COST){
            UPDATE AUTO_P
        }
    }
}

算法在更新探索节点的时候会有2种情况,那就是该点已经探索过了,在这种情况下,如果待探索节点的G大于新的G时需要更新节点的G值。如果该点没有探索过,就将其加入待搜索节点以用于之后的F最小值坐标点的探索。

理论上来说算法只需要一个向量(vector)就可以完成任务,但是为什么我们需要父向量(FATHERSET)和已搜索节点(OPENSET)呢?

这个跟算法的实现有关,思考一下,当我们从已搜索节点里面找到F的最小值得坐标点之后,为了能够搜索下一个点,算法需要将其从已搜索节点里面去除,但是之后为了在搜索的时候不再重复搜索这个点,我们就需要再另外建立一个向量,也就是父向量,而且在生成最终路径的时候也需要通过父节点来进行回溯得到路径。