Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Astar Algorithm

Astar Algorithm

作者头像
西红柿炒鸡蛋
发布于 2020-09-10 02:18:25
发布于 2020-09-10 02:18:25
84600
代码可运行
举报
文章被收录于专栏:自学笔记自学笔记
运行总次数:0
代码可运行

参考文献:https://www.gamedev.net/reference/articles/article2003.asp 这篇东西写的贼好。

介绍

A*算法主要用于寻路,比如游戏中的自动寻路系统。比其他算法优越的地方就在于他缩小了搜索空间。像迪杰斯特拉弗洛伊德算法这些都只是考虑了到原点的距离,他没有考虑到终点的代价,而且求出了很多不太必要的信息。如果我们仅仅只是求两点的距离,就没有必要用迪杰斯特拉和弗洛伊德了,因为他们把原点到所有点的距离都求了出来。 还有一些其他的算法比如广度和深度搜索,这些算法的都是遍历可能的空间,而深度优先不适用于最短路径的搜索,因为深度优先求出来的路径和代码编写的搜索方向有关系,也就是说深度优先搜索的路径有随机性。所以深度优先如果要做最短路径就只能遍历所有的路径了。广度优先搜索适用于最短路径,但是搜索空间还是很大的,比如如下的场景:

@是起点和终点,#是墙,如果是广度优先,需要把以起点到终点为半径的圆都搜索一遍。

这样的复杂度还是挺大的。为了能够缩小搜索区域,我们可以考虑把对终点的代价也计算在内。

算法细节

和迪杰斯特拉类似,迪杰斯特拉是寻找离原点最近的点,那么我们只需要加上终点代价即可,于是我们定义代价函数F = G + H,G是原点的距离,用勾股定理可以知道斜方向的距离是14,正方向的距离是10,乘10方便计算。H是到达原点的距离,用哈密顿距离即可。 事实上这样的估计只是一种粗略估计,求出来的就算不是最短路径那也和最短路径差不多了。如果存在场景使得哈密顿距离误差很大的话那么求出来的就未必是最短路径了。 需要注意的是,对于终点的计算,方块都是固定的,但是对于G的计算有些特别,讲道理每一方块到原点距离都是一样的,但是如果一直用这样用勾股定理计算,麻烦不说,并且在搜索过程中就没办法调整路径了。 比如,我们要计算路径,自然要保存父节点

如图,当前我们在绿色节点右下角的蓝色节点处,但是问题来了,蓝色节点左下角,也就是方块F值是88的节点如果按照勾股定理计算G就是20,但是目前由于这个节点是当前蓝色节点发现的,所以他的父节点是当前蓝色节点,但是事实上这并不是离原点最近的指向,他应该指向上才是最近的。我们就没办法更新这些父子关系了。

于是我们规定,G节点不再直接计算,方块上下左右移动增加10,斜方向移动增加14即可,这样就可以保留从父节点再到原点的G值了,在搜索过程中就可以对比G值更新父节点。

如图:

当前在节点2,我们通过节点2搜索得到的F=88的节点,此节点G为28(左上角是F值,左下角是G值,右下角是H值)。但是当我们搜索到节点3的时候,3也发现了F=88的节点,这个时候发现从3号更短,与是把F=88的节点父亲设置为3号。这样就达到了更新的目的。如果没有搜索到3号,那么说明这些节点太远了,没有用。

定义了代价函数后就是如何搜索了。

首先规定搜索规则,上下左右可以搜索,斜方向也可以搜索,但是斜方向的搜索不能被正向的挡住。比如往右上角移动,那么右方向和上方向就不可以有阻碍。

以#为墙为例子:

。# 。 | 。# 。 | 。 。 。

。起 # |。起 。 | 。起 #

上面的情况都不可以进行右上角的移动,以此类推即可。

准备openList和endList,openList用来存放候选节点,也就是最短路径的候选值,endList用来存放已经搜索完成的节点。

初始情况,我们把起点丢进openList里面,每一次从openList里面取出F值最小的节点,然后把这个节点上下左右斜方向都进行扩展,把扩展出来的候选值计算好F放进openList里面,这个时候从openList拿出的节点已经完成搜索,我们只需要加入endList即可。不断重复这个循环,从openList中取值。

在从openList取出值并扩展的获得候选点的时候有3个情况。如果扩展的候选点在endList里面了,那就不用管了,如果不在endList里面在openList里面,说明他已经有一个父亲和一个从父亲计算来的G了,这个时候你就要判断从当前节点到这个候选点是不是更近,也就是G是不是更小的,如果是更小的就替换父亲,替换G。如果不在openList也不在endList,那就计算FGH添加进openList即可。

流程非常简单。

接下来就是实现代码。

代码实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class node {
private:
    int x, y;
    node *parent;
    double G;
    double H;
    double F;

节点node信息,getset方法不贴了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    int nRow;
    int nColumn;
    pair<int, int> beginning;
    pair<int, int> ending;
    vector<pair<int, int>> walls;
    vector<string> maps;
    vector<bool> visited_open;
    vector<bool> visited_end;
    //F G node
    vector<node *> openList;
    vector<node *> endList;

map中存储数据结构。visited_open和visited_end是为了防止搜索重复遍历open和end列表。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    void generateMap() {
        vector<string> results;
        results.resize(nRow);
        visited_open.resize(nRow * nColumn);
        visited_end.resize(nRow * nColumn);
        for (int i = 0; i < nRow; i++) {
            for (int j = 0; j < nColumn; j++) {
                results[i][j] = '.';
            }
        }
        for (int i = 0; i < walls.size(); i++) {
            results[walls[i].first][walls[i].second] = '#';
        }
        results[beginning.first][beginning.second] = '@';
        results[ending.first][ending.second] = '@';
        maps = results;
    }

生成地图。 主算法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        node *begin = new node(beginning.first, beginning.second, nullptr);
        begin->setF(0);
        begin->setG(0);
        begin->setH(0);
        //起始点丢进openlist里面,更新最小点
        openList.push_back(begin);
        visited_open[begin->getX() * nColumn + begin->getY()] = true;

可以把起点丢进open列表,begin->getX() * nColumn + begin->getY()是这个点的编号,把矩阵拉长变成一维编号留下的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        while (!openList.empty()) {
            int index = getLowestF();
            node *current = openList[index];

            //cout << current->getX() << " " << current->getY() << endl;

            openList.erase(openList.begin() + index);

            endList.push_back(current);
            visited_end[current->getX() * nColumn + current->getY()] = true;
            if (current->getX() == ending.first && current->getY() == ending.second) {
                return;
            }

            addPoint(current);
        }
    }

首先遍历获取最小的F值,从open取出并加入end中,别忘了维护visited,其次把扩展的候选点加入open。

代码效果

初始地图。

箭头即是最短路径。 我们来看看搜索空间:

*号是加入open的节点,也就是参与搜索的节点,可以看到搜索空间比广度优先几乎少了一半。

算法优化

很明显,这个算法优化的空间还是比较大的,之前在openList就考虑过能不能用优先队列,因为在迪杰斯特拉算法中有优先队列是有很大的效率提升,但是问题就在于,在候选点存在openList列表的时候,我们需要进行更新父节点,这个时候要搜索openList了,C++的优先队列没有提供遍历功能,总不能全部倒出来再塞回去吧。所以如果使用优先队列,就需要考虑如何在优先队列中获取特点元素并完成动态更新的过程,因为你找到这个节点如果更新了之后,优先队列需要再动态的排序一次的。而具有让priorityqueue能动态排序的我目前就知道pair。不知道其他的语言优先队列能不能遍历,C++可以自己实现一个二叉堆即可。 其次,代价还是对于算法影响是非常大的,当H是0的时候,就完全退化成了迪杰斯特拉算法,因为他之和原点距离有关了。当H比实际代价要小上许多时,我们的搜索复杂度就会增加,因为这个时候主要取决于G,于是就会在原点周围不断扩展,搜索的点会非常多。 我们上面的例子中代价都是乘10,H的代价也是乘10的,一个格子就是10,我们现在缩减十倍,对于G来说,上下左右+10,斜方向加14,而对于H来说一格只加1,我们看看搜索空间的变化。

可以看到基本就退化成了广度优先。 当H原大于实际代价的时候,这就有一点随机性了,得看你障碍物的位置和你代码编写遍历方向的顺序有关系了。因为这个时候主要取决于H的变化。这就有点像BFS了。 所以按照这个方向优化,我们可以考虑让H代价更准确一点,修改代价函数F = G + W * H。W可以影响H的权重,这里的W可能要根据不同场景进行设置,可以设置所有节点都一样,也可以设置没个节点都不同。或许可以随着搜索的增加不断的增大H,使得搜索空间成一个哑铃的形状。当然这只是个想法,是不是还得实验一下。

代码:https://github.com/GreenArrow2017/PracticeAlgorithm/tree/master/Astar

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
人工智能基础-路径规划
在查找路径时,BFS能够快速找到最短路径,但是它的空间复杂度更高,而DFS也可以找到一条路径,但是不保证它就是最短路径。如果一定要查找最短路径,那么它就需要遍历所有节点。
DearXuan
2022/01/19
6660
人工智能基础-路径规划
搜索(8)
A*算法  很多人应该都玩过类似下面这样的拼图游戏(华容道):  为了简化题目,我们只使用3x3的规模,并且按照从上到下,从左至右,将每块拼图标记为正确位置的编号  上图中左边是初
mathor
2018/07/05
7140
【图论】迪杰特斯拉算法
迪杰特斯拉算法是由荷兰计算机科学家艾兹赫尔·迪杰特斯拉(Edsger W. Dijkstra)在1956年提出的,用于解决单源最短路径问题的经典算法。该算法的目标是从一个起始顶点找到到图中其他顶点的最短路径。
用户11305458
2024/10/09
3980
【图论】迪杰特斯拉算法
A* JPS寻路算法的探讨
A*(A-star)寻路算法是一种基于启发式搜索的路径规划算法,常用于游戏开发和人工智能领域,JPS是A*算法的一个优化算法,咱们就先做一段简单的A*算法介绍,后续再进行JPS算法的进一步探讨。
keyle
2024/11/01
2850
A* JPS寻路算法的探讨
C++启发式搜索算法(A*),给你一点阳光,你一定要灿烂哟!
给小孩子出一道数学题,在他不知所措,没有头绪时,你给他点提示。也许这点提示可以让他灵光一现,找到一点光亮,少一些脑回路,快速找到答案。这便是启发的作用。
一枚大果壳
2024/02/22
4480
C++启发式搜索算法(A*),给你一点阳光,你一定要灿烂哟!
写个A星寻路算法,主程也不一定能写出来!!!
如果一个游戏开发人员不知道A * 寻路算法的话有点说不过去,除非你是棋牌游戏的开发人员。虽然大部分的游戏开发都知道A星,但是能写出来,能理解清楚的也少之又少,今天就来一起学习下实现一下。
香菜聊游戏
2021/09/08
1.5K0
写个A星寻路算法,主程也不一定能写出来!!!
力扣1514——概率最大的路径
本题主要和图的遍历求解最短路径相关,可以用 Dijkstra 或者 Bellman-Ford 算法进行解决。
健程之道
2020/08/28
5540
力扣1514——概率最大的路径
A星寻路算法详解
A星寻路算法是静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法,它可以应对包括复杂地形,各种尺度的障碍物以及不同地形的路径规划问题。掌握A星寻路算法能够提高路径规划效率,应对各种复杂情况,并在实际应用中发挥重要作用。
astonishqft
2024/02/27
2K0
A星寻路算法详解
Python 图_系列之纵横对比 Bellman-Ford 和 Dijkstra 最短路径算法
因无向、无加权图的任意顶点之间的最短路径由顶点之间的边数决定,可以直接使用原始定义的广度优先搜索算法查找。
一枚大果壳
2022/08/23
4590
Python 图_系列之纵横对比  Bellman-Ford 和  Dijkstra 最短路径算法
【算法学习】分枝限界法
关注那些不断已被他人成功应用的新思路。你的原创思想只应该应用在那些你正在研究的问题上。
短短的路走走停停
2019/11/19
1.3K0
【算法学习】分枝限界法
机器学习之A*算法
A*算法是一种能快速找出两点之间较短路线的算法。和Dijkstra算法相比,虽然它找到的路径可能不是最短的,但是它要高效很多,使得它的应用十分广泛(因为大多数时候我们对路径是否最短的要求不是很严格,只要较短就行)。
用户9327464
2025/03/20
950
路径规划算法之A*算法
路径规划是非常常见的一类问题,例如移动机器人从A点移动到B点,游戏中的人物从A点移动到B点,以及自动驾驶中,汽车从A点到B点。这类问题中,都有两个关键问题需要解决:
用户10646968
2023/07/07
6480
路径规划算法之A*算法
一学就会:A*算法详细介绍(Python)
A*算法是一种高效的路径搜索算法,广泛应用于人工智能、机器人技术、游戏开发等领域。它由Peter Hart、Nils Nilsson和Bertram Raphael于1968年首次提出。A算法结合了Dijkstra算法的系统性搜索和启发式搜索的优点,通过使用启发式函数来减少搜索空间,同时保证找到最短路径。
不去幼儿园
2025/03/01
3640
一学就会:A*算法详细介绍(Python)
deepseek VS chatgpt (402)-- 算法导论25.3 2题
二、在 Johnson 算法里,在集合 V 中加入新结点 s 产生 V' 的目的是什么?如果要写代码,请用go语言。
福大大架构师每日一题
2025/02/19
530
deepseek VS chatgpt (402)-- 算法导论25.3 2题
A星寻路算法(A* Search Algorithm)
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢?
racaljk
2018/08/31
2.7K0
文心一言 VS 讯飞星火 VS chatgpt (381)-- 算法导论24.5 1题
一、给出图24-2的与图中两棵最短路径树不同的另外两棵最短路径树。如果要写代码,请用go语言。
福大大架构师每日一题
2024/11/01
700
文心一言 VS 讯飞星火 VS chatgpt (381)-- 算法导论24.5 1题
【愚公系列】2023年12月 五大常用算法(五)-分支限界算法
分支限界算法是一种解决最优化问题的常用算法,其基本思想是将问题的解空间划分为一棵树,每个节点代表一个可能的解,从根节点开始搜索,搜索过程中根据约束条件和限界条件,逐步减小搜索空间,只保留可能成为最优解的子树。具体来说,分支限界算法有以下几个基本步骤:
愚公搬代码
2023/12/16
3080
【小白学游戏常用算法】二、A*启发式搜索算法
  在上一篇博客中,我们一起学习了随机迷宫算法,在本篇博客中,我们将一起了解一下寻路算法中常用的A*算法。
马三小伙儿
2018/09/12
1.2K0
【小白学游戏常用算法】二、A*启发式搜索算法
[算法]-最短路径算法总结「建议收藏」
基本思想:首先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从顶点v 到其它各顶点的最短路径全部求出为止。
全栈程序员站长
2022/09/01
5930
文心一言 VS 讯飞星火 VS chatgpt (308)-- 算法导论22.2 6题
六、举出一个有向图$G=(V,E)$的例子,对于源结点$s∈V$和一组树边$E_π∈E$,使得对于每个结点 $v∈V$,图$(V,E_π)$中从源结点$s$到结点$v$的唯一简单路径也是图$G$中的一条最短路径,但是,不管邻接链表里结点之间的次序如何,边集$E_π$都不能通过在图$G$上运行 BFS 来获得。如果要写代码,请用go语言。
福大大架构师每日一题
2024/08/16
840
文心一言 VS 讯飞星火 VS chatgpt (308)-- 算法导论22.2 6题
相关推荐
人工智能基础-路径规划
更多 >
加入讨论
的问答专区 >
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
    本文部分代码块支持一键运行,欢迎体验
    本文部分代码块支持一键运行,欢迎体验