人工智能-A*算法-最优路径搜索实验

news2024/11/23 19:35:18

上次学会了《A*算法-八数码问题》,初步了解了A*算法的原理,本次再用A*算法完成一个最优路径搜索实验。

一、实验内容
1. 设计自己的启发式函数。
2. 在网格地图中,设计部分障碍物。
3. 实现A*算法,搜索一条最优路径。

二、A*算法实现步骤

1. 初始化:设置起始节点和目标节点,并创建一个open列表和一个closed列表。open列表用于存储待检查的节点,closed列表用于存储已经检查过的节点。
2. 开始循环,直到open列表为空:
  2.1. 从open列表中取出f值最小的节点(这个值是由g值(从起始节点到当前节点的实际距离)和h值(从当前节点到目标节点的启发式估计距离)之和组成的)。
  2.2. 如果这个节点是目标节点,那么就找到了最优路径,结束循环。
  2.3. 否则,将这个节点从open列表移动到closed列表,并检查它的所有邻居节点。
  2.4. 对于每一个邻居节点:
    i. 如果它已经在closed列表中,那么跳过它。
    ii. 如果它不在open列表中,那么添加它,并计算它的g值和f值。
    iii. 如果它已经在open列表中,那么比较g值:如果当前节点有更小的g值,那么更新它的g值和f值。
3. 如果open列表为空(没有找到路径),那么算法失败。
4. 返回从起始节点到目标节点的最短路径。

三、启发式估算距离方法

1, 曼哈顿距离计算公式

2, 欧几里得距离计算公式

 

3, 定义启发式函数

# 曼哈顿距离计算函数(上下左右4个方向移动)
def manhattan_distance(pos1, pos2):
    # 曼哈顿距离计算; D = |X1-X2|+|Y1-Y2| ;两点X,Y值差的绝对值和
    return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1])


# 欧几里得距离的计算函数(上下左右及其对角线8个方向移动)
# 相对来说,欧几里得距离计算函数更准确点
def euclidean_distance(pos1, pos2):
    # 欧几里得距离计算; D = 开平方根( (X1-X2)^2 + (Y1-Y2)^2 ) ;两点X,Y值差的平方和,再开平方根,即2点间的绝对距离。
    return math.sqrt((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2)


# 定义启发式函数,这里只使用欧几里得距离作为估价值
def heuristic(pos1, pos2):
    return euclidean_distance(pos1, pos2)

四、A*算法寻找最优路径

1,根据欧几里得距离计算寻找最优路径示例图

2,A*算法寻找最优路径方法

# 定义A*算法
def a_star_path(array, start, goal):
    # 所有可能的移动方向,包括对角线方向
    neighbors = [(0, 1), (0, -1), (1, 0), (-1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]
    # 只考虑上下左右移动方向,不包括对角线方向
    # neighbors = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    '''
        计算每个节点的总评分f值(f(n) = g(n) + h(n))来寻找最优路径
        g值(从起始节点到下一个移动方向节点的实际距离)
        h值(从下一个移动方向到目标节点的估计距离,使用启发式heuristic函数计算)
    '''
    # 起点的g,f初始值
    g0 = 0
    h0 = heuristic(start, goal)
    gscore = {start: g0}
    fscore = {start: g0 + h0}
    # 定义一个优先队列,open列表,用于存储待检查的节点,open列表的优先级由f值决定
    open_list = []
    # 关闭列表(表示已经访问过),用于存储已经检查过的节点,以避免重复检查
    close_set = set()
    # 从哪里来字典,记录节点来源,当成父节点
    come_from = {}
    '''
        heapq实现了一个适合与Python的列表一起使用的最小堆排序算法。
        heapq.heappush(),创建一个堆;当数据源添加新项时,将维护元素的堆排序顺序。
        heapq.heappop(),弹出并返回堆中的最小项。
        将起点加入open列表,fscore[start]为起点的优先级f值,即启发式函数估算值
    '''

    # 将起点加入open列表,fscore[start]为起点的优先级f值,即启发式函数估算值
    heapq.heappush(open_list, (fscore[start], start))
    # 遍历open列表
    while open_list:
        # 取出f值最小的节点作为当前节点
        current = heapq.heappop(open_list)[1]
        # 如果到达目标点,返回路径
        if current == goal:
            data = []
            # 从目标点向起点遍历路径
            while current in come_from:
                # 将当前点的位置加入路径
                data.append(current)
                # 将当前点设为从哪里来的节点,继续向上遍历
                current = come_from[current]
            # 将起始点的位置也加入路径
            data.append(start)
            # 将路径反转,因为我们是从目标向起点遍历的,所以需要反转得到真正的路径
            return data[::-1]
        # 将当前节点加入closed列表,表示已经检查过该节点了
        close_set.add(current)
        # 对当前节点的所有移动方向进行遍历
        for i, j in neighbors:
            # 根据当前节点的位置计算邻居节点的位置
            neighbor = current[0] + i, current[1] + j
            # 从当前节点到邻居节点的距离,作为 tentative_g_score 值(暂时的g值)
            tentative_g_score = gscore[current] + heuristic(current, neighbor)
            # 如果邻居节点在地图之外,跳过该邻居节点
            if 0 <= neighbor[0] < array.shape[0]:
                if 0 <= neighbor[1] < array.shape[1]:
                    # 如果邻居节点是障碍物,跳过该邻居节点
                    if array[neighbor[0]][neighbor[1]] == 1:
                        continue
                else:
                    continue
            else:
                continue
            '''
                如果邻居节点在closed列表中,并且从当前节点到邻居节点的距离比之前计算的距离长,
                则跳过该邻居节点
            '''
            if neighbor in close_set and tentative_g_score >= gscore.get(neighbor, 0):
                continue
            '''
                如果从当前节点到邻居节点的距离比之前计算的更短,或者邻居节点不在open列表中的话
                更新邻居节点的g值和f值,并将邻居节点重新加入open列表中(因为g值发生了变化)
            '''
            if tentative_g_score < gscore.get(neighbor, 0) or neighbor not in [i[1] for i in open_list]:
                come_from[neighbor] = current
                #
                ''' 更新邻居节点的g值和f值
                    计算每个节点的总评分f值(f(n) = g(n) + h(n))来寻找最优路径
                    g值(从起始节点到下一个移动方向节点的实际距离)
                    h值(从下一个移动方向到目标节点的估计距离,使用启发式heuristic函数计算)
                '''
                gscore[neighbor] = tentative_g_score
                h_score = heuristic(neighbor, goal)
                fscore[neighbor] = tentative_g_score + h_score
                # 将邻居节点重新加入open列表中
                heapq.heappush(open_list, (fscore[neighbor], neighbor))

    return False

 3,绘制网格地图和障碍物

# 定义网格矩阵长宽
map_size = (20, 20)
# 设置起点和终点
start = (0, 0)
end = (map_size[0] - 1, map_size[1] - 1)


# 随机生成网格矩阵障碍物位置
def generate_obstacles(map_size):
    # 生成障碍物位置个数在10到20之间的随机数
    num_obstacles = random.randint(map_size[0], (map_size[0] / 4) * (map_size[1] / 4))
    # 生成所有可能的位置
    all_positions = [(i, j) for i in range(map_size[0]) for j in range(map_size[1])]
    # print(all_positions)
    # 去除起始点
    all_positions.remove(start)
    all_positions.remove(end)
    # 从所有可能的位置中随机选择一些位置作为障碍物的位置
    obstacles = random.sample(all_positions, num_obstacles)
    # print(obstacles)
    return obstacles


# 随机生成障碍物位置列表
obstacles = generate_obstacles(map_size)

# 定义屏幕一个格子大小
CELL_SIZE = 20
# 定义屏幕宽高大小
WIDTH, HEIGHT = map_size[0] * CELL_SIZE, map_size[1] * CELL_SIZE

# 定义颜色
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
BLACK = (0, 0, 0)


# 初始化网格地图
def grid_init(map_size, obstacles):
    # 创建网格地图
    grid = [[0 for x in range(map_size[0])] for y in range(map_size[1])]
    for x, y in obstacles:
        # 设置障碍物
        grid[x][y] = 1
    return grid


# 打印网格地图
def grid_print(grid):
    for line in grid:
        print(line)


# 绘制主地图
def draw_grid(pygame, screen):
    for i in range(0, WIDTH, CELL_SIZE):
        pygame.draw.line(screen, BLACK, (i, 0), (i, HEIGHT))
    for i in range(0, HEIGHT, CELL_SIZE):
        pygame.draw.line(screen, BLACK, (0, i), (WIDTH, i))
    for obs in obstacles:
        # 设置矩形的位置和大小
        rect_pos = (obs[1] * CELL_SIZE, obs[0] * CELL_SIZE)
        rect_size = (CELL_SIZE, CELL_SIZE)
        # 使用pygame.draw.rect绘制矩形
        pygame.draw.rect(screen, BLUE, (rect_pos[0], rect_pos[1], rect_size[0], rect_size[1]))


# 绘制A*算法找到的路径
def draw_a_star_path():
    print("绘制A*算法找到的路径地图:")
    # 初始化 Pygame
    pygame.init()

    # 创建一个窗口(屏幕)对象
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    # 窗口描述
    pygame.display.set_caption("A星算法动画演示")
    # 定义字体
    font = pygame.font.Font(None, 30)
    # 填充屏幕背景为白色
    screen.fill(WHITE)

    # 绘制地图
    draw_grid(pygame, screen)
    # 更新显示屏幕
    pygame.display.flip()

    # 初始化网格地图
    grid = grid_init(map_size, obstacles)
    print("初始化网格地图:")
    # 打印网格地图
    grid_print(grid)
    # 设置起点和终点
    # start = (0, 0)
    # end = (map_size[0] - 1, map_size[1] - 1)
    # 执行A*算法
    path = a_star_path(np.array(grid), start, end)
    print("最优路径为:")
    print(path)
    # 循环刷新地图,显示最优路径
    for node in path:
        # 设置矩形的位置和大小
        rect_pos = (node[1] * CELL_SIZE, node[0] * CELL_SIZE)
        rect_size = (CELL_SIZE, CELL_SIZE)
        # 使用pygame.draw.rect绘制矩形
        pygame.draw.rect(screen, RED, (rect_pos[0], rect_pos[1], rect_size[0], rect_size[1]))
        # 更新显示屏幕
        pygame.display.flip()
        # 间隔0.5秒刷新地图
        time.sleep(0.5)
    # 退出 Pygame
    pygame.quit()


if __name__ == "__main__":
    draw_a_star_path()

五、A*算法寻找最优路径-运行效果

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

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

相关文章

DDA 算法

CAD 算法是计算机辅助设计的算法&#xff0c;几何算法是解决几何问题的算法 CAD 算法是指在计算机辅助设计软件中使用的算法&#xff0c;用于实现各种设计和绘图功能&#xff0c;CAD 广泛应用于建筑、机械、电子等领域&#xff0c;可以大大提高设计效率和精度 绘图算法是 CAD…

关于多重背包的笔记

多重背包可以看作01背包的拓展&#xff0c; 01背包是选或者不选。多重背包是选0个一直到选s个。 for (int i 1; i < n; i) {for (int j m; j > w[i]; --j){f[j] max(f[j], f[j - 1*w[i]] 1*v[i], f[j - 2*w[i]] 2*v[i],...f[j - s*w[i]] s*v[i]);} } 由上述伪代码…

FL Studio2024mac电脑版本下载步骤教程

FL Studio2024是款专业的音频录制编辑软件&#xff0c;可以针对作曲者的要求编辑出不同音律的节奏&#xff0c;例如鼓、镲、锣、钢琴、笛、大提琴等等任何乐器的节奏律动。FL Studio目前在中国已经受到广大制作人喜爱&#xff0c;使用它制作的音乐作品也已经数不胜数&#xff0…

Leetcode的AC指南 —— 链表:24. 两两交换链表中的节点

摘要&#xff1a; Leetcode的AC指南 —— 链表&#xff1a;24. 两两交换链表中的节点。题目介绍&#xff1a;给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能…

计算机服务器中了360后缀勒索病毒怎么处理,勒索病毒解密数据恢复

网络技术的不断发展与应用&#xff0c;越来越多的企业开始走向数字化办公模式&#xff0c;极大地方便了企业的生产运营。但随之而来的网络安全威胁也在不断增加&#xff0c;在本月&#xff0c;云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的计算机服务器遭到了360后…

文心一言 VS 讯飞星火 VS chatgpt (158)-- 算法导论12.3 5题

五、用go语言&#xff0c;假设为每个结点换一种设计&#xff0c;属性 x.p 指向 x 的双亲&#xff0c;属性 x.succ 指向 x 的后继。试给出使用这种表示法的二叉搜索树 T 上 SEARCH、INSERT 和DELETE 操作的伪代码。这些伪代码应在 O(h) 时间内执行完&#xff0c;其中 h 为树 T 的…

函数式编程 h函数

<template><div><table border><tr><th>id</th><th>name</th><th>age</th><th>操作</th></tr><tr v-for"item in list" :key"item.id"><td>{{ item.id }}<…

wsl kafka的简单应用

安装并配置单机版kafka所需环境 wsl2 环境可用性较高&#xff0c;如下介绍在该环境中安装单机版本kafka的详细过程。 启动命令行工具启动wsl&#xff1a;wsl --user root --cd ~&#xff0c;&#xff08;以root用户启动&#xff0c;进入wsl后当前路径为~“用户主目录”&#…

3.3【窗口】窗口的几何形状(二,窗口属性)

写在前面 应用程序使用窗口来显示内容。一些属性决定了窗口及其内容的大小和位置。其他属性决定了窗口内容的外观和解释。 了解窗口属性引用的两个坐标系非常重要。如果你知道你正在使用的坐标系,那么为你的窗口属性选择设置值会容易得多,并且会更有意义。 一,显示相关属…

SpringBoot零基础入门到项目实战——学习路线规划与目录结构

文章目录 第一部分&#xff1a;Spring Boot基础第二部分&#xff1a;Web开发与RESTful API第三部分&#xff1a;数据访问与持久化第四部分&#xff1a;安全与身份验证第五部分&#xff1a;高级主题第六部分&#xff1a;测试总结与扩展实战项目练习 &#x1f389;欢迎来到Spring…

Storage engine MyISAM is disabled (Table creation is disallowed)

如何解决Storage engine MyISAM is disabled (Table creation is disallowed&#xff09; 在开发中&#xff0c;需要把mysql5.7的数据库&#xff0c;迁移到mysql8.0 的阿里云数据库上 把Mysql5.7的数据导入到8.0时&#xff0c;出现 解决方法 1、使用指令找出那些表是MyISAM引擎…

求解最大子段和问题

求解最大子段和问题。 对于给定序列a1,a2,a3……an,寻找它的某个连续子段&#xff0c;使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。 要求&#xff1a;分别用教材所给的三种方法求解&#xff08;简单方法、分治法、动态规划&#xff09;&#xff0…

【Redis】AOF 基础

因为 Redis AOF 的实现有些绕, 就分成 2 篇进行分析, 本篇主要是介绍一下 AOF 的一些特性和依赖的其他函数的逻辑,为下一篇 (Redis AOF 源码) 源码分析做一些铺垫。 AOF 全称: Append Only File, 是 Redis 提供了一种数据保存模式, Redis 默认不开启。 AOF 采用日志的形式来记…

指针必刷题(C语言指针就该这么学)【数据结构基础】【C语言指针必刷题】

前言&#xff1a;必备知识回忆 1.数组名的意义 i.sizeof(数组名&#xff09;&#xff0c;这里的数组名表示整个数组&#xff0c;计算的是整个数组的大小 ii.&数组名&#xff0c;这里的数组名表示整个数组&#xff0c;取出的是整个数组的地址 iii.除此之外&#xff0c;所…

基于urllib库的网页数据爬取

实验名称&#xff1a; 基于urllib库的网页数据爬取 实验目的及要求&#xff1a; 【实验目的】 通过本实验了解和掌握urllib库。 【实验要求】 1. 使用urllib库爬取百度搜索页面。 2. 使用urllib库获取百度搜索的关键字搜索结果&#xff08;关键字任选&#xff09;。 实验原理及…

电子元器件介绍——二极管(四)

电子元器件介绍 文章目录 电子元器件介绍前言一、二极管的基础知识二、二极管的分类三、二极管的应用总结 前言 这一节我们看一下二极管。 一、二极管的基础知识 PN结&#xff1a;是指一块半导体单晶&#xff0c;其中一部分是P型区&#xff0c;其余部分是N型区。 在电场作用…

C++ Qt开发:自定义Dialog对话框组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍自定义Dialog组件的常用方法及灵活运用。 在…

基于linux系统的Tomcat+Mysql+Jdk环境搭建(四)linux安装Mysql

1.切换到你需要安装mysql的路径 cd /root/usr/ 2.在线安装 安装网上的安装方式都有很多&#xff0c;可以自己百度一下 我们这里是自己搭建测试环境&#xff0c;可以直接选择在线安装&#xff0c;命令如下&#xff1a;yum install mysql-server&#xff0c; 但是我失败了 ┭┮…

在排序数组中查找元素的第一个和最后一个位置(Java详解)

一、题目描述 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示…

verilog语法进阶,时钟原语

概述&#xff1a; 内容 1. 时钟缓冲 2. 输入时钟缓冲 3. ODDR2作为输出时钟缓冲 1. 输入时钟缓冲 BUFGP verilog c代码&#xff0c;clk作为触发器的边沿触发&#xff0c;会自动将clk综合成时钟信号。 module primitive1(input clk,input a,output reg y); always (posed…