用java实现A*寻路算法

前言:

最近的开发中遇到了寻路这个知识点,然后去了解了一下最常见的A算法,本会会结合我的理解,用最通俗易懂的话语讲解A算法的原理,下面会给出代码示例。

说到寻路算法,就涉及到了图的遍历,然后又分为深度优先和广度优先等等,这些知识点大家随便一搜就能查到,为了方便这里给个链接,里面有介绍算法的发展史。

在A出来之前,遍历算法要不就是效率不高,要不就是不是最优路径,A的出现,是结合了前面的有点,所以说A*只是相对来说效率很高的一种算法。

A*算法

A*算法通过下面这个函数来计算每个节点的优先级。
算法表示:
在这里插入图片描述
其中:

  • f(n)是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
  • g(n) 是节点n距离起点的代价。本文中,会以父子节点距离相加来算距离起点的距离。
  • h(n)是节点n距离终点的预计代价,这也就是A*算法的启发函数。关于启发函数我们在下面详细讲解。

一句话来解释就是:比如从A到C,中间有个点B,A算法会计算B离A的距离和B离C的距离的综合值来判定,顾前又顾后。
A
算法在运算过程中,每次从优先队列中选取f(n)值最小(优先级最高)的节点作为下一个待遍历的节点。

关键集合: 有两个集合openList和closeList,openList存放待遍历的坐标点,closeList存放遍历过的坐标点,closeList最后也用来表示路径。

如果用文字来描述A*算法:

  1. 将起始点加入openList。
  2. while循环,只要openList不包含目标点(如果包含了说明已经找到路径了),从opelist取出一个f(n)最小值(第一次是起始点)的点A,然后将点A周围的点,计算好f(n),放入openList。
  3. openList删除点A。
  4. closeList加入点A。
  5. 输入路径。

为了便于第二步取f(n)最小的点,openList和closeList,我都用的PriorityQueue优先级队列,里面规则就是f(n)最小的在最顶端。

然后简单说一下启发函数,对于网格形式的图,有以下这些启发函数可以使用:

  • 如果图形中只允许朝上下左右四个方向移动,则可以使用曼哈顿距离(Manhattan distance)。
  • 如果图形中允许朝八个方向移动,则可以使用对角距离。
  • 如果图形中允许朝任何方向移动,则可以使用欧几里得距离(Euclidean distance)。

本文中使用的欧几里得距离,也就是我们上学学的,两点坐标相减的平方,再开根号
在这里插入图片描述

代码部分:

我们先看看效果图:
我们随机生成一个20x15的地图,其中0表示可走,* 代表是障碍物不可走,起点A和终点B都是随机生成的。
在这里插入图片描述
然后看看算法给出的路径:这里路径用红色底色标出了。
在这里插入图片描述
其实这样一看,A*算法的步数少,时间也用的少,效率高。

下面就开始说下代码部分的思路,首先我们约定几点关键内容:

  1. 约定每一个格子的距离是1,这样算,如果A和B是平行的、挨着的两个点,那么他们距离相距1,如果A和B是斜对角的点,那么就是根号2,约定于1.4几,我们取1.4,为了没有小数,我们代码中在计算的时候都乘以10,所以就是10和14(代码中有这样的计算方式)。
  2. 我们用Node类来表示地图上每一个坐标点,每个Node都有parentNode,parentNode的用处就是用来计算g(n),通过累加来计算出当前点离起始点的g(n)值

代码主要有3个类,

  • Map.java,地图生成类,主要用来生成地图坐标和起始点。
  • Node.java,地图坐标的包装类,里面存了一些信息。
  • AStar.java,寻路算法类,里面是寻路算法的实现。

下面贴上所有类的代码,里面有关键注释。我们先从入口代码开始看:

public class MainTest {public static void main(String[] args) {Map map = new Map();map.ShowMap(null);AStar aStar = new AStar();aStar.search(map);}
}

Node.java

/*** 地图节点类*/
@Data
public class Node {//坐标的x和yprivate int x;private int y;//value:0表示可通过,*表示障碍物private String value;//这三个参数就是f(n)=g(n)+h(n)private double fValue = 0;private double gValue = 0;private double hValue = 0;//标注当前节点是否走过private boolean reachable;private Node parentNode;public Node(int x, int y, String value, boolean reachable) {this.x = x;this.y = y;this.value = value;this.reachable = reachable;}public Node() {}
}

Map.java

/*** 地图类*/
@Data
public class Map {public static final int Length = 15;public static final int Weight = 20;private Node[][] map;private Node start;private Node end;public Map() {map = new Node[Length][Weight];Random random = new Random();int startX = random.nextInt(Length - 2) + 1;int startY = random.nextInt(Weight - 2) + 1;int endX = random.nextInt(Length - 2) + 1;int endY = random.nextInt(Weight - 2) + 1;System.out.println("起点坐标:" + startX + "," + startY);System.out.println("目标点坐标:" + endX + "," + endY);for (int i = 0; i < Length; i++) {for (int j = 0; j < Weight; j++) {Node node = new Node(i, j, "0", true);if (i == 0 || j == 0 || i == Length - 1 || j == Weight - 1) {node.setValue("*");node.setReachable(false);} else {int num = random.nextInt(10);if (num < 3) {node.setValue("*");node.setReachable(false);}if (i == startX && j == startY) {node.setValue("A");node.setReachable(true);start = node;}if (i == endX && j == endY) {node.setValue("B");node.setReachable(true);end = node;}}map[i][j] = node;}}}public void ShowMap(PriorityQueue<Node> nodeList) {System.out.print("\\" + "   ");for (int i = 0; i < Weight; i++) {System.out.print(i + "   ");}System.out.println();for (int i = 0; i < Length; i++) {for (int j = 0; j < Weight; j++) {Node node = map[i][j];String value = map[i][j].getValue();if (nodeList == null) {if ("A".equals(value) || "B".equals(value)) {System.out.format("\33[41;4m" + value + "\33[0m" + "   ");} else {if (j == 0) {System.out.print(i + "   ");System.out.print(value + "   ");} else {System.out.print(value + "   ");}}} else {if (j == 0) {System.out.print(i + "   ");}if (nodeList.contains(node)) {System.out.format("\33[41;4m" + value + "\33[0m" + "   ");} else {System.out.print(value + "   ");}}}System.out.println();}}
}

Astar,java

public class AStar {private final PriorityQueue<Node> openList = new PriorityQueue<>((o1, o2) -> (int) (o1.getFValue() - o2.getFValue()));private final PriorityQueue<Node> closeList = new PriorityQueue<>((o1, o2) -> (int) (o1.getFValue() - o2.getFValue()));private Map map;public void search(Map mapData) {this.map = mapData;Node start = map.getStart();openList.add(start);while (!openList.contains(mapData.getEnd())) {Node node = openList.peek();assert node != null;selectNearByNode(node);node.setReachable(false);openList.remove(node);closeList.add(node);}closeList.add(mapData.getEnd());showPath(closeList, map);}/*** 将选中节点周围的节点添加进“开启列表”*/public void selectNearByNode(Node currentNode) {int x = currentNode.getX();int y = currentNode.getY();for (int dx = (x > 0 ? -1 : 0); dx <= (x < Map.Length ? 1 : 0); ++dx) {for (int dy = (y > 0 ? -1 : 0); dy <= (y < Map.Weight ? 1 : 0); ++dy) {if (dx != 0 || dy != 0) {Node node = map.getMap()[x + dx][y + dy];if (node.isReachable() && !openList.contains(node)) {node.setParentNode(currentNode);node.setGValue(getGValue(node));node.setHValue(getHValue(node, map.getEnd()));node.setFValue(getFValue(node));openList.add(node);}}}}}/*** 获取H值** @param currentNode:当前节点* @param endNode:终点*/public double getHValue(Node currentNode, Node endNode) {return (Math.abs(currentNode.getX() - endNode.getX()) + Math.abs(currentNode.getY() - endNode.getY())) * 10;}/*** 获取G值** @param currentNode:当前节点*/public double getGValue(Node currentNode) {if (currentNode.getParentNode() != null) {if (currentNode.getX() == currentNode.getParentNode().getX() || currentNode.getY() == currentNode.getParentNode().getY()) {//判断当前节点与其父节点之间的位置关系(水平?对角线)return currentNode.getParentNode().getGValue() + 10;}return currentNode.getParentNode().getGValue() + 14;}return currentNode.getGValue();}/*** 获取F值 : G + H*/public double getFValue(Node currentNode) {return currentNode.getGValue() + currentNode.getHValue();}/*** 将路径标记出来*/public void showPath(PriorityQueue<Node> closeList, Map map) {if (closeList.size() > 0) {System.out.println("路径为:");map.ShowMap(closeList);}}
}

总的来说,A*算法还是很简单的,如果有啥疑问欢迎在评论区询问,如果有错误也欢迎指出。

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

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

相关文章

代码学习记录14

随想录日记part14 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.03.07 主要内容&#xff1a;今天的主要内容是二叉树的第三部分&#xff0c;主要涉及二叉树最大深度&#xff1b;二叉树最小深度&#xff1b;完全二叉树的节点个数。 104.二叉树的最大深度111.二…

Jmeter基础使用---Token鉴权接口关联

接口测试流程&#xff1a; 查看API接口文档&#xff0c;熟悉接口业务&#xff08;地址、端口、参数、鉴权、状态码&#xff09;设计接口测试用例&#xff08;正例&#xff1a;正确的结果&#xff1b;反例&#xff1a;鉴权异常、参数异常、兼容异常、其他异常&#xff09;使用接…

受投资人青睐,易鑫租赁深交所再发8.10亿ABS,利率再创新低

近日&#xff0c;上海易鑫融资租赁有限公司&#xff08;以下简称“易鑫租赁”&#xff09;成功发行“天风-易鑫租赁惠民4期资产支持专项计划”&#xff0c;募集资金8.10亿元&#xff08;人民币&#xff0c;下同&#xff09;。此次发行利率再创易鑫ABS历史新低。 2023年1月&…

windows安装程序无法将windows配置为此计算机

目录 问题描述 问题原因 解决办法 方法一 方法二 方法三&#xff1a; 问题描述 重装系统时显示windows安装程序无法将windows配置在此计算机硬件上. 问题原因 安装介质已损坏 如果可引导的安装介质&#xff08;如DVD或USB驱动器&#xff09;损坏或损坏&#xff0c;安装过…

17-Java解释器模式 ( Interpreter Pattern )

Java解释器模式 摘要实现范例 解释器模式&#xff08;Interpreter Pattern&#xff09;实现了一个表达式接口&#xff0c;该接口解释一个特定的上下文 这种模式被用在 SQL 解析、符号处理引擎等 解释器模式提供了评估语言的语法或表达式的方式&#xff0c;它属于行为型模式 …

buildadmim生成代码时让菜单有层级

当我们使用buildadmin生成代码的时候&#xff0c;在菜单的部分&#xff0c; 有时希望它生的是一个带有层级的菜单&#xff0c;有时候则想生成一个没有层级的菜单 like this 经过本人测试 如果我们要生成没有层级的菜单 我们可以在高级设置中的 相对位置处更改&#xff0c;同时…

使用java批量写入环境变量

环境需求 jdk版本&#xff1a;1.8 jna依赖&#xff1a; <dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>5.10.0</version></dependency><dependency><groupId>net.java.…

ServletContext

ServletContext 1.共享数据 ServletContext servletContext this.getServletContext(); String username "徐凤年"; servletContext.setAttribute("username",username);ServletContext servletContext this.getServletContext(); String username (…

因果学习篇(2)-Causal Attention for Vision-Language Tasks(文献阅读)

Causal Attention for Vision-Language Tasks 引言 这篇论文是南洋理工大学和澳大利亚莫纳什大学联合发表自2021年的CVPR顶会上的一篇文献&#xff0c;在当前流行的注意力机制中增加了因果推理算法&#xff0c;提出了一种新的注意力机制&#xff1a;因果注意力(CATT)&#xff…

航芯防护组合拳「MCU+安全」,让数字资产加倍安全!

在这个万物互联的时代&#xff0c;数据安全的“飓风”正在袭来。随着集成电路的广泛应用&#xff0c;安全问题也日益凸显。从芯片漏洞到硬件攻击&#xff0c;这些问题都给集成电路的应用带来了严峻的挑战。一旦安全防线被突破&#xff0c;智能设备的数字资产安全将面临威胁。 …

电动自行车易着火的启示

电动自行车起火情况不时发生&#xff0c;上海雷卯EMC小哥分析原因&#xff0c;或许对您有所启示。 电动自行车容易发生起火的原因可能是由于电池管理系统&#xff08;BMS&#xff09;的保护功能不足或者电池质量问题导致的。要改进BMS的保护功能以减少火灾风险&#xff0c; 可…

【CSP】201403-3-命令行选项

CSP-201403-3-命令行选项 关键点&#xff1a;将整行字符串按空格分割 在解析命令行时&#xff0c;一个常见的需求是将整个命令行字符串分割成多个部分&#xff0c;通常以空格为分隔符。这些部分包括命令行工具的名称、选项&#xff08;可能带有前缀-或--&#xff09;和这些选项…

mongodb 图形界面工具 -- Studio 3T(下载、安装、连接mongodb数据库)

目录 mongodb 图形界面工具 -- Studio 3T下载安装第一次使用&#xff1a;注册添加一个连接&#xff08;连接 mongodb 数据库&#xff09;1、点击【添加新连接】&#xff0c;选择【手动配置我的连接设置】2、对 Server 设置连接数据3、连接的用户认证设置&#xff08;创建数据库…

航芯1-Wire安全认证新品上市,防抄板和耗材认证高性价比之选

随着用户产品附加值提升带来的对防抄板的需求&#xff0c;以及电池、医疗耗材、电子配件、IOT领域中对设备认证的需求&#xff0c;上海航芯经过多年的技术积累和对市场的理解&#xff0c;推出了高性价比的ACL16_Axx系列。 1-Wire单总线&#xff1a;小尺寸实现高效传输 ACL16_…

JAVA WEB开发 错误:无效发行版解决办法

1.首先查询本安装的jdk版本 在cmd中输入如下指令 2.打开file——progect structure 2.将这两处保持一致即可

一图看懂:什么是“新质生产力”?

◆2023年9月&#xff0c;首次提出“新质生产力”。 ◆2024年1月&#xff0c;强调“加快发展新质生产力&#xff0c;扎实推进高质量发展”。 ◆2024年3月&#xff0c;《政府工作报告》中提出&#xff0c;要大力推进现代化产业体系建设&#xff0c;加快发展新质生产力。充分发挥…

SpringBoot【问题 05】PostgreSQL数据库启用SSL后使用默认配置进行数据库连接(Navicat工具与Java程序)

官网SSL说明&#xff1a;https://www.postgresql.org/docs/9.1/libpq-ssl.html 1.配置 1.1 文件 使用SSL需要的4个文件&#xff0c;名称要一致&#xff1a; 客户端密钥&#xff1a;postgresql.keyJava客户端密钥&#xff1a;postgresql.pk8客户端证书&#xff1a;postgresq…

阿里云2核4G服务器支持人数并发测试,2核4G主机测评

阿里云2核4G服务器多少钱一年&#xff1f;2核4G配置1个月多少钱&#xff1f;2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

进制之间的转换

文章目录 编译过程进制转换1、进制的概念1.1 二进制1.2 八进制1.3 十六进制 进制在程序中的表现方式十进制转二进制将十进制转换成二进制&#xff08;除2反序取余法&#xff09;二进制转十进制&#xff08;权值法&#xff09; 八进制转十进制将十进制转换成八进制(除8反序取余法…

医药行业五大难题深度剖析:CRM解决方案助力突围

医疗行业关系着民生、经济乃至战备&#xff0c;是国民经济的重要组成部分。虽然近20年来我国医疗行业年均增长率维持在15%之上&#xff0c;但行业发展仍存在诸多问题。引进CRM管理系统可能是一个行之有效的解决方法。文中将为您整理医疗行业目前的五大挑战&#xff0c;以及CRM如…