A*寻路算法

news2024/11/19 19:31:52

广度优先算法搜索以广度做为优先级进行搜索。

从起点开始,首先遍历起点周围邻近的点,然后再遍历已经遍历过的点邻近的点,逐步的向外扩散,直到找到终点。

这种算法就像洪水(Flood fill)一样向外扩张。直至洪水将整张地图都漫延。在访问节点时候,每个点需要记录到达该点的前一个点的位置(父节点),访问到终点时候,便可以从终点沿着父节点一路走回到起点,从而找出路径。(注意:这也是A*算法的一部分)

这种洪水蔓延式寻找路径的方式较为野蛮粗暴,仅仅依据广度来找路径难以找到最短的路径

Dijkstra算法

在实际寻路场景中,要考虑“移动成本”。不同的路径有不同的成本,例如,穿过平原或沙漠可能需要 1 个移动点,但穿过森林或丘陵可能需要 5 个移动点。玩家在水中行走的成本是在草地上行走的 10 倍。为此我们需要Dijkstra算法。

在Dijkstra算法中,需要计算每一个节点距离起点移动的总移动代价,同时,还需要一个优先队列结构,对于所有待遍历的节点,放入优先队列中,优先队列会按照代价进行排序。(这也是在下面要介绍的A*算法实现案例中待优化的点!)

在算法运行过程中,每次都从优先队列中选出移动成本最小的作为下一个要遍历检查的节点,直到访问到达终点为止。

两种算法的对比:

考虑这样一种场景,在一些情况下,图形中相邻节点之间的移动代价并不相等。例如,游戏中的一幅图,既有平地也有山脉,那么游戏中的角色在平地和山脉中移动的速度通常是不相等的。

在Dijkstra算法中,需要计算每一个节点距离起点的总移动代价。同时,还需要一个优先队列结构。对于所有待遍历的节点,放入优先队列中会按照代价进行排序。

在算法运行的过程中,每次都从优先队列中选出代价最小的作为下一个遍历的节点。直到到达终点为止。

下面对比了不考虑节点移动代价差异的广度优先搜索与考虑移动代价的Dijkstra算法的运算结果:

广度优先算法: Dijkstra算法:

启发式搜索?

通过广度优先搜索和 Dijkstra 算法,边界向各个方向扩展。如果试图找到通往所有位置或许多位置的路径,这是一个合理的选择。

如果仅仅是找到到达一个位置的路径,我们应当控制洪水的流向,时期朝向目标位置流动。而控制方向的条件判断便可以通过启发式搜索来完成。

而所谓启发式搜索函数便是用来计算当前点到达目标点的距离(步数),预测到达目标点的距离。

启发函数来计算到目标点距离方式有多种选择:

  • 曼哈顿距离

    如果图形中只允许朝上下左右四个方向移动,则启发函数可以使用曼哈顿距离,它的计算方法如下图所示:

    img

    曼哈顿距离=abs(node.x-target.x)+abs(node.y-target.y)

  • 对角移动

    如果图形中允许斜着朝邻近的节点移动,则启发函数可以使用对角距离。

    img

  • 欧几里得距离

    如果图形中允许朝任意方向移动,则可以使用欧几里得距离。

    欧几里得距离2=(node.x-target.x)2+(node.y-target.y)2

采用启发函数控制广度优先搜索的方式为(贪婪)最佳优先搜索。

最佳优先搜索(Best First)

最佳优先搜索的原理也简单,与Dijkstra算法类似(广度优先+移动成本),也使用一个优先队列存储启发函数值(该点与目标点的距离),每次选取与目标点最近的点(邻居)来访问,直至访问到终点。

可以理解算法是广度优先算法+选择最小目标点距离参数的结果。

image

同样,由于没有考虑移动成本,在移动成本不同的地图中也不能考虑到森林、沼泽等移动速度不同的场景造成的移动成本的增加。不能保证找到的路径是最短的路径。

那就结合在一起呗!

A*算法=广度优先算法+考虑“移动成本”+考虑“与目标点的距离”;

既可以保证聪明地选择好走的路线避开难走的路线或者障碍物,也可以集中管控搜索方向向着目标点中的位置搜寻。

下面是对三个算法的详细对比:

A*算法集合了前两个算法的优势,可以保证在两点之间找到最佳的路线。

image

A*寻路算法

 

寻路算法的启发函数控制:(理解两个参数对搜寻的影响)

  • 在极端情况下,当启发函数h(n)始终为0,则将由g(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
  • 如果h(n)始终小于等于节点n到终点的代价,则A*算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
  • 如果h(n)完全等于节点n到终点的代价,则A*算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
  • 如果h(n)的值比节点n到终点的代价要大,则A*算法不能保证找到最短路径,不过此时会很快。
  • 在另外一个极端情况下,如果h()n相较于g(n)大很多,则此时只有h(n)产生效果,这也就变成了最佳优先搜索。

在寻路过程中,每次依据周边节点的f(n)数值来选择,每次选择最小的f(n);

 

//核心算法
public static class Pathfinding {
    /// <summary>
    /// A*算法核心
    /// </summary>
    /// <param name="startNode">开始点</param>
    /// <param name="targetNode">目标点</param>
    /// <returns>最短路径</returns>
    /// <exception cref="Exception"></exception>
    public static List<NodeBase> FindPath(NodeBase startNode, NodeBase targetNode) {
        var toSearch = new List<NodeBase>() { startNode };//要处理搜寻的节点(未访问过)
        var processed = new List<NodeBase>();//存储访问过的节点
 
        while (toSearch.Any()) {//判断是否有要搜寻的元素节点
            //fixit 待优化的点 可以使用一个堆排序思想 
            var current = toSearch[0];
            foreach (var t in toSearch) 
                if (t.F < current.F || t.F == current.F && t.H < current.H) current = t;
 
            processed.Add(current);
            toSearch.Remove(current);//搜寻过后就移除
 
            current.SetColor(ClosedColor);
 
            //终点检查
            if (current == targetNode) {
                var currentPathTile = targetNode;
                var path = new List<NodeBase>();//保存路径
                var count = 100;
                //从终点到出发点的节点路径寻找
                while (currentPathTile != startNode) {
                    path.Add(currentPathTile);
                    currentPathTile = currentPathTile.Connection;
                    count--;//这里根据地图的大小设置一个路径长度极限(可以忽略)
                    if (count < 0) throw new Exception();
                    Debug.Log("sdfsdf");
                }
                foreach (var tile in path) tile.SetColor(PathColor);
                startNode.SetColor(PathColor);
                Debug.Log(path.Count);
                return path;//返回路径
            }
            //关心一下自己的邻居--可以到达的邻居,并且还未访问过的(非障碍物)
            foreach (var neighbor in current.Neighbors.Where(t => t.Walkable && !processed.Contains(t))) {
                var inSearch = toSearch.Contains(neighbor);//获取对邻居的访问状况
                var costToNeighbor = current.G + current.GetDistance(neighbor);
                //更新一下邻居的G值
                //对于已经访问过的邻居 如果从另一个方向过来想再次访问,那么G值应当比之前访问的要小(只能是最近路程访问)
                if (!inSearch || costToNeighbor < neighbor.G) {
                    neighbor.SetG(costToNeighbor);
                    neighbor.SetConnection(current);//记录与之连接的节点(算作路径中)
                    //如果是第一次访问 还需要进行H值的计算
                    if (!inSearch) {
                        neighbor.SetH(neighbor.GetDistance(targetNode));//计算启发函数值
                        toSearch.Add(neighbor);//将邻居添加到要访问的列表中
                    }
                }
            }
        }
        return null;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1232839.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CRM系统的销售预测是什么?怎么做?

简单来说&#xff0c;销售预测可以通过销售关键信息为团队预测收入&#xff0c;分配目标。CRM中的销售预测可以帮助企业制定合理的销售目标和策略&#xff0c;并通过实时数据发现瓶颈所在&#xff0c;提高团队绩效。下面说说CRM中销售预测是什么&#xff1f;如何销售预测&#…

广度优先算法(BFS)

广度优先算法&#xff08;Breadth-First Search&#xff09;是在图和树领域的搜索方法&#xff0c;其核心思想是从一个起始点开始&#xff0c;访问其所有的临近节点&#xff0c;然后再按照相同的方式访问这些临近节点的节点&#xff0c;这种访问方式类似涟漪泛起&#xff0c;一…

[汇编实操]DOSBox工具安装——Ubuntu18.04系统

一、下载&安装 sudo apt install -y dosbox 二、启动 dosbox 三、C盘挂载 将上述文件下载放在任意路径&#xff0c;将DEBUG目录映射为虚拟C盘 MASM.EXE 是用来编译的&#xff0c;LINK.EXE 用来链接&#xff0c;这俩是必须的。 执行如下命令&#xff1a; mount c /m…

基于Java封装继承多态实现的一个简单图书系统

首先我们大概了解下图书系统的需求 1.要有两种身份 管理员和普通用户。普通用户和管理员分别对应的功能不一样&#xff0c;需要分开实现 2. 图书系统肯定要有图书&#xff0c;和存放图书的地方&#xff0c;存放就用数组来实现 3.实现对应用户的功能 接下来我们第一步&#xf…

react重要知识点(面经)

react重要知识点&#xff08;面经&#xff09; react生命周期classhooks reduxredux 核心概念redux 计数器案例 react页面加载卡顿使用懒加载异步加载JavaScript压缩和缓存静态资源使用React.memo() PubSub使用方式1.1 react导入库1.2 react 页面引入pubsubjs1.3 pubsubjs使用2…

企业要满足什么条件才能实施CRM系统?

CRM的作用相信大家也所有了解&#xff0c;但并不是所有的企业都适合实施CRM。或者说&#xff0c;大部分企业实施CRM并不会100%的成功。那么&#xff0c;企业实施CRM的条件是什么&#xff1f;下面我们就来说一说。 1、业务规模 如果您的客户数量较少&#xff0c;没有复杂的客户…

数据中心走向绿色低碳,液冷存储舍我其谁

引言&#xff1a;没有最冷&#xff0c;只有更冷&#xff0c;绿色低碳早已成为行业关键词。 【全球存储观察 &#xff5c; 科技热点关注】 每一次存储行业的创新&#xff0c;其根源离不开行业端的用户需求驱动。 近些年从数据中心建设的整体发展情况来看&#xff0c;从风冷到…

隧道ip网络广播系统

隧道ip网络广播系统 隧道ip网络广播系统的优势有那些&#xff1f; 节省人力及维护成本&#xff1a;隧道ip网络广播系统可以自动播放节目&#xff0c;无需人工操作&#xff0c;节省了人力成本。定时广播&#xff0c;分区广播&#xff0c;全区广播&#xff0c;方便管理和简易化…

钩子函数-hook

钩子函数-hook hook 的作用 利用钩子函数可以在所有测试用例执行前做一些预置操作&#xff08;如&#xff1a;准被测试数据、测试环境&#xff09; 或者在测试结束后做一些后置操作&#xff08;如&#xff1a;清理测试数据&#xff09; 钩子函数在其它框架中也有&#xff0…

在线客服系统源码 聊天记录实时保存 附带完整的搭建教程

在线客服系统是一个企业网站进行网络营销的最重要的工具。企业进行网络宣传后&#xff0c;会有很多访客进入到网站&#xff0c;这时候网站就需要有在线客服人员进行接待&#xff0c;及时的与访客进行沟通&#xff0c;才能留住访客&#xff0c;变流量为销量。 在线客服系统可以…

Vue框架学习笔记——指令语法:v-bind动态绑定属性、data的层级结构

文章目录 指令语法v-bind&#xff0c;增加标签内属性动态绑定方式 data的层级结构 指令语法 在标签中想让属性动态变化的时候&#xff0c;不能使用插值语法。 插值语法一般用于标签体中&#xff0c;当针对标签的里面的时候&#xff0c;例如标签属性&#xff0c;就可以使用指令…

捷报连连!怿星科技荣获北京市科学技术进步奖一等奖

近期&#xff0c;北京市科学技术委员会、中关村科技园区管理委员会揭晓了2022年北京市科学技术奖的获奖名单。其中&#xff0c;由清华大学牵头、怿星科技参与开发的《电动汽车底盘运动控制与能量管理关键技术及应用》项目荣获“北京市科学技术进步奖一等奖”。 作为北京市政府设…

销售团队可以借助CRM系统做什么?

销售主管都想有一支效率高、质量高的销售团队&#xff0c;无论对于初创企业还是大型企业销售团队都是企业盈利的主力部门&#xff0c;直接为企业带了业绩。如何提升销售团队水平&#xff1f;离不开CRM系统的辅助&#xff0c;CRM软件能为销售团队提供哪些支持&#xff1f;下面我…

什么样的企业可以使用免费版的CRM?

市面上大部分的免费CRM不需要付费即可使用&#xff0c;但是对于使用人数和功能进行了部分限制。下面我们就来说说&#xff0c;免费CRM的适用对象是谁&#xff1f; 1、初创/小微企业 这种小微企业没有太多的资金&#xff0c;也没有复杂的客户管理需求&#xff0c;仅仅需要一款…

智能井盖传感器建设信息化时代智慧城市

近年来随着信息技术的快速发展和城市化进程的加速推进&#xff0c;智慧城市的概念逐渐成为现实。作为智慧城市生命线建设中的重要组成部分&#xff0c;智能井盖传感器的应用正在为城市的可持续发展和居民的生活质量提供新的解决方案。 智能井盖传感器能够实时监测井盖状态&…

数据质量校验

1.事实表包含昨日数据 2.昨日同比趋势分析 圆通业务量较为平稳 &#xff0c;每日数据量和昨日比差距不足20%&#xff0c;会做数据量的昨日环比差距分析

汇编-MOVSXD64位带符号扩展传送

允许源操作数为32位的寄存器或内存操作数 ExitProcess PROTO .code main PROCmov ebx, 0FFFFFFFFh movsxd rax, ebx ;RAX FFFFFFFFFFFFFFFFhmov ebx, 01FFFFFFFh movsxd rdx, ebx ;RDX 000000001FFFFFFFhmov ecx,0 ;结束程序call ExitProcess main ENDP E…

洗衣行业在线预约小程序源码系统 附带完整的搭建教程

目前&#xff0c;人们对生活品质的追求不断提高&#xff0c;但生活节奏却也不断加快。对品质的追求遇到了忙碌的生活节奏&#xff0c;人们更渴望以最简单、便捷的方式达到追求品质的目的。同时&#xff0c;由于线上支付的普及&#xff0c;大家更希望足不出户就可以解决自己生活…

Windows本地搭建rtmp推流服务

前言 开发时偶尔需要使用rtmp直播流做视频流测试&#xff0c;苦于网上开源的rtmp视频流都已经失效&#xff0c;无奈只好尝试在本地自己搭建一个rtmp的推流服务&#xff0c;方便测试使用。 一、工具准备 Nginx&#xff1a;使用nginx-rtmp-win64推流工具FFmpeg&#xff1a;官方…

算法设计与分析算法实现——删数问题

通过棋盘输入一个高精度的正整数n(n的有效位数<=240)去掉其中任意s个数字后,剩下的数字按原左右次序将组成一个新的正整数。变成对给定的n和s,寻找一种方案,使得剩下的数字组成的新数最小。 输入:n,s 输出:最后剩下的最小数 输入实例: 178543 4 输出示例: 13 首先…