图论——A*算法

距离估算方法
参考文章

A*算法使用在游戏中,自动寻路。
将游戏地图抽象成一个很大的矩阵,起始点和终止点的位置坐标已知,中间可能有很多的阻碍,求最短路径。
在这种场景下,如果将矩阵的每一块在抽象为图论中的每一个节点,这样子节点与节点之间的距离(也就是权重)就是1。

对于传统的最短路径算法,在这里,Dijkstra已经和广度优先搜索没什么区别了,因为距离为1,而Dijkstra是求有权重(权重不能为负,如果为负请用Bellman-Ford算法)。所以下面将Dijkstra使用广度优先搜索代替。

同时这和图论中的最短路径还有区别,图论中最短路径,没有办法计算目前点或者是起点与终点的估算距离。而在游戏中自动寻路的场景中,可以估算,原因在与这里的坐标已知。

寻路中使用广度优先搜索

如图:

广度优先搜索

图示偷别人,哈哈,蓝色区域是搜索过的区域,这里应该是一个类似圆形,而不是方形。

但是如果有障碍:

有障碍的广度优先搜索

左半边没有画出,也是一个完整的圆形。

在寻路中使用广度优先搜索,能够找到最短的举例,但是计算量大。

寻路中使用贪心算法

距离估算方法

因为目标点和终点已知,那么我们就可以估算当前点或是起点和终点的距离,然后在与当前点相接的点中,始终选取距离终点最短的点(不考虑回头走)。
如图:

贪心算法

这种方法使得计算量大大的减少了。
但是如果有障碍

有障碍的贪心算法

那么就被拒了,计算量仍然小,但是找到的路径不是最短的。

A*算法

如果结合广度优先算法和贪心算法的思想。使得计算量少于广度优先搜索,但是大于贪心算法。同时总能够保证找到一条最短路径。
例如:

拐个弯?

如果在这里能够拐个弯,就可以找到最短路径。
使得路径能够如下图所示

理想的路径,过于理想的搜索范围

A*算法的思想是这样的:

  1. 每个节点记录三个数据,G:离开点的步数、H:和终点的距离估算、F=G+H
    同时还应该有一个P,记录其父节点,一遍能够找到路径

  2. 两个集合,接壤集合,和已走结合
    同时还有一个集合是没有走的,但是这个集合并不会显式的写出来。

  3. 从起点开始计算与起点接壤的节点的GHF值,然后放入接壤集合
    这里计算完了,需要将起始节点放入已走节点集合,否则会重复计算两次。
    同时需要处理p的值

  4. 从接壤集合中取出F值最小的节点,计算与该节点接壤的节点GHF值,放入接壤结合,然后将该节点放入已走节点
    这里,如果一个节点已经计算过了,或者在已走节点中,那就不要算了。
    如果一个节点已经计算过,那么表明该节点以前的F值比再次计算出来的F值要小。

  5. 重复步骤4,直到到达终点

其实这里还是借鉴了Dijkstra的思想,每次处理距离最短的节点。

A*算法避免的广度优先搜索(或是Dijkstra)算法的大计算量,同时,避免了贪心算法走入死胡同。

如图:

A*

在上面的路径中,可能在到达B点的时候,计算完该店,然后接壤集合中F值最小的已经不是和B点接壤的点了。而是A点上面的点,然后从该点在慢慢到A点。
同时,在走到障碍外的某点时,可能障碍内的某个点是F最小的点,因此又会计算与该店接壤的点。

下面是伪代码,其中canMove容器以二叉堆的形式存放,加快寻找F值最小的节点。
就这样了。

//伪代码

#include <vector>
using namespace std;
struct  Node
{
    int x;  //该节点在nodeMap中的坐标
    int y;

    int px;
    int py;

    int g;  //从起点到该点的步数
    int h;  //从该点带终点的估算距离
    int f;  //g+h
};

class Axing
{
public:
    Axing();//用来初始化矩阵,障碍等。
    ~Axing();
private:
    vector<vector<int>> nodeMap;        //可以移动到的点为1,障碍点为0也就是不能移动上去的。
    int countH(int curx,int cury,int endx,int endy);    //该算法可能是直接勾股定理,或者是曼哈顿算法等
    Node getMinFNode(vector<Node> &);    //这个函数是从接壤容器中取出F最小的点。返回坐标
    void addNode(vector<Node> &);       //将Node添加到容器中
    //获取一个节点可以移动到的节点,并调用handleNode()处理
    void getCanMoveNode(vector<Node> &canMove,vector<vector<bool>> &memo,Node &cur);
    //该函数处理一个节点,计算该节点的接壤节点存入canMove。
    void handleNode(vector<Node> &canMove,int x,int y,Node &cur);  
    
    //为了少穿点参数。就加了两个变量
    //不知道编程中能不能这样做。
    int endx;
    int endy;
public:
    vector<vector<int>> findPath(vector<Node> &canMove,vector<vector<bool>> &memo,int startX,int startY,int endX,int endY);
};

void Axing::getCanMoveNode(vector<Node> &canMove,vector<vector<bool>> &memo,Node &cur,Node &end)
{
    //从二叉堆canMove中取
}
void Axing::addNode(vector<Node> &canMove)
{
    //canMove以二叉堆的方式存放,.f值为key
}



void Axing::handleNode(vector<Node> &canMove,int x,int y,Node &cur)
{
    int pg=cur.g+1;
    int h=countH(x,y,_endx,_endy);
    int f=h+pg;
    Node node(x,y,px,py,h,f);
    canMove.push(std::move(node));
}


vector<vector<int>> Axing::findPath(int startX,int startY,int endX,int endY)
{
    vector<Node> canMove;           //该容器存放接壤的点,表示可移动上去
    vector<Node> hasPass;           //存放已经走过的Node,以便重建路径
    vector<vector<bool>> memo;      //该容器表示某个点是否已经走过了。

    Node startNode(startX,startY,startX,startY,0,0,0);
    
    addNode(canMove,std::move(startNode));
    while(! canMove.empty())
    {
        Node curNode = getMinFNode(canMove);
        
        int x=cur.x;
        int y=cur.y;
        //依次处理四个节点
        if(memo[x-1][y] == false )
        {

            handleNode(canMove,x-1,y,cur);
            memo[x-1][y] =true;
            if(x-1 == endX && y == endY)
            {
                break;
            }
        }
        if(memo[x+1][y] == false)
        {
            handleNode(canMove,x+1,y,cur);
            memo[x+1][y] =true;
            if(x+1 == endX && y == endY)
            {
                break;
            }
        }
        if(memo[x][y-1] == false)
        {
            handleNode(canMove,x,y-1,cur);
            memo[x][y-1] =true;
            if(x == endX && y-1 == endY)
            {
                break;
            }
        }
        if(memo[x][y+1] == false)
        {
            memo[x][y+1] =true;
            handleNode(canMove,x,y+1,cur);
            if(x == endX && y+1 == endY)
            {
                break;
            }
        }

        hasPass.push_back(std::move(curNode));
    }
    //下面就从从最后一个节点,到开始节点重建该路径
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • 回想起曾学习A-star寻径算法时,难以透彻理解其原理和机制,但随着对图和搜索算法的理解愈发深入,近期重拾A-st...
    胡哈哈哈阅读 4,241评论 0 1
  • 写在前面的话 无意中在cocoaChina的首页看到了一篇介绍A*算法用swift实现的文章,对A*寻路算法产生了...
    好好学习天天引体向上阅读 15,109评论 2 57
  • 舜定五弦琴 文王增一弦 商纣何暴敛 武王欲伐战 伐之情壮烈 复添琴一弦 抹挑勾打托 上下复吟猱 抚琴指初起 听者情...
    桂之华阅读 377评论 6 7
  • Git原理及使用 二话不说,先来一张原理图 §Workspace:工作区 §Index / Stage:暂存区 §...
    暮京枫阅读 624评论 0 1
  • 听着花粥的奇妙民谣,轻快地歌声中和青春的文章,让我回忆起我的高中,我的凤姐夫。 不知道为何总是想要写一篇关于他的文...
    ___大鱼阅读 342评论 0 0