重温数据结构与算法之A star 算法

news2024/11/25 16:36:22

文章目录

  • 前言
  • 一、原理
    • 1.1 网格距离
    • 1.2 宽度优先搜索
    • 1.3 Dijkstra 算法
    • 1.4 最佳优先搜索
    • 1.5 A*算法
  • 二、代码实现
    • 2.1 伪码
    • 2.2 python 实现
    • 2.3 可视化
  • 三、优缺点分析
    • 3.1 优点
    • 3.2 缺点
  • 参考

前言

A*(A-Star)算法是一种静态路网中求解最短路径有效的直接搜索方法,也是解决许多搜索问题的有效算法。

A*算法属于启发式搜索算法,它结合了最佳优先(Best-First)搜索Dijkstra算法的优点,能够快速地在图中找到一条从起点到终点的最短路径。自从1968年由 Peter Hart, Nils Nilsson 和 Bertram Raphael 提出以来,A* 算法已经成为了许多领域中最常用的路径规划算法之一。

  • 最佳优先搜索通过使用启发函数来评估每个节点的优先级,从而快速地找到目标节点。
  • Dijkstra算法则通过记录每个节点到起点的距离来保证找到的路径是最短的。

A* 算法结合了这两种方法,它使用一个启发函数来评估每个节点到目标节点的距离 h ( n ) h(n) h(n),并将这个值与该节点到起点的距离 g ( n ) g(n) g(n)相加,从而得到一个总估计值 f ( n ) = g ( n ) + h ( n ) f(n) = g(n) + h(n) f(n)=g(n)+h(n)。A*算法总是选择具有最小总估计值的节点进行扩展,因此能够快速地找到一条从起点到终点的最短路径。

此外,在游戏开发、人工智能和运筹学等领域中都有着广泛应用。

一、原理

1.1 网格距离

根据网格的拓扑结构,可选用以下三种距离,假设A与B两点横纵坐标距离分别为x 和 y:

  1. 曼哈顿距离,只允许4个方向移动,AB的距离为: x + y x + y x+y

  2. 对角距离,允许8方向移动,AB的距离为: x + y + ( 2 − 2 ) ∗ m i n ( x , y ) x + y + (\sqrt{2}-2)*min(x, y) x+y+(2 2)min(x,y)

  3. 欧几里得距离,允许任意方向移动,AB的距离为: x 2 + y 2 \sqrt{x^2+y^2} x2+y2

下面默认曼哈顿距离

1.2 宽度优先搜索

宽度优先搜索(Breadth First Search,简称 BFS),在我之前一篇博客中就有介绍,这是最基本的求最短路径的算法。BFS 相当于暴力搜索,这个算法在网格中搜索有2个较大的缺陷:

  1. 格子之间的移动花费是均等的,实际应用中不同格子之间移动代价会不一样。
  2. 它是均等的向四周搜索,即使你肉眼看到目标就在右边,但也会均匀向四周扩撒。

广度优先搜索0.gif

当碰到障碍时

广度优先搜索1.gif

1.3 Dijkstra 算法

Dijkstra 算法是由荷兰计算机科学家狄克斯特拉 于1959年提出的算法。它是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。Dijkstra 算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

例如在游戏《文明》中,走平地或沙漠,花费1个移动点,但是穿越森林或山丘可能需要花费5个移动点。

bfs_dijkstra.gif

1.4 最佳优先搜索

最佳优先搜索算法是一种启发式搜索算法,它使用一个启发函数,会优先选择离终点距离更近的节点。

下图表示当网格间权重都相等时,Dijkstra 算法会退化为宽度优先搜索,搜索时间更长,搜索格子更多

dijkstra_bfs.gif

1.5 A*算法

上述最佳优先搜索并不一定是最短的,例如在下面场景下选择的就不是最短路径

dijkstra_vs_bfs.gif

这时候结合Dijkstra 算法 和最佳优先搜索算法的A* 就上场了,它结合了这两者的优点,它选择两个值相加(已走距离+预估距离)作为节点的优先级。

a_star_compare.png

二、代码实现

2.1 伪码

frontier = PriorityQueue()
frontier.put(start, 0)
came_from = dict()
came_from[start] = None

while not frontier.empty():
   current = frontier.get()

   if current == goal:
      break
   
   for next in graph.neighbors(current):
      if next not in came_from:
         priority = heuristic(goal, next)
         frontier.put(next, priority)
         came_from[next] = current

2.2 python 实现

import numpy as np
import heapq
from collections import namedtuple
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties 
font_set = FontProperties(fname=r"c:\\windows\\fonts\\simsun.ttc", size=15)#导入宋体字体文件

# 15*15网格,0表示可以通过的点,而1表示障碍物
grid = np.array([[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, 0, 0,0, 0, 0],
                 [0, 0, 1, 1, 1, 1,1, 1, 1, 1, 1, 1,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0,1, 0, 0],
                 [0, 0, 1, 1, 1, 1,1, 1, 1, 1, 1, 1,1, 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, 0, 0, 0, 0,0, 0, 0]])

Node = namedtuple('Node', ['x', 'y'])
dir = [Node(0, 1), Node(0, -1), Node(1, 0), Node(-1, 0)]
# dir = [Node(0, 1), Node(0, -1), Node(1, 0), Node(-1, 0),
#        Node(1, 1), Node(-1, -1), Node(1, -1), Node(-1, 1) ]
start = Node(12, 0)
end = Node(2, 14)


def distance(node1, node2):
    x = abs(node1.x - node2.x)
    y = abs(node1.y - node2.y)
    return x + y
    # return x + y + (2**0.5 - 2) * min(x, y)


def far_cost(node1, node2):
    return distance(node1, node2)


def a_star(grid, start, end):
    if grid[start.x][start.y] == 1 or grid[end.x][end.y] == 1:
        print("起/终点在障碍物上")
        return []
    rows, cols = grid.shape
    # open_set 是一个优先队列,用于存储待扩展的节点。每次从 open_set 中取出代价最小的节点进行扩展。
    open_set = []
    # cost 是一个字典,用于存储从起点到每个节点的代价。
    cost = {}
    cost[start] = 0
    # path 是一个字典,存储最终路径每个节点的前一个节点
    path = {}
    path[start] = None
    heapq.heappush(open_set, (0, start))

    while (open_set):
        current = heapq.heappop(open_set)[1]
        if (current == end):
            break
        for p in dir:
            newpx = current.x + p.x
            newpy = current.y + p.y
            if newpx < 0 or newpx >= rows or newpy < 0 or newpy >= cols or grid[newpx][newpy] == 1:
                continue
            newNode = Node(newpx, newpy)
            newCost = cost[current] + far_cost(current, newNode)
            
            if not newNode in cost or newCost < cost[newNode]:
                cost[newNode] = newCost
                priority = newCost + distance(newNode, end)
                heapq.heappush(open_set, (priority, newNode))
                path[newNode] = current
    if end not in path:
        print("未能到达结束点")
        return []
    shortest_path = []
    current = end
    while current != None:
        shortest_path.append(current)
        current = path[current]
    shortest_path.reverse()
    return shortest_path
nodes = a_star(grid, start ,end)
print(nodes)

使用上述代码会打印出路径所经过的节点,如下所示,但这样不太很直观查看路径,需要可视化数据

[Node(x=12, y=0), Node(x=11, y=0), Node(x=10, y=0), Node(x=9, y=0), Node(x=8, y=0), Node(x=7, y=0), Node(x=6, y=0), Node(x=5, y=0), Node(x=4, y=0), Node(x=3, y=0), Node(x=2, y=0), Node(x=2, y=1), Node(x=1, y=1), Node(x=1, y=2), Node(x=1, y=3), Node(x=1, y=4), Node(x=1, y=5), Node(x=1, y=6), Node(x=1, y=7), Node(x=1, y=8), Node(x=1, y=9), Node(x=1, y=10), Node(x=1, y=11), Node(x=1, y=12), Node(x=1, y=13), Node(x=1, y=14), Node(x=2, y=14)]

2.3 可视化

可以使用matplotlib对网格数据进行可视化

def draw(nodes):
    fig, ax = plt.subplots()
    im = ax.imshow(grid)
    rows, cols = grid.shape
  
    ax.set_xticks(np.arange(rows))
    ax.set_yticks(np.arange(cols))
 
    plt.setp(ax.get_xticklabels(), rotation_mode="anchor")

    for n in nodes:
        text = ax.text(n.y, n.x, '2', ha="center", va="center", color="blue")
    # bgColor = ['black', 'gray']
    for i in range(rows):
        for j in range(cols):
            if Node(i, j) not in nodes:
                text = ax.text(j, i, grid[i][j],
                               ha="center", va="center", color="white")#, backgroundcolor=bgColor[grid[i][j]])
    ax.set_title('A*算法', fontproperties = font_set)
    fig.tight_layout()
    plt.show()
nodes = a_star(grid, start ,end)
draw(nodes)

a_star_0.png

上述是4方向曼哈顿距离,可简单修改代码实现八方向对角距离

a_star_1.png

三、优缺点分析

3.1 优点

  1. 相对于需要将所有节点展开搜寻的算法,A* 算法最大的优点就是引入了启发信息作为向目标点移动的决策辅助,所以不再需要遍历整个地图,降低了计算复杂度,减少了时间损耗少。

  2. A *算法得到的是最优解是可以被理论证明的。如果得到的不是最优解,那么就是实现的时候出了什么问题,或者是没有完全按照 A * 的定义去实现

3.2 缺点

  1. 实时性差,随着节点数的增多,算法搜索效率降低

  2. 在不存在解的情况下(比如在寻路问题中,根本不存在一条通达的路),采用 A* 算法求解,势必会穷举所有的可能。

  3. A*算法需要使用一个启发函数来估算未知节点的成本。如果启发函数不准确,可能会导致找到的路径不是最短的。

参考

  1. Introduction to the A* Algorithm
  2. A*算法详解 一看就会 手把手推导 完整代码注释
  3. Python A*算法的简单实现
  4. A*算法
  5. A* 算法之误区
  6. 自动驾驶路径规划:A*(Astar)算法

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

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

相关文章

uniapp(三) 之 表单提交

更换UI库 经过我昨天仔细看了下ThorUI&#xff0c;里面有个会员组件&#xff0c;好像有用的组件都是会员组件一样&#xff0c;作为一个白嫖怪&#xff0c;我决定今天再换一个UI库 引入后&#xff0c;根据组件位置自行调整位置 好了现在使用UNI-UI了 但是我的语法是vue3,官网范…

Hadoop数据仓库的主要特征有哪些?

数据仓库(英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW),是一个用于存储、分析、报告的数据系统。数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;分析结果为企业提供决策支持(Decision Support)。 数据仓库本身并不“生产”任何数据&#xff0c;其数据…

Nebula分布式集群

2022年9月15日18:47:38文章目录 Nebula1.安装:2.数据模型3.NebulaGraph 架构总览4.基本命令文档4.0 数据类型4.1 spaces图空间CREATE SPACEDROP SPACECLEAR SPACESHOW SPACESDESC SPACE 4.2 Tag4.3 edge4.4 点语句INSERT VERTEXDELETE VERTEXUPDATE VERTEXUPSERT VERTEX 4.5 边…

day11 -- 存储过程+触发器+事物处理

学习内容 什么是存储过程 如何使用存储过程 学习记录 存储过程 经常会有一个完整的操作需要多条语句才能完成。 此外&#xff0c;需要执行的具体语句及其次序也不是固定的&#xff0c;它们可能会根据条件而选择性的执行。 那应该怎么办呢&#xff1f;可以创建存储过程。 存储…

工业镜头分类、相关参数含义

一、工业镜头参数 1、焦距/后焦距 焦距是像方主面到像方焦点的距离。后焦距指光线离开镜头最后一片镜片表面到sensor感光面的距离&#xff0c;如8mm&#xff0c;16mm&#xff0c;25mm等&#xff1b; 焦距的大小决定着视角大小&#xff0c;焦距数值小&#xff0c;视角大&#…

4个月完成职位申请并CSC改派出国|新加坡南洋理工大学访学申请记

由于原访学国家签证被拒&#xff0c;O老师期望能申请手续便捷且容易通过签证的国家&#xff0c;最终我们成功申请到世界名校新加坡南洋理工大学的国家教育研究学院。从获得邀请函、办理CSC改派及派出、顺利签证直至出国等全套手续&#xff0c;仅仅4个月。 O老师背景&#xff1a…

腾讯应用宝 - 微下载

首次接触微下载这个概念&#xff0c;故简单记录一下 产品&#xff1a;微下载配置好了吗&#xff1f; Me&#xff1a; 嗯&#xff1f;什么微下载&#xff1f; 基础认知微下载是什么&#xff1f;微下载在哪里使用&#xff1f;微下载链接获取方式&#xff1f;个性化功能&#xff…

搜索引擎召回策略总结

一、搜索引擎召回策略的方法和注意事项(自己能想到的&待补充) 二、相关资料 同义变换在百度搜索广告中的应用 https://mp.weixin.qq.com/s/ybkbU8p_3jgKuCGdNWeG8w 2020年kdd Facebook搜索向量召回读后感【小红书MXie】 https://zhuanlan.zhihu.com/p/184920498 美团搜…

一键安装 HomebrewCN

一键安装 HomebrewCN Brew介绍Homebrew 能干什么?Homebrew自身如何使用安装Homebrew国内源安装 Homebrew(github源) Brew介绍 macOS 和 Linux 缺失软件包的管理器 Homebrew 能干什么? 使用 Homebrew 安装 Mac&#xff08;或Linux&#xff09;没有预装但你需要的东西。 Ho…

python 第三章 基础语句

系列文章目录 第一章 初识python 第二章 变量 文章目录 3.1 输出格式化输出格式化符号格式化字符串扩展f-格式化字符串转义字符结束符 3.2 输入3.3 数据类型转换转换数据类型的函数 3.4 PyCharm交互式开发3.5 运算符运算符的分类算数运算符赋值运算符复合赋值运算符比较运算符逻…

java设计模式之:工厂模式详解(简单工厂+工厂方法+抽象工厂)

文章目录 简单工厂实现应用场景 工厂方法实现适用场景缺点 抽象工厂实现缺点 在面向对象编程中&#xff0c;创建对象实例最常用的方式就是通过 new 操作符构造一个对象实例&#xff0c;但在某些情况下&#xff0c;new 操作符直接生成对象会存在一些问题。举例来说&#xff0c;对…

软件测试2023年行情怎么样?仔细讲解!

目录 前言&#xff1a; 普通功能测试人员不建议跳槽 还有一个要求就是要对业务的极致理解 那么产业互联网趋势会导致什么呢&#xff1f; 现在跳槽涨薪需要掌握到什么样的技术呢&#xff1f; 给大家一些跳槽建议 前言&#xff1a; 软件测试是为了发现程序中的错误而执行程序的…

Keil5新建工程

STM32新建工程 1、基于寄存器、基于库函数、基于HAL2、基于标准库的工程3、工程架构4、基于库函数点灯实验 1、基于寄存器、基于库函数、基于HAL 1、基于寄存器&#xff1a;与51单片机开发案方式一样&#xff0c;是用程序直接配置寄存器&#xff0c;来达到我们想要的功能&…

基于NE555芯片的简单延时电路和方波信号发生器

简单延时电路 NE555芯片是一种经典的计时器集成电路&#xff0c;常用于电子设计中的定时和延时功能。下面是一个简单的NE555延时电路的详细分析和讲解&#xff1a; NE555芯片是一个多功能的集成电路&#xff0c;主要由比较器、RS触发器、RS锁存器以及输出驱动器等组成。它可以工…

CLIP对比语言-图像预训练算法

本文参考&#xff1a;AIGC神器CLIP&#xff1a;技术详解及应用示例_Baihai IDP的博客-CSDN博客 一、CLIP概述 CLIP&#xff1a;Constastive Language-Image Pretraining。它是一个开源的、多模式的、zero-shot的模型。如果提供一张图像和一段文本描述&#xff0c;该模型可以预…

requestAnimationFrame() 方法

[TOC](requestAnimationFrame() 方法) 一、基本使用 1.基本介绍 window.requestAnimationFrame() 主要是用来实现动画的时候使用的&#xff0c;不管是移动动画还是数字增长动画&#xff0c;使用这个api可以让你的动画看起来非常平滑&#xff0c;因为它是要求浏览器在下次重绘…

【C++ 基础篇:22】:类的 const 对象 与 const 成员函数/方法 以及 类中涉及 const 的常见问题!

本系列 C 相关文章 仅为笔者学习笔记记录&#xff0c;用自己的理解记录学习&#xff01;C 学习系列将分为三个阶段&#xff1a;基础篇、STL 篇、高阶数据结构与算法篇&#xff0c;相关重点内容如下&#xff1a; 基础篇&#xff1a;类与对象&#xff08;涉及C的三大特性等&#…

基于html+css的图展示111

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

软件测试基础教程学习2

文章目录 软件测试基础2.1 软件测试模型2.2 确认和验证2.3 软件测试分类2.4软件测试流程概述 软件测试基础 2.1 软件测试模型 2.2 确认和验证 软件确认&#xff08;Validation&#xff09;和验证&#xff08;Verification&#xff09;&#xff0c;简称V&V或V2。 确认是指…

python基于yolov7开发构建手写甲骨文检测识别系统

在我之前的文章中&#xff0c;关于手写文字、手写数字、手写字母的检测识别相关的项目都有了不少的实践了&#xff0c;这里就不在赘述了&#xff0c;感兴趣的话可以自行移步阅读即可。 《基于轻量级目标检测模型实现手写汉字检测识别计数》 《python开发构建基于机器学习模型…