人工智能实验(四)-A*算法求解迷宫寻路问题实验

零、A*算法学习参考资料

1.讲解视频

A*寻路算法详解 #A星 #启发式搜索_哔哩哔哩_bilibili

2.A*算法学习网站

A* 算法简介

一、实验目的

熟悉和掌握A*算法实现迷宫寻路功能,要求掌握启发式函数的编写以及各类启发式函数效果的比较。

二、实验要求

同课本 附录B 实验指导书 实验五的指导要求和实验报告要求:

1.画出用A*算法求解迷宫最短路径的流程图。

2.设置不同的地图,以及不同的初始状态和目标状态,记录A*算法的求解结果,包括最短路径、扩展节点数、生成结点数和算法运行时间。

3.对于相同的初始状态和目标状态,设计不同的启发式函数,比较不同启发式函数对迷宫寻路速度的提升效果,包括扩展节点数、生成结点数和算法运行时间。

实验内容

迷宫问题是在以方格表示的地图场景中,对于给定的起点、终点和障碍物,如何找到一条从起点开始避开障碍物到达终点的最短路径。 假设在一个n*m的迷宫里,入口坐标和出口坐标分别为(1,1)和(5,5),每一个坐标点有两种可能:0和1,其中0表示该位置允许通过,1表示该位置不允许通过。

三、实验材料

A*算法的基本原理:

A*算法在搜索过程中,考虑了每个节点到起点的实际代价(g(n))和从该节点到目标的估算代价(h(n))。它使用以下公式来计算每个节点的评估代价:

f(n)=g(n)+h(n)

g(n):从起点到节点n的实际代价,通常是路径长度或其他度量。

h(n):从节点n到目标节点的估算代价,通常通过启发式方法来计算。常见的启发式函数包括曼哈顿距离、欧几里得距离等。

f(n):节点n的总代价,它是g(n)和h(n)的和。A*算法会优先选择具有最小f值的节点进行扩展。

A*算法的步骤:

1. 初始化:将起点加入开放列表(Open List),并设置其g和h值。起点的f值为g + h。

2. 循环搜索:

3. 目标找到:当目标节点被加入闭合列表时,路径搜索结束。此时,可以从目标节点回溯到起点,得到最短路径。

4. 结束:如果开放列表为空,表示没有路径可达目标。

特点:

启发式:A*算法通过启发式函数来引导搜索过程,使得搜索在达到目标时更为高效。

最优性:只要启发式函数是可接受的(即不高估到目标的代价),A*算法可以保证找到最短路径。可接受的启发式函数通常需要满足一致性或单调性条件。

启发式函数的选择:

启发式函数h(n)的选择对A*算法的性能和效率有重要影响。常见的启发式函数包括:

曼哈顿距离:适用于格子状地图,只能横向或纵向移动。

欧几里得距离:适用于平面上的直线距离。

四、实验步骤

1.画出A*算法求解迷宫最短路径问题的流程图。

2.分析不同启发式函数h(n)对迷宫寻路求解的速度提升效果。

3.分析A*算法求解不同规模迷宫最短路径问题的性能。

4.提交源程序。

5.总结实验心得体会。

五、思考与问答

问:A*算法如何处理迷宫中的障碍物?

答:在A*算法中,每次扩展节点时,都需要检查其邻居节点是否为障碍物。如果邻居是障碍物,算法就会跳过这个邻居,不将其加入开放列表。障碍物被认为是无法通过的区域,因此它们在路径搜索过程中是不可达的。

问:A*算法保证找到最短路径吗?

答:是的,只要启发式函数是“可接受的”,即不高估从当前节点到目标节点的实际代价,A算法就能保证找到最短路径。对于迷宫问题,常见的启发式函数(如曼哈顿距离、欧几里得距离等)是可接受的,因此A可以找到从起点到终点的最短路径。

问:A*算法的效率如何?如何应对大规模迷宫?

答:A算法的效率取决于迷宫的复杂度和启发式函数的设计。在大规模迷宫中,A算法的时间复杂度和空间复杂度可能很高,因为需要维护开放列表和闭合列表。在这种情况下,可以采取以下优化策略:

- 启发式函数优化:选择合适的启发式函数,如曼哈顿距离、欧几里得距离等,以缩小搜索范围。

- 迭代加深A*(IDA*):通过限制搜索深度,减少内存消耗。

- 跳跃搜索:在某些情况下,通过跳过某些中间步骤,减少计算量。

六、源代码

'''from queue import PriorityQueuedef a_star_search(graph,start,goal):"""使用 A* 搜索算法在图中寻找从起点到终点的路径。参数:graph (Graph): 要搜索的图。start: 搜索的起点。goal: 搜索的终点。返回:tuple: 包含came_from和cost_so_far的元组。came_from (dict): 一个字典,存储了每个节点的前一个节点,用于回溯路径。cost_so_far (dict): 一个字典,存储了到每个节点的当前最小代价。"""# 使用优先级队列 frontier 来存储待探索的节点,优先级由估计的总代价决定frontier = PriorityQueue()# 将起点 start 放入 frontier 中,其优先级为 0frontier.put(start, 0)# 创建 came_from 字典,用于记录每个节点的前一个节点came_from = dict()# 创建 cost_so_far 字典,用于记录到每个节点的当前最小代价cost_so_far = dict()# 将起点 start 的前一个节点设置为 None,并将其代价设置为 0came_from[start] = Nonecost_so_far[start] = 0# 当 frontier 不为空时,循环执行以下操作while not frontier.empty():# 从 frontier 中取出具有最小代价的节点 currentcurrent = frontier.get()# 如果 current 节点等于目标节点 goal,则搜索结束if current == goal:break# 遍历 current 节点的所有邻居节点 nextfor next in graph.neighbors(current):# 计算从起点到 next 节点的新代价 new_costnew_cost = cost_so_far[current] + graph.cost(current, next)# 如果 next 节点没有被访问过,或者通过 current 节点到达 next 节点的代价更小if next not in cost_so_far or new_cost < cost_so_far[next]:# 更新到 next 节点的代价为 new_costcost_so_far[next] = new_cost# 计算从起点到 next 节点的估计总代价 prioritypriority = new_cost + heuristic(goal, next)# 将 next 节点及其估计总代价 priority 放入 frontier 中frontier.put(next, priority)# 将 next 节点的前一个节点设置为 currentcame_from[next] = current# 返回 came_from 和 cost_so_far 字典,用于回溯路径和计算总代价return came_from, cost_so_fardef heuristic(a, b):"""计算两个点 a 和 b 之间的曼哈顿距离。参数:a (tuple): 第一个点的坐标,格式为 (x1, y1)。b (tuple): 第二个点的坐标,格式为 (x2, y2)。返回:int: 两点之间的曼哈顿距离。"""# 解包元组 a 得到 x1 和 y1(x1, y1) = a# 解包元组 b 得到 x2 和 y2(x2, y2) = b# 计算 x 坐标之差的绝对值x_diff = abs(x1 - x2)# 计算 y 坐标之差的绝对值y_diff = abs(y1 - y2)# 返回曼哈顿距离,即 x 和 y 坐标之差的绝对值之和return x_diff + y_diff'''
# A* 算法代码from queue import PriorityQueue
import time
import mathclass MazeGraph:"""迷宫图的表示类,包含迷宫的行列数据,以及获取邻居节点和路径代价的方法。"""def __init__(self, maze):"""初始化 MazeGraph 对象。参数:maze (list of lists): 二维列表,代表迷宫的布局。返回:None"""# 将传入的迷宫赋值给实例变量 self.mazeself.maze = maze# 获取迷宫的行数,并赋值给实例变量 self.rowsself.rows = len(maze)# 获取迷宫的列数,并赋值给实例变量 self.colsself.cols = len(maze[0])def neighbors(self, node):"""返回当前节点的所有邻居节点"""# 获取节点的 x 和 y 坐标x, y = node# 初始化邻居节点列表neighbors = []# 定义四个方向的偏移量:左、右、上、下directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]# 遍历每个方向for dx, dy in directions:# 计算邻居节点的坐标nx, ny = x + dx, y + dy# 检查邻居节点是否在迷宫范围内,并且不是墙壁(值为 0)if 0 <= nx < self.rows and 0 <= ny < self.cols and self.maze[nx][ny] == 0:# 如果是有效邻居节点,则将其添加到列表中neighbors.append((nx, ny))# 返回所有有效的邻居节点return neighborsdef cost(self, from_node, to_node):"""返回从 from_node 到 to_node 的路径代价,这里代价为 1(单位代价)参数:from_node (tuple): 起点坐标。to_node (tuple): 终点坐标。解释:行走每一个格子的代价为 1(单位代价)"""return 1def a_star_search(graph, start, goal):"""使用 A* 搜索算法在图中寻找从起点到终点的路径。参数:graph (MazeGraph): 迷宫图对象。start: 搜索的起点。goal: 搜索的终点。返回:tuple: 包含came_from和cost_so_far的元组。came_from (dict): 一个字典,存储了每个节点的前一个节点,用于回溯路径。cost_so_far (dict): 一个字典,存储了到每个节点的当前最小代价。"""# 使用优先级队列 frontier 来存储待探索的节点,优先级由估计的总代价决定frontier = PriorityQueue()frontier.put((0, start))  # 将起点 start 放入 frontier 中,其优先级为 0# 创建 came_from 字典,用于记录每个节点的前一个节点came_from = dict()# 创建 cost_so_far 字典,用于记录到每个节点的当前最小代价cost_so_far = dict()# 将起点 start 的前一个节点设置为 None,并将其代价设置为 0came_from[start] = Nonecost_so_far[start] = 0# 记录扩展节点数和生成节点数expand_count = 0generate_count = 0# 记录起始时间start_time = time.time()while not frontier.empty():current_cost, current = frontier.get()expand_count += 1   # 扩展节点数加一# 如果 current 节点等于目标节点 goal,则搜索结束if current == goal:break# 遍历 current 节点的所有邻居节点 nextfor next in graph.neighbors(current):# 计算从起点到 next 节点的新代价 new_costnew_cost = cost_so_far[current] + graph.cost(current, next)# 如果 next 节点没有被访问过,或者通过 current 节点到达 next 节点的代价更小if next not in cost_so_far or new_cost < cost_so_far[next]:# 更新到 next 节点的代价为 new_costcost_so_far[next] = new_cost# 在这选择估价曼哈顿算法# heuristic = heuristic_manhattan(next, goal)# 在这选择估价欧几里得算法heuristic = heuristic_euclidean(next, goal)priority = new_cost + heuristic  # fn = gn + hn# 将 next 节点及其估计总代价 priority 放入 frontier 中frontier.put((priority, next))# 将当前结点更新在came_from中came_from[next] = currentgenerate_count += 1 # 生成节点数# 计算运行时间end_time = time.time()execution_time = end_time - start_timereturn came_from, cost_so_far, expand_count, generate_count, execution_timedef heuristic_manhattan(a, b):"""计算两个点 a 和 b 之间的曼哈顿距离。参数:a (tuple): 第一个点的坐标,格式为 (x1, y1)。b (tuple): 第二个点的坐标,格式为 (x2, y2)。返回:int: 两点之间的曼哈顿距离。"""(x1, y1) = a(x2, y2) = breturn abs(x1 - x2) + abs(y1 - y2)def heuristic_euclidean(a, b):"""计算两个点 a 和 b 之间的欧几里得距离。参数:a (tuple): 第一个点的坐标,格式为 (x1, y1)。b (tuple): 第二个点的坐标,格式为 (x2, y2)。返回:float: 两点之间的欧几里得距离。"""(x1, y1) = a(x2, y2) = breturn math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)def reconstruct_path(came_from, start, goal):"""从 came_from 字典中回溯路径。参数:came_from (dict): 包含每个节点的前一个节点的字典。start: 起点。goal: 终点。返回:list: 从 start 到 goal 的路径。"""# 初始化路径列表path = []# 将当前节点设置为终点current = goal# 当当前节点不等于起点时,循环执行以下操作while current!= start:# 将当前节点添加到路径中path.append(current)# 从 came_from 字典中获取当前节点的前一个节点,并更新当前节点current = came_from[current]# 将起点添加到路径中path.append(start)# 反转路径,使其从起点到终点path.reverse()# 返回重构的路径return pathdef print_map(maze):"""打印迷宫地图。参数:maze (list of lists): 二维列表,代表迷宫的布局。返回:None"""# 遍历迷宫的每一行for row in maze:# 使用 join 方法将每一行的单元格值连接成一个字符串,单元格之间用空格分隔print(" ".join(str(cell) for cell in row))def mark_path_on_map(maze, path):"""在迷宫中标记路径,使用字母 A, B, C 等。参数:maze (list of lists): 二维列表,代表迷宫的布局。path (list of tuples): 路径上的点的列表,每个点是一个 (x, y) 坐标对。返回:list of lists: 修改后的迷宫地图,其中路径上的点被标记为字母。"""# 初始化字母为 'A'letter = 'A'# 遍历路径上的每个点for (x, y) in path:# 包括起点和终点maze[x][y] = letter# 更新字母到下一个letter = chr(ord(letter) + 1)# 返回标记路径后的迷宫地图return mazeimport randomdef generate_maze(rows, cols, obstacle_probability=0.3):# 初始化迷宫地图,所有单元格默认为可通行(0)maze = [[0 for _ in range(cols)] for _ in range(rows)]# 定义起点和终点start = (0, 0)end = (rows - 1, cols - 1)# 使用深度优先搜索生成从起点到终点的路径def dfs(x, y):stack = [(x, y)]visited = set()visited.add((x, y))while stack:cx, cy = stack.pop()# 随机打乱邻居顺序neighbors = [(cx + dx, cy + dy) for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]]random.shuffle(neighbors)for nx, ny in neighbors:if 0 <= nx < rows and 0 <= ny < cols and (nx, ny) not in visited:if (nx, ny) == end or dfs(nx, ny):maze[cx][cy] = 0maze[nx][ny] = 0return Truereturn False# 生成路径dfs(*start)# 随机生成障碍物for i in range(rows):for j in range(cols):if (i, j) != start and (i, j) != end and maze[i][j] == 0:if random.random() < obstacle_probability:maze[i][j] = 1return mazedef main():'''    # 定义迷宫地图,0 表示可通行,1 表示障碍物maze = [[0, 0, 0, 0, 0],[1, 0, 1, 0, 1],[0, 0, 1, 1, 1],[0, 1, 0, 0, 0],[0, 0, 0, 1, 0]]''''''# 测试样例1:无解maze = [[0, 0, 0, 0, 0],[1, 1, 1, 0, 1],[0, 1, 1, 1, 0],[0, 1, 0, 1, 0],[0, 0, 0, 1, 0]
]
''''''# 测试样例2:maze = [[0, 0, 0, 0, 0],[0, 0, 0, 0, 0],[0, 0, 0, 0, 0],[0, 0, 0, 0, 0],[0, 0, 0, 0, 0]
]
''''''# 测试样例3:maze = [[0, 1, 0, 0, 0],[0, 1, 0, 1, 0],[0, 1, 0, 1, 0],[0, 1, 0, 1, 0],[0, 0, 0, 0, 0]
]'''maze = generate_maze(10, 10)# 设置起点和终点坐标start = (0, 0)  # 起点 (1,1)end = (9, 9)  # 终点 (5,5)# 创建迷宫图对象graph = MazeGraph(maze)# 调用 A* 搜索算法,返回 came_from 字典和 cost_so_far 字典came_from, cost_so_far, expand_count, generate_count, execution_time = a_star_search(graph, start, end)# 如果找到路径if end in came_from:# 重构路径path = reconstruct_path(came_from, start, end)# 打印最短路径print("最短路径: ", [f"({p[0] + 1},{p[1] + 1})" for p in path])# 在迷宫地图上标记路径marked_maze = mark_path_on_map([row[:] for row in maze], path)# 打印带路径标记的迷宫地图print("迷宫地图带路径标记:")print_map(marked_maze)# 输出扩展节点数、生成节点数、算法运行时间print(f"扩展节点数: {expand_count}")print(f"生成节点数: {generate_count}")print(f"算法运行时间: {execution_time:.30f} 秒")else:# 如果没有找到路径,打印提示信息print("没有找到路径")if __name__ == "__main__":main()

七、结果

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

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

相关文章

Web开发(一)HTML5

Web开发&#xff08;一&#xff09;HTML5 写在前面 参考黑马程序员前端Web教程做的笔记&#xff0c;主要是想后面自己搭建网页玩。 这部分是前端HTML5CSS3移动web视频教程的HTML5部分。主要涉及到HTML的基础语法。 HTML基础 标签定义 HTML定义 HTML(HyperText Markup Lan…

LabVIEW水位监控系统

LabVIEW开发智能水位监控系统通过集成先进的传感技术与控制算法&#xff0c;为工业液体存储提供精确的水位调控&#xff0c;保证了生产过程的连续性与安全性。 项目背景 在化工和饮料生产等行业中&#xff0c;水位控制的准确性对保证生产安全和提高产品质量至关重要。传统的水…

【Rust】结构体定义域实例化

目录 思维导图 1. 结构体的定义与实例化 1.1 结构体的基本概念 1.2 定义结构体 1.3 创建结构体实例 1.4 结构体的定义与实例化示例 2. 访问与修改结构体字段 2.1 访问字段 2.2 修改字段 3. 结构体实例的构造函数 3.1 构造函数的定义 3.2 使用字段初始化简写 4. 结…

013:深度学习之神经网络

本文为合集收录&#xff0c;欢迎查看合集/专栏链接进行全部合集的系统学习。 合集完整版请参考这里。 深度学习是机器学习中重要的一个学科分支&#xff0c;它的特点就在于需要构建多层且“深度”的神经网络。 人们在探索人工智能初期&#xff0c;就曾设想构建一个用数学方式…

Java 将RTF文档转换为Word、PDF、HTML、图片

RTF文档因其跨平台兼容性而广泛使用&#xff0c;但有时在不同的应用场景可能需要特定的文档格式。例如&#xff0c;Word文档适合编辑和协作&#xff0c;PDF文档适合打印和分发&#xff0c;HTML文档适合在线展示&#xff0c;图片格式则适合社交媒体分享。因此我们可能会需要将RT…

【2024年华为OD机试】(C卷,100分)- 攀登者1 (Java JS PythonC/C++)

一、问题描述 题目描述 攀登者喜欢寻找各种地图&#xff0c;并且尝试攀登到最高的山峰。 地图表示为一维数组&#xff0c;数组的索引代表水平位置&#xff0c;数组的元素代表相对海拔高度。其中数组元素0代表地面。 例如&#xff1a;[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0]&…

day06_Spark SQL

文章目录 day06_Spark SQL课程笔记一、今日课程内容二、DataFrame详解&#xff08;掌握&#xff09;5.清洗相关的API6.Spark SQL的Shuffle分区设置7.数据写出操作写出到文件写出到数据库 三、Spark SQL的综合案例&#xff08;掌握&#xff09;1、常见DSL代码整理2、电影分析案例…

Copula算法原理和R语言股市收益率相依性可视化分析

阅读全文&#xff1a;http://tecdat.cn/?p6193 copula是将多变量分布函数与其边缘分布函数耦合的函数&#xff0c;通常称为边缘。在本视频中&#xff0c;我们通过可视化的方式直观地介绍了Copula函数&#xff0c;并通过R软件应用于金融时间序列数据来理解它&#xff08;点击文…

Spring Boot 支持哪些日志框架

Spring Boot 支持多种日志框架&#xff0c;主要包括以下几种&#xff1a; SLF4J (Simple Logging Facade for Java) Logback&#xff08;默认&#xff09;Log4j 2Java Util Logging (JUL) 其中&#xff0c;Spring Boot 默认使用 SLF4J 和 Logback 作为日志框架。如果你需要使…

OpenCV基础:视频的采集、读取与录制

从摄像头采集视频 相关接口 - VideoCapture VideoCapture 用于从视频文件、摄像头或其他视频流设备中读取视频帧。它可以捕捉来自多种源的视频。 主要参数&#xff1a; cv2.VideoCapture(source): source: 这是一个整数或字符串&#xff0c;表示视频的来源。 如果是整数&a…

Uniapp仿ChatGPT Stream流式输出(非Websocket)

Uniapp仿ChatGPT Stream流式输出&#xff08;非Websocket&#xff09; 前言&#xff1a;流式输出可以使用websocket也可以使用stream来实现EventSource是 HTML5 中的一个接口&#xff0c;用于接收服务器发送的事件流&#xff08;Server - Sent Events&#xff0c;SSE&#xff…

《自动驾驶与机器人中的SLAM技术》ch2:基础数学知识

目录 2.1 几何学 向量的内积和外积 旋转矩阵 旋转向量 四元数 李群和李代数 SO(3)上的 BCH 线性近似式 2.2 运动学 李群视角下的运动学 SO(3) t 上的运动学 线速度和加速度 扰动模型和雅可比矩阵 典型算例&#xff1a;对向量进行旋转 典型算例&#xff1a;旋转的复合 2.3 …

深入 Flutter 和 Compose 在 UI 渲染刷新时 Diff 实现对比

众所周知&#xff0c;不管是什么框架&#xff0c;在前端 UI 渲染时&#xff0c;都会有构造出一套相关的渲染树&#xff0c;并且在 UI 更新时&#xff0c;为了尽可能提高性能&#xff0c;一般都只会进行「差异化」更新&#xff0c;而不是对整个 UI Tree 进行刷新&#xff0c;所以…

Elasticsearch—索引库操作(增删查改)

Elasticsearch中Index就相当于MySQL中的数据库表 Mapping映射就类似表的结构。 因此我们想要向Elasticsearch中存储数据,必须先创建Index和Mapping 1. Mapping映射属性 Mapping是对索引库中文档的约束&#xff0c;常见的Mapping属性包括&#xff1a; type&#xff1a;字段数据类…

occ的开发框架

occ的开发框架 1.Introduction This manual explains how to use the Open CASCADE Application Framework (OCAF). It provides basic documentation on using OCAF. 2.Purpose of OCAF OCAF (the Open CASCADE Application Framework) is an easy-to-use platform for ra…

esp32在编译是报错在idf中有该文件,但是说没有

报错没有头文件esp_efuse_table.h D:/Espressif/frameworks/esp-idf-v5.3.1/components/driver/deprecated/driver/i2s.h:27:2: warning: #warning "This set of I2S APIs has been deprecated, please include driver/i2s_std.h, driver/i2s_pdm.h or driver/i2s_tdm.h …

git - 用SSH方式迁出远端git库

文章目录 git - 用SSH方式迁出远端git库概述笔记以gitee为例产生RSA密钥对 备注githubEND git - 用SSH方式迁出远端git库 概述 最近一段时间&#xff0c;在网络没问题的情况下&#xff0c;用git方式直接迁出git库总是会失败。 失败都是在远端, 显示RPC错误。 但是git服务器端…

http和https有哪些不同

http和https有哪些不同 1.数据传输的安全性&#xff1a;http非加密&#xff0c;https加密 2.端口号&#xff1a;http默认80端口&#xff0c;https默认443端口 3.性能&#xff1a;http基于tcp三次握手建立连接&#xff0c;https在tcp三次握手后还有TLS协议的四次握手确认加密…

超详细-java-uniapp小程序-引导关注公众号、判断用户是否关注公众号

目录 1、前期准备 公众号和小程序相互关联 准备公众号文章 注册公众号测试号 微信静默授权的独立html 文件 2&#xff1a; 小程序代码 webview页面代码 小程序首页代码 3&#xff1a;后端代码 1&#xff1a;增加公众号配置项 2&#xff1a;读取公众号配置项 3&…

【Python进阶——分布式计算框架pyspark】

Apache Spark是用于大规模数据处理的统一分析引擎 简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成百上千的服务器集群&#xff0c;计算TB、PB乃至EB级别的海量数据&#xff0c;Spark作为全球顶级的分布式计算框架&#xff0c;支持众多的编程语言进行开…