AStar 启发函数设计(老物)

本文深入探讨了A*算法的基础及其在游戏寻路中的应用,并通过调整启发式函数进行了两次优化,旨在寻找最短路径的同时提高搜索效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作为我出山的第一篇日志,怎么也得写篇对得起我身份和地位的文章吧?

先容我吐槽一下不小心发的贴图,那个只是我不小心收藏了隔壁兄弟班的课表就别大家这么热情的 BB 我感到很有压力,额,废话不多说,立刻进入正题吧。

简单说一下 AStar (A*)算法,这是一种根据启发函数图遍历算法雏形。

举个栗子,如果你身处迷宫,但你知道出口的方向,那么你应该会尝试往出口方向前进,那么,第一种启发函数就被确定了,取出周围的点,根据一个欧拉距离运算来确定各个点距离出口还有多远,从而选择距离较近的点进行移动,这就是一种由启发函数进行导向的算法模型,假若还不了解什么是启发式搜索请自行移步到百度百科。

好滴,其实上面只是作为自己的一种理解让大家好明白我接下来要说明的内容是关于哪些方面的,如果不是计算机专业的同学看到这里,请自觉跳到最底通过看图例来感受一下启发式函数搜索的魅力吧(笑)。

通常的 A* 算法

1641852-20190902210452098-1227842437.png1641852-20190902210457528-1599881367.png

通常的A*算法主要有两种估值函数,曼哈顿距离以及距离消耗步数进行结合,最终得到权值 F 可以做为下一步将要移动的位置,举个图例就是:

注:右图左上角为图形边界 30*30 ,右下角为搜索次数,起始点为(12,5),终止点为(12,24)。最终搜索到的点为 423*4 (最大值)。
根据上图我们可见,通常 A* 寻路可以获得最短路径,但我这里的想法是应用在游戏寻路中,为了达到这个目的我不得不做出一些牺牲。
在做这个的优化的时候先在原有的基础上进行调整吧!

基础 A* 所用启发式函数为:

F = G + H
G = 移到到该处所需要走的步数
H = abs(this->x - Goal.x) + abs(this->y - Goal.y);
this 为当前位置的一个点,其中含有X,Y坐标信息,Goal为其所指向的目标点。

所以根据这个 F(x, y)我们可以得到上图的搜索情况,接着进行第一次简单优化。

首先我们构造的一个优先队列将所有搜索到的点入队依次按照 F 值排列,则这里可以提高 H 值的比重,让搜索向着目标方向进行,以减少点的进队,须知道一点就是每当搜索到一个点的时候就意味着有可能要对该点进行不大于 4 (基于 4 方向移动)的点展开搜索,这就是优化的方向,减少点进入优先队列,从而提高搜索效率。那么我现在做第一次优化。

F = sideMAX * H + G;//sideMAX为两边界的较大值

那么这时候的结果如下:
再来个图栗子吧~

1641852-20190902210508204-1507041758.png1641852-20190902210513873-1792697589.png

此时,搜索会更加倾向于目标点从而一定程度上忽略了 G 值,这样搜索就会强调在大体方向,这样就完成了第一次优化,可喜可贺。
但是,我不认为这样就能够让大家满意了,我既然这么自信的发上来,那肯定是有点小九九的啦。

首先我要说明一点,曼哈顿距离 H = abs(this->x - Goal。x) + abs(this->y - Goal。y);

对目标距离并不敏感,因为它的值分布图是呈现菱形由目标点向外扩散的,那么,这就意为这个菱形的边的距离等效,在移动的时候会将这菱形的所有边长均录入优先队列,那么怎么办呢?(注:下文将构成菱形边长的所有点称为等效点)
首先,更换曼哈顿距离计算,将其换成欧拉距离(简单点说就是平面直角坐标系里的两点间距离公式)

D = sqrt(pow(Goal。x - this->x, 2) + pow(Goal。y - this->y, 2)); // sqrt 为开平方,pow(X,2) 为 X^2 方。

然而,这又有何卵用呢?首先,根据欧拉距离得到的等效点不同于曼哈顿的菱形,它的比较好看,就像一个圆形一样一圈圈由目标点扩散出去。

Let's look at it!

1641852-20190902210526974-346658444.png

以上是曼哈顿距离值的情况

continue!

以下是欧拉距离值的情况

1641852-20190902210533619-1385322538.png

你会发现有一些些不同,首先欧拉数据类型比较小,存在浮点数,这就导致了它不能和 G 值共存(忘了 G 值意义的往回看看),而曼哈顿为整数,呈现出有棱有角的形式。从图上看欧拉距离更趋近于圆形而非菱形,这就导致了在一些凹障碍的时候,两个的表示会不太一样,现在我们看看接下来的优化,好滴,这就再举个栗子!

左图为 F = G + sideMAX * H, 右图为F = D;

起始点为 (12,10) ,终止点为 (12,24) 。

1641852-20190902210540292-1176282956.png1641852-20190902210545770-1846359370.png

接着简单说明一下两者的区别的各个的优势,这也是我发这篇文章的最最主要的目的,先看左图搜索次数,其上图表示搜索了点的分布情况,这里我们在其右下角有一个路线的统计,这个 196 是统计着所有搜索过的路径,简单点看图,对,就左图中上图的方块数量,那个数量就等同于该值,这就可以作为两者的效率的一种简单的比较了(请允许我不用 O(N) 之类的说法),那么开始好好说明一下吧~

效率上右图要比左图快得多,但由于不能和 G 值配套使用导致了其无法得到最短路径,这就是两者的区别,至于左图上方的尖角,那就是曼哈顿距离生成的菱形所导致的,这里我进行下一步优化~

1641852-20190902210552558-1784088197.png1641852-20190902210557711-154226068.png

具体的启发式函数,就留给想知道的同学啦,虽然我也不太清楚有几个同学会喜欢研究这类玩意啦,但作为我出山开端,怎么也得给点拿得出手的东西吧~后续还有一些研究就放着了,待我成果能够确定下来为止我再进一步发出来吧 =- = ~

转载于:https://www.cnblogs.com/juwan/p/11449030.html

<think>好的,我现在要帮助用户了解A*算法的实现和使用。首先,我需要回顾一下自己关于A*算法的知识,确保信息的准确性。A*算法是一种启发式搜索算法,常用于路径规划和图遍历,结合了Dijkstra算法和贪心最佳优先搜索的优点。它通过评估函数f(n)=g(n)+h(n)来选择路径,其中g(n)是从起点到当前节点的实际成本,h(n)是当前节点到目标的启发式估计成本。 接下来,用户提到了参考引用中的内容,特别是引用[1]提到的MATLAB/C++混合实现,这可能涉及到不同编程环境下的实现细节。另外,引用[3]提到了处理图中的不连通组件和循环,以及负权边的问题,需要注意到A*算法通常假设启发函数是可接受的(即不会高估实际成本),并且如果图中有负权边,可能会导致算法失效,这点需要在回答中提及。 用户的需求是了解A*算法的实现和使用,因此需要分步骤说明。首先,介绍A*算法的基本原理,然后给出具体的实现步骤,可能包括伪代码或代码示例。由于用户提供的引用中有Python示例(如快速排序),可能更适合用Python来展示A*的实现,但引用[1]提到了MATLAB/C++,可能需要考虑是否要涉及这些语言,但用户的问题更偏向于一般性的实现,所以可能用Python更合适。 接下来需要考虑A*算法的关键组成部分:开放列表、关闭列表、节点结构(包含g值、h值、父节点等)。在实现中,优先队列(通常用堆实现)用于管理开放列表,以快速获取f值最小的节点。同时,启发函数的选择,如曼哈顿距离、欧几里得距离或对角线距离,需要根据具体应用场景来决定。 在代码实现部分,需要定义节点类,初始化起始节点和目标节点,维护开放和关闭列表,处理邻居节点,计算g和h值,并更新路径。同时,需要考虑如何回溯路径,一旦找到目标节点,通过父节点指针反向追溯得到路径。 此外,需要讨论A*算法的优化方法,比如使用二叉堆提高开放列表的效率,预处理地图数据,或者动态调整启发函数的权重(如加权A*)。同时,应用场景如游戏AI、机器人路径规划、交通导航等,需要具体举例说明。 还要注意引用用户提供的参考资料,特别是引用[3]中提到的处理不连通组件和循环,以及负权边的问题。需要指出A*算法假设边权为正,因此在存在负权边时可能不适用,这可能导致算法无法找到最优解或陷入无限循环。 最后,生成相关问题,可能涉及其他算法的比较(如Dijkstra、BFS)、启发函数设计、性能优化、处理动态障碍物的方法等。确保问题能够引导用户进一步深入学习。 总结步骤: 1. 介绍A*算法的基本原理和核心公式。 2. 分步骤说明实现过程,包括数据结构算法流程。 3. 提供代码示例(Python),并解释关键部分。 4. 讨论优化策略和应用场景。 5. 引用相关文献,特别是处理图结构和边权的问题。 6. 生成相关问题,促进深入思考。</think>### A*算法实现应用详解 #### 一、算法原理 A*算法通过评估函数$f(n) = g(n) + h(n)$选择最优路径,其中: - $g(n)$:起点到节点$n$的实际成本 - $h(n)$:节点$n$到终点的启发式估计成本(需满足$h(n) \leq \text{真实成本}$)[^1] #### 二、实现步骤 1. **数据结构** - **开放列表**:优先队列(按$f(n)$排序) - **关闭列表**:已探索节点集合 - **节点结构**:记录坐标、$g$值、$h$值、父节点指针 2. **算法流程** 1. 初始化开放列表,加入起点 2. 循环直到找到目标或开放列表为空: - 取出$f(n)$最小的节点$u$ - 若$u$是终点,回溯路径 - 遍历$u$的邻居$v$: - 若$v$在关闭列表,跳过 - 计算$g_{temp} = g(u) + \text{移动成本}$ - 若$g_{temp} < g(v)$,更新$v$的$g$值和父节点 3. 返回路径或失败 #### 三、代码实现(Python示例) ```python import heapq class Node: def __init__(self, x, y): self.x = x self.y = y self.g = float('inf') self.h = 0 self.parent = None def f(self): return self.g + self.h def __lt__(self, other): return self.f() < other.f() def astar(grid, start, end): open_list = [] start.g = 0 start.h = abs(start.x - end.x) + abs(start.y - end.y) # 曼哈顿距离 heapq.heappush(open_list, start) closed_set = set() while open_list: current = heapq.heappop(open_list) if (current.x, current.y) == (end.x, end.y): path = [] while current: path.append((current.x, current.y)) current = current.parent return path[::-1] closed_set.add((current.x, current.y)) for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]: x, y = current.x + dx, current.y + dy if 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 0: neighbor = Node(x, y) if (x, y) in closed_set: continue tentative_g = current.g + 1 if tentative_g < neighbor.g: neighbor.g = tentative_g neighbor.h = abs(x - end.x) + abs(y - end.y) neighbor.parent = current heapq.heappush(open_list, neighbor) return None ``` #### 四、关键优化 1. **优先队列结构**:使用最小堆实现$O(\log n)$的插入/删除操作 2. **启发函数选择**: - 曼哈顿距离(网格4方向移动):$h(n) = |x_1 - x_2| + |y_1 - y_2|$ - 欧几里得距离(自由移动):$h(n) = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}$ 3. **动态加权**:$f(n) = g(n) + w \cdot h(n)$,$w>1$时加速搜索但可能牺牲最优性[^3] #### 五、应用场景 1. **游戏AI**:实时路径规划(RTS游戏单位移动) 2. **机器人导航**:SLAM系统中的避障路径生成 3. **交通规划**:GPS导航系统的最短路径计算 4. **网络路由**:数据包传输路径优化 #### 六、注意事项 1. **负权边问题**:若存在负权边,可能导致无法找到最优解,此时应改用Bellman-Ford算法 2. **启发函数可接受性**:必须满足$h(n) \leq$真实成本,否则可能错过最优解 3. **地图预处理**:对大规模静态地图可预先生成导航网格提升性能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值