一种单目标A*算法设计与实现

news2024/11/17 1:57:29

一种单目标A*算法设计与实现

作者:吴屏珊

  • 最近在学习简单的单目标A*算法,其中在CSDN上阅读到的一篇博文给了我很大启发,于是在该博文的基础上,笔者记录了一点自己对于A*算法的体会和感悟。
  • 原文链接

目录

文章目录

  • 一种单目标A*算法设计与实现
    • 目录
    • 1. A*算法简单介绍
      • 1.1 A*算法的基本要素
      • 1.2 A*算法的中心思想
      • 1.3 A*算法所需数据结构
        • 1.3.1 定义点Node的结构
        • 1.3.2 记录点状态的几个表
    • 2. A\*算法步骤演示
      • 2.1 第一轮操作
      • 2.2 第二轮操作
    • 3. A*算法实现代码
      • 3.1 定义Node类
      • 3.2 Solution方法
      • 3.3 main方法
      • 3.4 输出结果


1. A*算法简单介绍

1.1 A*算法的基本要素

Image
1、图的说明:有一个8*8的地图,其中绿色的点为起点,红色的点为终点,黑色的点为障碍点(即不可达点),当然边界也是不可达点。

2、将要实现的目标:从起点走到终点,其中要求所走距离最短并且要绕开所有不可达点。
3、移动方向:每个点只有上,下,左,右四个方向可以行进。

1.2 A*算法的中心思想

  • 我们首先思考一个问题作为引入:既然每一个点有四种移动方向,那如何判断该点下一步的移动方向呢,如何选出在该点下一步即将到达的所有点中选出最好的点呢?
    (1)这里我们需要用到一个公式:F=G+H;其中F称为代价,G为从起点到该点已走过的距离,H为从该点到终点将走的曼哈顿距离。
    (2)曼哈顿距离:在地图上我们忽略所有障碍点(不可达点),两点的横坐标之差的绝对值与纵坐标之差的绝对值之和即为两点之间的曼哈顿距离,数学公式表示如下: ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ {|x_1-x_2|+|y_1-y_2|} x1x2+y1y2 ;代码表示如下:Math.abs(x1-x2)+Math.abs(y1-y2)
    (3)选取移动方向的依据:F,每次移动前,计算下一步可到达点的 F 值,然后对所有可到达点(不限于下一步)的 F 值进行比较,优先选取移动代价最小即 F 值最小的点。

1.3 A*算法所需数据结构

1.3.1 定义点Node的结构

1、点的坐标(x,y)
2、计算点的代价时所需数值:F,G,H
3、父节点:Father;父节点记录的是该节点的上一个节点(即该节点是由哪个节点遍历到的)。
  父节点的作用:A*算法从起点开始寻路,当找到终点的时候代表最短路径已经找到,这时我们可以利用终点结构的Father来回溯到起点(起点的Father为NULL),从而找出并且输出这条最短路径。

1.3.2 记录点状态的几个表

1、优先队列Open表:记录由该点下一步可以到达的所有点,并且每次将F值最小的点放在队首。
2、Close表:记录所有已经走过的点。
3、Exist表:记录所有遍历过的点(即在Open与Close中出现过的点)。
4、Close表与Exist表的区别:Close中存储的点是已走过的点,Exist表中存储的点是已遍历过,但不一定走过的点。

2. A*算法步骤演示

  • Open表不为空且未找到终点时一直进行循环

2.1 第一轮操作

  • 首先我们将起点放入Open表中

    alt textalt text
  • 在Open表中找到当前F值最小的结点A,并将该点移出Open表,加入到Close表中。
  • 遍历该点A四周所有可到达节点,如果这些节点不包含在Exist表中(即未出现过),则计算它们的F值,并且根据F值大小顺序加入到Open表中。

    alt textalt text
  • 记录这些点的父节点为该点A。

    alt text

2.2 第二轮操作

  • 在第一轮操作结束后的Open表中,将队首节点(即F值最小的节点)移出Open表,加入到Close表中。
  • 遍历该点B四周所有可到达节点,如果这些节点不包含在Exist表中(即未出现过),则计算它们的F值,并且根据F值大小顺序加入到Open表中。
  • (3,3)节点为障碍物,(3,1)节点已被Exist表包含,所以两个点都不加入到Open表中。

    alt textalt text
  • 记录新加入这些点的父节点为该点B。

    alt text

3. A*算法实现代码

3.1 定义Node类

  • init_Node(Father,end):传入父节点和终点,并计算两者间的曼哈顿距离,最终根据公式算出该点的F值。
  • compareTo(Node):用于比较出F值最小的点
class Node implements Comparable<Node> {
    public int x;  //x坐标
    public int y;  //y坐标
    public int F;  //F属性
    public int G;  //G属性
    public int H;  //H属性
    public Node Father;    //此结点的上一个结点
    //获取当前结点的坐标
    public Node(int x, int y) {
        this.x = x;
        this.y = y;
    }
    //通过结点的坐标可以得到F, G, H三个属性
    //需要传入这个节点的上一个节点和最终的结点
    public void init_node(Node father, Node end) {//father是父节点
        this.Father = father;
        if (this.Father != null) {
            this.G = father.G + 1;
        } else { //父节点为空代表它是第一个结点
            this.G = 0;
        }
        //计算通过现在的结点的位置和最终结点的位置计算H值
        this.H = Math.abs(this.x - end.x) + Math.abs(this.y - end.y);
        this.F = this.G + this.H;
    }
    // 用来进行和其他的Node类进行比较
    @Override
    public int compareTo(Node o) {
        return Integer.compare(this.F, o.F);
    }
}

3.2 Solution方法

  • is_exist方法:判断是否在Exist表中出现过。
public boolean is_exist(Node node)
    {
        for (Node exist_node : Exist) {
            if (node.x == exist_node.x && node.y == exist_node.y) {
                return true;
            }
        }
        return false;
    }
  • is_valid方法:判断是否合法(边界,不可达点,已存在于Exist表中都为不合法)
public boolean is_valid(int x, int y) {
        // 如果结点的位置是-1,则不合法
        if (map[x][y] == -1) return false;
        for (Node node : Exist) {
            //如果结点出现过,不合法
//            if (node.x == x && node.y == y) {
//                return false;
//            }
            if (is_exist(new Node(x, y))) {
                return false;
            }
        }
        //以上情况都没有则合法
        return true;
    }
  • extend_current_node方法:遍历该点的上下左右四个方向,并判断这些点是否合法。
public ArrayList<Node> extend_current_node(Node current_node) {
        int x = current_node.x;
        int y = current_node.y;
        ArrayList<Node> neighbour_node = new ArrayList<Node>();
        if (is_valid(x + 1, y))
        {
            Node node = new Node(x + 1, y);
            neighbour_node.add(node);
        }
        if (is_valid(x - 1, y))
        {
            Node node = new Node(x -1, y);
            neighbour_node.add(node);
        }
        if (is_valid(x, y + 1))
        {
            Node node = new Node(x, y + 1);
            neighbour_node.add(node);
        }
        if (is_valid(x, y - 1))
        {
            Node node = new Node(x, y - 1);
            neighbour_node.add(node);
        }
        return neighbour_node;
    }
  • astarSearch方法:A*算法具体实现方法。
public Node astarSearch(Node start, Node end) {
        //把第一个开始的结点加入到Open表中
        this.Open.add(start);
        //把出现过的结点加入到Exist表中
        this.Exist.add(start);
        //主循环
        while (Open.size() > 0) {
            //取优先队列顶部元素并且把这个元素从Open表中删除
            Node current_node = Open.poll();
            //将这个结点加入到Close表中
            Close.add(current_node);
            //对当前结点进行扩展,得到一个四周结点的数组
            ArrayList<Node> neighbour_node = extend_current_node(current_node);
            //对这个结点遍历,看是否有目标结点出现
            //没有出现目标结点再看是否出现过
            for (Node node : neighbour_node) {
                if (node.x == end.x && node.y == end.y) {//找到目标结点就返回
                    node.init_node(current_node,end);
                    return node;//返回的是终止结点
                }
                if (!is_exist(node)) {  //没出现过的结点加入到Open表中并且设置父节点
                    node.init_node(current_node, end);
                    Open.add(node);
                    Exist.add(node);
                }
            }
        }
        //如果遍历完所有出现的结点都没有找到最终的结点,返回null
        return null;
    }

3.3 main方法

  • 绘制8*8地图,并设置好起点和终点(原文直接在地图中进行起点终点的设置,修改起点和终点时相对麻烦,我在这里对原文进行了改进,在代码中设置起点终点,测试不同种情况时相对便捷)。
int[][] map = {
                {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
                {-1,  0,  0,  0,  0,  0,  0,  0,  0, -1},
                {-1,  0,  0,  0,  0, -1,  0,  0,  0, -1},
                {-1,  0,  0,  0, -1,  0,  0,  0,  0, -1},
                {-1,  0,  0,  0, -1,  0,  0,  0,  0, -1},
                {-1,  0,  0,  0,  0, -1,  0,  0,  0, -1},
                {-1,  0,  0,  0, -1,  0,  0,  0,  0, -1},
                {-1,  0,  0,  0,  0, -1,  0,  0,  0, -1},
                {-1,  0,  0,  0,  0,  0,  0,  0,  0, -1},
                {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
        };
        Node start = new Node(4, 2);
        map[start.x][start.y]=1;
        start.Father = null;
        Node end = new Node(4, 7);
        map[end.x][end.y]=2;
  • 调用Solution函数,拿到最短路径
Solution solution = new Solution();
        Node res_node = solution.astarSearch(start, end);//res—node最先接收的是该地图的终止
        while (res_node != null) {
            map[res_node.x][res_node.y] = res_node.G;
            res_node = res_node.Father;//迭代操作,从终止结点开始向后退,直到起点的父节点为null。循环终止
        }
  • 输出路径,我在这里对输出地图进行了一定程度上的渲染,红色代表边界和障碍不可达点,绿色代表起点和终点,蓝色代表路径,数字代表所走的步数(我在这里进行了改进,使输出地图更加直观)。
//渲染迷宫
        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                Node nownode =new Node(i,j);
                if(map[i][j]==-1)
                    System.out.printf("\033[31m%3d\033[0m", map[i][j]);//red
                else if (equal_node(nownode,start) || equal_node(nownode,end))
                    System.out.printf("\033[32m%3d\033[0m", map[i][j]);//green
                else if(map[i][j]==0 && !equal_node(nownode,start))
                    System.out.printf("%3d", map[i][j]);//black
                else
                    System.out.printf("\033[34m%3d\033[0m", map[i][j]);//blue
            }
            System.out.println();
        }

3.4 输出结果


Image

源代码已保存到课题组github文件夹

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

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

相关文章

如何破解西门子博途V19里的密码设置

现在使用TIA Portal V19的工程师是越来越多了&#xff0c;V19有个显著的变化就是访问密码的设置&#xff0c;很多小伙伴忽然发现已经用了很多年的功能&#xff0c;在改动以后都不会设置了&#xff0c;那我们今天就带着您看一下如何才能在 V19 中正确的设置 S7-1500 访问密码。 …

论文笔记(四十六)RobotGPT: Robot Manipulation Learning From ChatGPT

xx RobotGPT: Robot Manipulation Learning From ChatGPT 文章概括摘要I. 介绍II. 相关工作III. 方法论A. ChatGPT 提示机器人操作B. 机器人学习 IV. 实验A. 衡量标准B. 实验设置C. 模拟实验D. 真实机器人实验E. AB测试 V. 结论 文章概括 引用&#xff1a; article{jin2024r…

【算法】模拟:(leetcode)6.Z 字形变换(medium)

目录 题目链接 题目介绍 解法 1、模拟&#xff1a; 2、找矩阵中的规律&#xff1a; 公差 第一行和最后一行 中间行 代码 题目链接 6. Z 字形变换 - 力扣&#xff08;LeetCode&#xff09; 题目介绍 解法 1、模拟&#xff1a; 采用模拟的思想&#xff0c;按照Z字形&…

《动手学深度学习》笔记1.7——模型选择 + 过拟合-欠拟合

目录 1. 模型选择 1.1 训练误差 vs. 泛化误差 1.2 验证数据集 vs. 测试数据集 1.3 K-折交叉验证 1.4 总结 2. 过拟合与欠拟合&#xff08;核心知识&#xff09; 2.1 过拟合 vs. 欠拟合 2.2 模型容量 2.3 估计模型容量 2.4 VC维 衡量模型容量 2.5 数据复杂度 3. 代…

Vue3:element-plus el-Table列表合计处理显示字符串类型/计算合计数值

需求整理 1.使用element组件库中的 el-table组件实现图上 底部当前页合计的功能。在一般的情况下&#xff0c;只需要计算数值部分的值&#xff0c;因为组件中的方法中处理的就是将值的类型转换成数值类型&#xff0c;像string类型的字符串的话&#xff0c;在进行转换的时候会出…

什么是远程过程调用(RPC)

进程间通信(IPC) 进程间通信(Inter-Process Communication)是指两个进程或者线程之间传送数据或者信号的一些技术或者方法。进程是计算机进行资源分配的最小的单位。每个进程都有自己独立的系统资源,而且彼此之间是相对隔离的。为了使得不同的进程之间能够互相访问,相互协…

Redis: 特色,业务场景举例,底层原理,持续进阶等问题梳理

Redis 的特色 Redis 是目前使用非常广泛的中间件&#xff0c;在于它功能强大&#xff0c;持续改进&#xff0c;经久不衰主要体现在以下几点 1 ) 高性能: Redis 的底层是C语言编写的&#xff0c;它的定位是内存型数据库而且 Redis 的作者对操作系统也非常的精通它通讯方式采用了…

MobileNetV2: Inverted Residuals and Linear Bottlenecks

Link&#xff1a;https://arxiv.org/abs/1801.04381 这篇文章是一篇关于MobileNetV2的学术论文&#xff0c;主要介绍了MobileNetV2的架构设计及其在图像分类、目标检测和语义分割任务中的应用。以下是对这些核心内容的简要概述&#xff1a; MobileNetV2架构设计&#xff1a; …

深度学习技术在超材料科学中的应用与实操

人工智能算法赋能材料设计与应用专题培训 前沿背景 人工智能与材料科学的融合趋势&#xff1a;在材料科学领域&#xff0c;人工智能&#xff08;AI&#xff09;的引入正在引发一场革命。传统的材料设计和优化依赖于经验和试错方法&#xff0c;这不仅耗时且成本高昂。关于AI赋…

Note_XML学习笔记

XML学习笔记 1. XML 教程 经常见到XML学习一下。由于是学到中间才想起记笔记&#xff0c;之前的就简略回顾一下&#xff1a; 1&#xff09;XML是数据存储的一种语言载体&#xff1b; 2&#xff09;只负责存储&#xff0c;不负责显示&#xff1b; 3&#xff09;和HTML语言的风…

简单的算法题

1、求12345 #include <stdio.h> int main(){int i,s1;for(i1;i<5;i){s s*i;}printf("%d",s); }2、求1357911 #include <stdio.h> int main(){int i,s1;for(i1;i<11;ii2){s s*i;}printf("%d",s); }3、判定2000—2500年中的每一年是否…

CSP-CCF★★★201909-2小明种苹果(续)★★★

一、问题描述 二、解答 关键&#xff1a;判断是否发生苹果掉落&#xff0c;使用flag[]数组来标记&#xff0c;1为掉落&#xff0c;0为没有掉落&#xff0c;这样也是为了后续比较连续三棵树是否掉落 误区&#xff1a;用最后一次正数&#xff08;即最后一次统计苹果个数&#x…

ChatGPT 提取文档内容,高效制作PPT、论文

随着人工智能生成内容&#xff08;AIGC&#xff09;的快速发展&#xff0c;利用先进的技术工具如 ChatGPT 的 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;模式&#xff0c;可以显著提升文档内容提取和内容创作的效率。以下将详细介绍如…

LabVIEW提高开发效率技巧----使用状态机架构

状态机架构&#xff08;State Machine Architecture&#xff09;是LabVIEW编程中的一种常见且高效的设计模式&#xff0c;特别适合用于处理具有多个操作状态的复杂系统。通过这种架构&#xff0c;程序能够根据不同的输入条件或事件&#xff0c;在多个状态之间切换&#xff0c;从…

canvas分享,从入门到入门。

开始之前 canvas是一个可以使用脚本在其中绘制图形的 HTML 元素.它本身并不具备绘图能力&#xff0c;需要配合JavaScript使用 用途 游戏应用特效字体相册&#xff0c;幻灯片股票行情等动态图像思维图以及图形编辑器等在线可视化工具 基本特性 canvas元素会初始化宽度为300像…

Java | Leetcode Java题解之第434题字符串中的单词数

题目&#xff1a; 题解&#xff1a; class Solution {public int countSegments(String s) {int segmentCount 0;for (int i 0; i < s.length(); i) {if ((i 0 || s.charAt(i - 1) ) && s.charAt(i) ! ) {segmentCount;}}return segmentCount;} }

黎巴嫩寻呼机爆炸案背后的技术原理

引言 近日&#xff0c;黎巴嫩发生了一起震惊世界的寻呼机爆炸事件。当地时间9月17日和19日&#xff0c;黎巴嫩多地的寻呼机和对讲机先后发生爆炸&#xff0c;造成了大量人员伤亡。这起事件引发了全球对电子设备安全性的广泛关注。本文将从技术角度分析这次寻呼机爆炸案的背后原…

OTTO奥托机器人开发总结

OTTO机器人是一个开源外壳&#xff0c;硬件和软件的桌面机器人项目&#xff0c;非常适合新手研究和拓展。 我一直希望找一个合适的项目入手研究机器人&#xff0c;这种项目最好是软硬件都开源的&#xff0c;可以随着自己的想法无限的扩展和私人订制&#xff0c;做为初学者&…

【计网】从零开始掌握序列化 --- 基础知识储备与程序重构

从零开始掌握序列化与反序列化 1 初识序列化与反序列化2 再谈Tcp协议3 程序重构3.1 Socket类3.2 回调函数设计3.3 最终的Tcp服务器类 1 初识序列化与反序列化 在刚学习计算机网络时&#xff0c;我们谈到过网络协议栈&#xff0c;其中最上层的就是应用层&#xff0c;那么这个应…

探索Llama 3.1:开源模型的本地部署与创新应用实践

文章目录 1 Llama 3.1模型的突破性进展2 Llama 3.1模型在业务场景中的实践案例3 使用教程4 Llama 3.1在客户服务中的运用 1 Llama 3.1模型的突破性进展 在数字化转型的浪潮中&#xff0c;大型语言模型&#xff08;LLM&#xff09;以其卓越的处理能力和广泛的应用潜力&#xff…