最佳路径优先搜索算法

本来想直接写A* 的,不过看完最佳路径优先搜索算法后觉得还是要先理解一下这个算法后才能更好的理解A* 算法,所以把这篇文章放到A* 前面。

基本概念

最佳优先搜索算法(Best-first-searching)是一种启发式搜索算法(Heuristic Algorithm),其基于广度优先搜索算法,不同点是其依赖于估价函数对将要遍历的节点进行估价,选择代价小的节点进行遍历,直到找到目标点为止。BFS算法不能保证找到的路径是一条最短路径,但是其计算过程相对于Dijkstra算法会快很多。

BFS算法的启发估价函数公式为:

f ( n ) = h ( n ) f(n) = h(n) f(n)=h(n)

n表示当前的点,g(n)为从起始点到点n的实际代价,h(n)为从点n到目标点的估价。

从上述公式我们可以看出,BFS的搜索方式非常简单粗暴,直接从起点开始连接终点,对线上的点进行搜索,遇到障碍物时向四周展开。在没有障碍物的场景下,BFS是一种快速有效的路径搜索方式:
在这里插入图片描述
但是在大部分情况下,我们的场景中肯定会存在不同的障碍物,此时它的搜索路径就不再是最优的了:
在这里插入图片描述

代码实现

import os
import sys
from collections import deque
import matplotlib.pyplot as pltimport math
import heapqclass BFS:"""BFS add the new visited node in the end of the openset"""def __init__(self, s_start, s_goal, heuristic_type,xI, xG):self.s_start = s_startself.s_goal = s_goalself.heuristic_type = heuristic_typeself.u_set = [(-1, 0), (-1, 1), (0, 1), (1, 1),(1, 0), (1, -1), (0, -1), (-1, -1)]  # feasible input setself.obs = self.obs_map()  # position of obstaclesself.OPEN = []  # priority queue / OPEN setself.CLOSED = []  # CLOSED set / VISITED orderself.PARENT = dict()  # recorded parentself.g = dict()  # cost to comeself.x_range = 51  # size of backgroundself.y_range = 31self.xI, self.xG = xI, xGself.obs = self.obs_map()def update_obs(self, obs):self.obs = obsdef obs_map(self):"""Initialize obstacles' positions:return: map of obstacles"""x = 51y = 31obs = set()for i in range(x):obs.add((i, 0))for i in range(x):obs.add((i, y - 1))for i in range(y):obs.add((0, i))for i in range(y):obs.add((x - 1, i))for i in range(10, 21):obs.add((i, 15))for i in range(15):obs.add((20, i))for i in range(15, 30):obs.add((30, i))for i in range(16):obs.add((40, i))return obsdef update_obs(self, obs):self.obs = obsdef animation(self, path, visited, name):self.plot_grid(name)self.plot_visited(visited)self.plot_path(path)plt.show()def plot_grid(self, name):obs_x = [x[0] for x in self.obs]obs_y = [x[1] for x in self.obs]plt.plot(self.xI[0], self.xI[1], "bs")plt.plot(self.xG[0], self.xG[1], "gs")plt.plot(obs_x, obs_y, "sk")plt.title(name)plt.axis("equal")def plot_visited(self, visited, cl='gray'):if self.xI in visited:visited.remove(self.xI)if self.xG in visited:visited.remove(self.xG)count = 0for x in visited:count += 1plt.plot(x[0], x[1], color=cl, marker='o')plt.gcf().canvas.mpl_connect('key_release_event',lambda event: [exit(0) if event.key == 'escape' else None])if count < len(visited) / 3:length = 20elif count < len(visited) * 2 / 3:length = 30else:length = 40## length = 15if count % length == 0:plt.pause(0.001)plt.pause(0.01)def plot_path(self, path, cl='r', flag=False):path_x = [path[i][0] for i in range(len(path))]path_y = [path[i][1] for i in range(len(path))]if not flag:plt.plot(path_x, path_y, linewidth='3', color='r')else:plt.plot(path_x, path_y, linewidth='3', color=cl)plt.plot(self.xI[0], self.xI[1], "bs")plt.plot(self.xG[0], self.xG[1], "gs")plt.pause(0.01)def searching(self):"""Breadth-first Searching.:return: path, visited order"""self.PARENT[self.s_start] = self.s_startself.g[self.s_start] = 0self.g[self.s_goal] = math.infheapq.heappush(self.OPEN,(self.heuristic(self.s_start), self.s_start))while self.OPEN:_, s = heapq.heappop(self.OPEN)self.CLOSED.append(s)if s == self.s_goal:breakfor s_n in self.get_neighbor(s):new_cost = self.g[s] + self.cost(s, s_n)if s_n not in self.g:self.g[s_n] = math.infif new_cost < self.g[s_n]:  # conditions for updating Costself.g[s_n] = new_costself.PARENT[s_n] = s# best first set the heuristics as the priority heapq.heappush(self.OPEN, (self.heuristic(s_n), s_n))return self.extract_path(self.PARENT), self.CLOSEDdef get_neighbor(self, s):"""find neighbors of state s that not in obstacles.:param s: state:return: neighbors"""return [(s[0] + u[0], s[1] + u[1]) for u in self.u_set]def cost(self, s_start, s_goal):"""Calculate Cost for this motion:param s_start: starting node:param s_goal: end node:return:  Cost for this motion:note: Cost function could be more complicate!"""if self.is_collision(s_start, s_goal):return math.infreturn math.hypot(s_goal[0] - s_start[0], s_goal[1] - s_start[1])def is_collision(self, s_start, s_end):"""check if the line segment (s_start, s_end) is collision.:param s_start: start node:param s_end: end node:return: True: is collision / False: not collision"""if s_start in self.obs or s_end in self.obs:return Trueif s_start[0] != s_end[0] and s_start[1] != s_end[1]:if s_end[0] - s_start[0] == s_start[1] - s_end[1]:s1 = (min(s_start[0], s_end[0]), min(s_start[1], s_end[1]))s2 = (max(s_start[0], s_end[0]), max(s_start[1], s_end[1]))else:s1 = (min(s_start[0], s_end[0]), max(s_start[1], s_end[1]))s2 = (max(s_start[0], s_end[0]), min(s_start[1], s_end[1]))if s1 in self.obs or s2 in self.obs:return Truereturn Falsedef extract_path(self, PARENT):"""Extract the path based on the PARENT set.:return: The planning path"""path = [self.s_goal]s = self.s_goalwhile True:s = PARENT[s]path.append(s)if s == self.s_start:breakreturn list(path)def heuristic(self, s):"""Calculate heuristic.:param s: current node (state):return: heuristic function value"""heuristic_type = self.heuristic_type  # heuristic typegoal = self.s_goal  # goal nodeif heuristic_type == "manhattan":return abs(goal[0] - s[0]) + abs(goal[1] - s[1])else:#sqrt(x^2+y^2)return math.hypot(goal[0] - s[0], goal[1] - s[1])def main():s_start = (5, 5)s_goal = (45, 25)bfs = BFS(s_start, s_goal, 'None',s_start,s_goal)path, visited = bfs.searching()bfs.animation(path, visited, "Breadth-first Searching (BFS)")if __name__ == '__main__':main()

简单讲解一下原理(跟A*那篇类似):
简单解析一下:

在获取起点后,算法维护了两个字典:

        self.PARENT[self.s_start] = self.s_startself.g[self.s_start] = 0self.g[self.s_goal] = math.inf

PARENT字典中存取的是这个点由哪个点延伸出来的,即它的上一个节点是谁,用于最后的路径的返回;g这个字典中存储的是每个坐标的代价值,即g[s]。

然后,算法通过堆栈的概念维护了一个堆栈:

        heapq.heappush(self.OPEN,(self.heuristic(self.s_start), self.s_start))

将初始值的f[s]存储在这里,然后进行while循环:

首先从堆栈中取出栈顶元素:

_, s = heapq.heappop(self.OPEN)

注意到这里使用的heapq堆栈功能中的两个函数heapq.heappop与heapq.heappush。heappop会取出栈顶元素并将原始数据从堆栈中删除,而heappush则是对插入的数据按大小排序并存储在堆栈中。所以每一个遍历的点都会按照它的代价值放入堆栈中,同时每次取出的都是代价值最小的那个。

然后判断出栈顶元素是否为目标点,如果为目标点,则退出:

            if s == self.s_goal:  # stop conditionbreak

如果不是,则更新该点附近点的代价值:

            for s_n in self.get_neighbor(s):new_cost = self.g[s] + self.cost(s, s_n)if s_n not in self.g:self.g[s_n] = math.infif new_cost < self.g[s_n]:  # conditions for updating Costself.g[s_n] = new_costself.PARENT[s_n] = s# best first set the heuristics as the priority heapq.heappush(self.OPEN, (self.heuristic(s_n), s_n))

get_neighbor为获取该点周围的点的坐标。heappush入栈时需要存储的该点的代价值的计算方式为:

     def heuristic(self, s):"""Calculate heuristic.:param s: current node (state):return: heuristic function value"""heuristic_type = self.heuristic_type  # heuristic typegoal = self.s_goal  # goal nodeif heuristic_type == "manhattan":return abs(goal[0] - s[0]) + abs(goal[1] - s[1])else:#sqrt(x^2+y^2)return math.hypot(goal[0] - s[0], goal[1] - s[1])

这里计算了一个启发函数的代价值h(n),h的计算可以根据实际情况进行选择,例如在栅格地图中,计算h的方式一般可以分为两种:曼哈顿距离与欧几里德距离。

另外这里注意一个非常巧妙的问题,对于同一个点,如果它在计算其左边的点的时候会将其添加到堆栈中,计算其本身时会出栈,再计算其右边时是否还需要重新入栈呢?

这个是不一定的,是否重新入栈的关键取决于该点在右边的点的计算代价值是否小于左边:

if new_cost < self.g[s_n]:

如果代价值比原来的小,则会重新入栈,否则,这个点就不需要重新计算,这样子就避免了大量的重复计算以及死循环的问题。

最后得到结果如下:
在这里插入图片描述
从结果可以看出,虽然最佳路径优先搜索算法得到的结果不是最优的,但是在搜索过程中确实效率还是非常高的。

参考:

1、最佳优先搜索和A*搜索算法

2、路径规划算法

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

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

相关文章

Python实现GA遗传算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;最早是由美国的 John holland于20世…

Docker从零到掌握(详解)

目录 1.初识Docker 1.1 为什么使用docker 1.2 Docker技术 1.3.安装Docker 1.4.Docker架构 1.5.配置Docker镜像加速器 2.Docker常用命令 2.1.Docker服务相关的命令 2.2.Docker镜像相关的命令 2.3.Docker容器相关的命令 3. 容器的数据卷 3.1.数据卷的概念和作用 3.2…

Idea添加mybatis的mapper文件模版

针对Java开发人员&#xff0c;各种框架的配置模版的确是需要随时保留一份&#xff0c;在使用的时候&#xff0c;方便复制粘贴&#xff0c;但是也依然不方便&#xff0c;我们可以给开发工具&#xff08;IDE&#xff09;中添加配置模版&#xff0c;这里我介绍下使用idea开发工具&…

Python 中的机器学习简介:多项式回归

一、说明 多项式回归可以识别自变量和因变量之间的非线性关系。本文是关于回归、梯度下降和 MSE 系列文章的第三篇。前面的文章介绍了简单线性回归、回归的正态方程和多元线性回归。 二、多项式回归 多项式回归用于最适合曲线拟合的复杂数据。它可以被视为多元线性回归的子集。…

uniapp返回

// 监听返回事件onNavigationBarButtonTap() {uni.showModal({title: 提示,content: 确定要返回吗&#xff1f;,success: (res) > {if (res.confirm) {uni.navigateBack({delta: 2})}}})},

Opencv-C++笔记 (16) : 几何变换 (图像的翻转(镜像),平移,旋转,仿射,透视变换)

文章目录 一、图像平移二、图像旋转2.1 求旋转矩阵2.2 求旋转后图像的尺寸2.3手工实现图像旋转2.4 opencv函数实现图像旋转 三、图像翻转3.1左右翻转3.2、上下翻转3.3 上下颠倒&#xff0c;左右相反 4、错切变换4.1 实现错切变换 5、仿射变换5.1 求解仿射变换5.2 OpenCV实现仿射…

力扣 -- 139. 单词拆分

一、题目 题目链接&#xff1a;139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; 二、解题步骤 下面是用动态规划的思想解决这道题的过程&#xff0c;相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 三、参考代码 class Solution { public:bool wordBreak(str…

基于Java的新闻全文搜索引擎的设计与实现

中文摘要 本文以学术研究为目的&#xff0c;针对新闻行业迫切需求和全文搜索引擎技术的优越性&#xff0c;设计并实现了一个针对新闻领域的全文搜索引擎。该搜索引擎通过Scrapy网络爬虫工具获取新闻页面&#xff0c;将新闻内容存储在分布式存储系统HBase中&#xff0c;并利用倒…

建筑行业如果应用了数字孪生技术能有什么改变?

数字孪生是一种将现实世界与数字世界相结合的先进技术&#xff0c;它在建筑行业中正发挥着越来越重要的作用。通过数字孪生技术&#xff0c;建筑行业可以实现从设计、施工到运营的全生命周期数字化管理&#xff0c;带来了许多优势和机遇。 ① 建筑设计阶段的应用 数字孪生能够…

Unity Shader:闪烁

还是一样的分为UI闪烁和物体闪烁&#xff0c;其中具体可分为&#xff1a;UI闪烁、物体闪烁与半透明闪烁 1&#xff0c;UI闪烁 对于UI 还是一样的&#xff0c;改写UI本身的shader&#xff1a; Shader "UI/YydUIShanShder" {Properties{[PerRendererData] _MainTex(…

Spring Boot如何整合mybatis

文章目录 1. 相关配置和代码2. 整合原理2.1 springboot自动配置2.2 MybatisAutoConfiguration2.3 debug过程2.3.1 AutoConfiguredMapperScannerRegistrar2.3.2 MapperScannerConfigurer2.3.4 创建MapperFactoryBean2.3.5 创建MybatisAutoConfiguration2.3.6 创建sqlSessionFact…

怎么合并多个视频?简单视频合并方法分享

合并多个视频可以将它们组合成一个更长的视频&#xff0c;这对于需要播放多个短视频的情况非常有用。此外&#xff0c;合并视频还可以使视频编辑过程更加高效&#xff0c;因为不必将多个独立的视频文件分别处理。最后&#xff0c;合并视频可以减少文件数量&#xff0c;从而使整…

用html+javascript打造公文一键排版系统13:增加半角字符和全角字符的相互转换功能

一、实践发现了bug和不足 今天用了公文一键排版系统对几个PDF文件格式的材料进行文字识别后再重新排版&#xff0c;处理效果还是相当不错的&#xff0c;节约了不少的时间。 但是也发现了三个需要改进的地方&#xff1a; &#xff08;一&#xff09;发现了两个bug&#xff1a;…

【JVM】 垃圾回收篇——自问自答(1)

Q什么是垃圾&#xff1a; 运行程序中&#xff0c;没用任何指针指向的对象。 Q为什么需要垃圾回收&#xff1f; 内存只分配&#xff0c;不整理回收&#xff0c;迟早会被消耗完。 内存碎片的整理&#xff0c;为新对象腾出空间 没有GC程序无法正常进行。 Q 哪些区域有GC&#…

Java电子招投标采购系统源码-适合于招标代理、政府采购、企业采购、等业务的企业tbms

​ 功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查…

多线程案例(3)-定时器

文章目录 多线程案例三三、 定时器 大家好&#xff0c;我是晓星航。今天为大家带来的是 多线程案例三 相关的讲解&#xff01;&#x1f600; 多线程案例三 三、 定时器 定时器是什么 定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就…

视频太大怎么压缩变小?三招教会你压缩视频

如果视频文件太大&#xff0c;不仅占用空间&#xff0c;还不方便传输&#xff0c;这时候就需要我们对视频进行压缩处理&#xff0c;目前市面上有多种视频压缩软件&#xff0c;想要压缩率高&#xff0c;又保留原视频的画质&#xff0c;可以参考以下的几个方法。 一、嗨格式压缩大…

K8s中的Controller

Controller的作用 &#xff08;1&#xff09;确保预期的pod副本数量 &#xff08;2&#xff09;无状态应用部署 &#xff08;3&#xff09;有状态应用部署 &#xff08;4&#xff09;确保所有的node运行同一个pod&#xff0c;一次性任务和定时任务 1.无状态和有状态 无状态&…

python excel 操作

excel文件内容如下&#xff1a; 一、xlrd 读Excel 操作 1、打开Excel文件读取数据 filexlrd.open_workbook(filename)#文件名以及路径&#xff0c;如果路径或者文件名有中文给前面加一个 r 2、常用函数 &#xff08;1&#xff09;获取一个sheet工作表 table file.sheets(…

大模型使用——超算上部署LLAMA-2-70B-Chat

大模型使用——超算上部署LLAMA-2-70B-Chat 前言 1、本机为Inspiron 5005&#xff0c;为64位&#xff0c;所用操作系统为Windos 10。超算的操作系统为基于Centos的linux&#xff0c;GPU配置为A100&#xff0c;所使用开发环境为Anaconda。 2、本教程主要实现了在超算上部署LLAM…