路径规划——贪婪最佳优先搜索

news2024/9/22 19:30:06

路径规划——贪婪最佳优先搜索

学习A算法前先学习一下贪婪最佳优先搜索算法,在学习过程中在网上找了一些博客、文章还有视频来看以深入理解此算法,但是实际情况却是非常令人恼怒。有些文章标题是贪婪最佳优先搜索,内容却是其他的算法,还有的是前面一部分是GBFS,后面却突然变成了其他的算法,更有甚者,先是讲了GBFS的原理,然后展示算法流程,算法流程却是Dijkstra算法或A算法,另外,还有算法效果图胡乱配的,很明显就不是GBFS算法的效果图,让我看得脑子懵懵的,刚开始我还以为是我代码中算法步骤哪里出错了,或者是启发式函数不对,本来GBFS并不复杂,一会儿就能够理解掌握,我却花费了两天(其实是两个半天了,毕竟每天都是睡到中午)来学此算法,这些技术博主/分享者能不能都负责一点啊!真的是要枯了~
好,言归正传,下面学习贪婪最佳优先搜索算法,其实主要是我学习过程中的记录,也是和大家分享一下,希望能够帮助需要学习或是回顾此算法的朋友,如果有误还请指出,谢谢!

贪婪最佳优先搜索(Greedy Best First Search)与Dijkstra类似,却又有不同之处,Dijkstra算法使用当前节点与起点间的实际距离对优先队列排序,而GBFS算法使用当前节点到目标点的估计距离对优先队列排序,将首先探索当前节点的邻节点中离目标点最近的节点,每次迈出的都是最贪婪的一步。

算法原理

贪婪最佳优先搜索是一种启发式搜索算法,核心是每次选择当前节点中启发式函数值最小的节点进行扩展搜索。对于一个图G,从图G中一起点开始遍历,使用启发式函数计算起点的若干可通行的邻节点到目标点的预估代价,并使用优先队列存储待遍历的各个节点,然后下次遍历时从优先队列中选择预估代价最小的节点,重复上述操作直到搜索到目标点或者是遍历完所有节点。

算法流程如下:
在运算过程中每次从open中选择启发式函数值即到目标点的预估距离最小的节点作为当前节点进行遍历;
如果当前节点是目标节点则搜索成功,算法结束;
否则将当前节点从open中移除,并添加到closed中以标记该节点已被遍历;
对于当前节点的若干邻节点,如果已经添加到了closed中了则跳过,如果不在closed,检查是否在open中出现,如果不在open中,则计算其启发式函数值,并将其添加到open中(在这里有人认为还要再加上如果已经出现在open中了则再次计算其启发式函数值,如果小于之前的值则更新,但是目标点是固定的,对于一个节点来讲其启发式函数值也是固定的,又何必要更新呢)。重复以上操作直到open为空或找到目标点。
下面用几张图来展示算法流程:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从起点开始在向上搜索的过程上面的节点和右边的节点的启发式函数值是一样的,这里以向上为优先,这个优先是邻节点存储的顺序次序。

算法实现

import heapq

class Node:
    def __init__(self, name, h=0):
        self.name = name  # 节点名称
        self.h = h        # 启发式函数值
        self.parent = None
    
    def __lt__(self, other):
        # 便于优先队列利用启发式函数值h进行排序
        return self.h < other.h

def gbfs(start, goal, neighbors, heuristic):
    open_list = []
    closed_list = set()
    
    start_node = Node(start, heuristic(start))
    heapq.heappush(open_list, start_node)
    
    while open_list:
        current_node = heapq.heappop(open_list)
        
        if current_node.name == goal:
            path = []
            while current_node:
                path.append(current_node.name)
                current_node = current_node.parent
            return path[::-1]
        
        closed_list.add(current_node.name)
        
        for neighbor in neighbors[current_node.name]:
            if neighbor in closed_list:
                continue
            
            neighbor_node = Node(neighbor, heuristic(neighbor))
            neighbor_node.parent = current_node
            
            if neighbor_node not in open_list:
                heapq.heappush(open_list, neighbor_node)
    
    return None

# 启发式函数
def heuristic(node):
    # 这里是具体的启发式函数计算过程
    return 0

下面举个栗子:
在栅格地图上找到从起点到终点的最佳路径,这里假设只允许朝上下左右四个方向搜索,启发式函数使用曼哈顿距离函数

import heapq
import math
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation


class GBFS:
    def __init__(self,grid,start,goal,board_size):
        self.grid = grid
        self.start = start
        self.goal = goal
        self.board_size = board_size

    class Node:
        def __init__(self, position, h=0, parent=None):
            self.position = position  # position of node
            self.h = h        # heuristic value
            self.parent = parent
        
        def __lt__(self, other):
            # In order to priority queue using heuristic function values for sorting
            return self.h < other.h


    def plan(self):
        open = []
        closed = set() # Used to mark nodes that are closed
        self.searched = [] # Used to record nodes that are searched

        start_node = self.Node(self.start, self.heuristic(self.start))
        heapq.heappush(open, start_node)

        while open:
            # Select the node closest to the start node
            current_node = heapq.heappop(open)
            if current_node.position in closed:
                continue
            # Append the current node into searched nodes
            # self.searched.append(current_node.position)

            closed.add(current_node.position)
            
            # Break when the current node is the goal
            if current_node.position == self.goal:
                self.path = []
                while current_node:
                    self.path.append(current_node.position)
                    current_node = current_node.parent
                self.path = self.path[::-1]
                return len(self.path)-1
            
            # Find the neighbors of the current node and determine in turn if they have already been closed
            neighbors = self.get_neighbors(current_node.position)
            for neighbor in neighbors:
                h = self.heuristic(neighbor)
                # If the current node has been searched, skip it
                if neighbor in self.searched:
                    continue

                neighbor_node = self.Node(neighbor,h,current_node)
                heapq.heappush(open,neighbor_node)
                self.searched.append(neighbor)


        return None

    def get_neighbors(self, node):
        neighbors = []
        next_directions = [(0,1),(1,0),(0,-1),(-1,0)]
        # next_directions = [(-1,0),(1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)]

        for next_d in next_directions:
            neighbor = (node[0] + next_d[0], node[1] + next_d[1])
            if self.board_size <= neighbor[0] < len(self.grid)-self.board_size and self.board_size <= neighbor[1] < len(self.grid[0])-self.board_size:
                if self.grid[neighbor[0]][neighbor[1]] == 0:
                    neighbors.append(neighbor)

        return neighbors
    
    def heuristic(self, node):
        # Manhattan Distance
        return abs(node[0] - self.goal[0]) + abs(node[1] - self.goal[1])
    
    # def heuristic(self, node):
    #     # Chebyshev Distance
    #     D = 1
    #     D2 = 1.414
    #     dx = abs(node[0] - self.goal[0])
    #     dy = abs(node[1] - self.goal[1])
    #     return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)
    
    # def heuristic(self, node):
    #     # Euclidean Distance
    #     D = 1
    #     dx = abs(a[0] - self.goal[0])
    #     dy = abs(a[1] - self.goal[1])
    #     return D * math.sqrt(dx * dx + dy * dy)
    

    def visualize_grid(self, cost):
        fig, ax = plt.subplots()
        grid = np.array(self.grid)

        plt.imshow(grid, cmap='Greys', origin='upper')

        plt.title("BFS\n"+"Cost: "+str(cost))

        # Mark the start and goal point
        plt.scatter(self.start[1], self.start[0], c='green', marker='o', s=60)
        plt.scatter(self.goal[1], self.goal[0], c='blue', marker='o', s=60)

        # # Mark locations which has been visited
        # for node in self.searched:
        #     plt.gca().add_patch(plt.Rectangle((node[1]-0.5, node[0]-0.5), 1, 1, fill=True, color='gray', alpha=0.5))
            
        # # 标记路径
        # if self.path:
        #     path_x, path_y = zip(*self.path)
        #     plt.plot(path_y, path_x, c='red', linewidth=2)

        
        visited_patches = []
        path_line, = ax.plot([], [], c='red', linewidth=2)
        # for order in range(len(self.searched)):
        #     node = self.searched[order]
        #     plt.Rectangle((node[1]-0.5, node[0]-0.5), 1, 1, fill=True, color='gray', alpha=0.5)

        def update(frame):
            if frame < len(self.searched):
                node = self.searched[frame]
                patch = plt.Rectangle((node[1]-0.5, node[0]-0.5), 1, 1, fill=True, color='gray', alpha=0.5)
                visited_patches.append(patch)
                ax.add_patch(patch)
            elif self.path:
                path_x, path_y = zip(*self.path)
                path_line.set_data(path_y, path_x)

            return visited_patches + [path_line]*10
    
        ani = animation.FuncAnimation(fig, update, frames=len(self.searched) + 10, interval=100, repeat=False)
        ani.save("map_animate1.gif",writer='pillow')
        # ani.save("map_animate.mp4",writer='ffmpeg')

        plt.savefig("map_animate1.png",bbox_inches='tight')
        plt.show()

结果:
在这里插入图片描述

启发式函数用对角距离试一下:

import heapq
import math
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation


class GBFS:
    def __init__(self,grid,start,goal,board_size):
        self.grid = grid
        self.start = start
        self.goal = goal
        self.board_size = board_size

    class Node:
        def __init__(self, position, h=0, parent=None):
            self.position = position  # position of node
            self.h = h        # heuristic value
            self.parent = parent
        
        def __lt__(self, other):
            # In order to priority queue using heuristic function values for sorting
            return self.h < other.h


    def plan(self):
        open = []
        closed = set() # Used to mark nodes that are visited
        self.searched = [] # Used to record nodes that are searched

        start_node = self.Node(self.start, self.heuristic(self.start))
        heapq.heappush(open, start_node)

        while open:
            # Select the node closest to the start node
            current_node = heapq.heappop(open)
            if current_node.position in closed:
                continue
            # Append the current node into searched nodes
            # self.searched.append(current_node.position)

            closed.add(current_node.position)
            
            # Break when the current node is the goal
            if current_node.position == self.goal:
                self.path = []
                while current_node:
                    self.path.append(current_node.position)
                    current_node = current_node.parent
                self.path = self.path[::-1]
                return len(self.path)-1
            
            # Find the neighbors of the current node and determine in turn if they have already been closed
            neighbors = self.get_neighbors(current_node.position)
            for neighbor in neighbors:
                h = self.heuristic(neighbor)
                # If the current node has been searched, skip it
                if neighbor in self.searched:
                    continue

                neighbor_node = self.Node(neighbor,h,current_node)
                heapq.heappush(open,neighbor_node)
                self.searched.append(neighbor)


        return None

    def get_neighbors(self, node):
        neighbors = []
        next_directions = [(0,1),(1,0),(0,-1),(-1,0)]
        # next_directions = [(-1,0),(1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)]

        for next_d in next_directions:
            neighbor = (node[0] + next_d[0], node[1] + next_d[1])
            if self.board_size <= neighbor[0] < len(self.grid)-self.board_size and self.board_size <= neighbor[1] < len(self.grid[0])-self.board_size:
                if self.grid[neighbor[0]][neighbor[1]] == 0:
                    neighbors.append(neighbor)

        return neighbors
    
    # def heuristic(self, node):
    #     # Manhattan Distance
    #     return abs(node[0] - self.goal[0]) + abs(node[1] - self.goal[1])
    
    def heuristic(self, node):
        # Chebyshev Distance
        D = 1
        D2 = 1.414
        dx = abs(node[0] - self.goal[0])
        dy = abs(node[1] - self.goal[1])
        return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)
    
    # def heuristic(self, node):
    #     # Euclidean Distance
    #     D = 1
    #     dx = abs(a[0] - self.goal[0])
    #     dy = abs(a[1] - self.goal[1])
    #     return D * math.sqrt(dx * dx + dy * dy)
    

    def visualize_grid(self, cost):
        fig, ax = plt.subplots()
        grid = np.array(self.grid)

        plt.imshow(grid, cmap='Greys', origin='upper')

        plt.title("BFS\n"+"Cost: "+str(cost))

        # Mark the start and goal point
        plt.scatter(self.start[1], self.start[0], c='green', marker='o', s=60)
        plt.scatter(self.goal[1], self.goal[0], c='blue', marker='o', s=60)

        # # Mark locations which has been visited
        # for node in self.searched:
        #     plt.gca().add_patch(plt.Rectangle((node[1]-0.5, node[0]-0.5), 1, 1, fill=True, color='gray', alpha=0.5))
            
        # # 标记路径
        # if self.path:
        #     path_x, path_y = zip(*self.path)
        #     plt.plot(path_y, path_x, c='red', linewidth=2)

        
        visited_patches = []
        path_line, = ax.plot([], [], c='red', linewidth=2)
        # for order in range(len(self.searched)):
        #     node = self.searched[order]
        #     plt.Rectangle((node[1]-0.5, node[0]-0.5), 1, 1, fill=True, color='gray', alpha=0.5)

        def update(frame):
            if frame < len(self.searched):
                node = self.searched[frame]
                patch = plt.Rectangle((node[1]-0.5, node[0]-0.5), 1, 1, fill=True, color='gray', alpha=0.5)
                visited_patches.append(patch)
                ax.add_patch(patch)
            elif self.path:
                path_x, path_y = zip(*self.path)
                path_line.set_data(path_y, path_x)

            return visited_patches + [path_line]*10
    
        ani = animation.FuncAnimation(fig, update, frames=len(self.searched) + 10, interval=100, repeat=False)
        ani.save("map_animate.gif",writer='pillow')
        # ani.save("map_animate.mp4",writer='ffmpeg')

        plt.savefig("map_animate.png",bbox_inches='tight')
        plt.show()

结果:
在这里插入图片描述

这个例子中使用曼哈顿距离和使用对角距离得到的路径虽然不同但是路径长度却是相等的,那么,总是相等的吗?答案是否定的,是否相等取决于实际情况。如下情况:
使用曼哈顿距离:
在这里插入图片描述

使用对角距离:
在这里插入图片描述

这种情况下使用曼哈顿距离路径较短。
再来看一种情况:
使用曼哈顿距离:
在这里插入图片描述

使用对角距离:
在这里插入图片描述
这里是使用对角距离启发式函数得到的路径较短。
在图搜索中,如果只允许朝上下左右四个方向移动,则使用曼哈顿距离;如果可以朝上下左右以及对角线方向共八个方向移动,则使用对角距离;如果可以朝任意方向移动,则使用欧几里得距离。但是启发式函数是用于预估当前位置到目标点间的距离,所以使用不同的启发式函数,结果也会有所不同。在上面的程序中尽管只允许朝上下左右四个方向移动,有时使用对角距离也会得到较曼哈顿距离好更的结果。
大家可以将搜索邻居节点时的方向由4个改为8个试试。

next_directions = [(0,1),(1,0),(0,-1),(-1,0)]
 # next_directions = [(-1,0),(1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)]

关于图搜索中的启发式函数的应用,下次在A* 算法中会再具体学习。

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

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

相关文章

【密码学】椭圆曲线密码体制(ECC)

椭圆曲线密码体制&#xff08;Elliptic Curve Cryptography, ECC&#xff09;是一种基于椭圆曲线数学特性的公钥密码系统。在介绍椭圆曲线之前&#xff0c;我们先来了解一下椭圆曲线的基本概念。 一、椭圆曲线是什么&#xff1f; &#xff08;1&#xff09;椭圆曲线的数学定义…

PolSARPro软件安装处理TerraSAR数据(CSDN_20240804)

1. 打开polSARPro软件&#xff0c;点击Enter 2. 点击OK 3. 点击左侧第一个图像&#xff0c;进入PolSARPro Bio。 4. 点击Enter. 5. 点击Environment&#xff0c;选择Single Data Set 6. 选择工作路径 7. 点击No 8. Import -> Spaceborne Sensors ->TerraSAR-X->Quad-P…

C++的vector类

目录 简介 特点 接口 构造函数 迭代器函数 Capacity系列 element access系列 modifiers系列 定义在全局的重载函数 find 总结 简介 vector 是 C 标准模板库&#xff08;Standard Template Library&#xff0c;简称 STL&#xff09;中的一个模板类&#xff0c;用于表…

【iOS】——GCD总结

同步和异步的区别 同步执行等待操作完成&#xff0c;而异步执行允许程序在操作完成前继续运行&#xff0c;提高了效率和响应性。这里的关键就是上一个操作需不需要等待当前操作的执行&#xff0c;如果需要就是同步&#xff0c;如果不需要就是异步。 异步有开启新线程的能力但…

如何构建AI产品:OpenAI与前Shopify产品负责人Miqdad Jaffer的经验分享

一、引言 构建AI产品是一项复杂且充满挑战的任务&#xff0c;尤其是当涉及到面向消费者的解决方案时。在最近的一期播客节目中&#xff0c;OpenAI 和前Shopify产品负责人 Miqdad Jaffer 分享了他在构建AI产品的经验和策略。下面我们将探讨构建AI产品的最佳实践&#xff0c;以及…

行为型设计模式1:状态/策略/命令

行为型设计模式&#xff1a;状态/策略/命令 (qq.com)

【秋招笔试】24-08-03-米哈游-秋招提前批笔试题

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍰 米哈游提前批笔试也是来了,本次题目…

初谈表的约束

文章目录 概念空属性默认值空属性和默认值对比列描述zerofill主键 概念 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。比如有一个字段是emai…

Open3D 计算点云的归一化协方差矩阵

目录 一、概述 1.1原理 1.2实现步骤 1.3应用 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2数据显示 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#xff09;-CSDN博…

文章相关接口

1.新增文章分类 文章分类的表结构和实体类 实体类 接口文档 实现 新创建CategoryController,CategoryService,(CategoryServiceImpl),CategoryMapper 在CategoryController中添加方法 使用注解PostMapping,没有映射路径&#xff0c;我们在CategoryController的类上添加一个映…

Java 并发编程:Java 中的乐观锁与 CAS

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 025 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

【DOCKER】显示带UI的软件

1. Linux 1.1 宿主机开放X server权限 xhost 1.2 启动容器 docker run -it --rm --privilegedtrue --useru20 --workdir/home/u20 \ -e DISPLAYhost.docker.internal:0 u20:dev1.3 测试 # 安装测试软件 sudo apt-get -y install x11-apps# 显示测试程序 xclock2. Windows …

websocket的学习

第一步&#xff1a;配置Spring <dependency><groupId>org.springframework</groupId><artifactId>spring-messaging</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> …

RabbitMQ知识总结(基本原理+高级特性)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 基本原理 消息的可靠性投递 RabbitMQ 消息的投递路径为&#xff…

Idea包含UI内容的插件开发

Idea包含UI内容的插件开发 前言插件效果项目结构配置功能的实现找一个股票接口完成最终的页面配置Plugin.xml源码地址 前言 在这一篇文章中将会做一个包含UI内容的能看股票的插件。 插件效果 首先是在设置中配置股票的编号&#xff0c;如sh000001,sh600519。 接着在侧边栏中…

手机端微信聊天记录无法全部同步到电脑端的微信?搞定它!

前言 昨天晚上深夜…… 哼哼&#xff0c;想哪去了&#xff1f; 昨天有个深圳的哥们跟小白吐槽&#xff1a;手机端的微信聊天记录怎么没办法自动同步到电脑端上&#xff1f; 刚开始小白还以为他是因为电脑端的微信在线也没办法同步聊天记录&#xff0c;所以就给出了答案&…

样式与特效(3)——实现一个测算页面

这次我们使用前端实现一个简单的游戏页面,理论上可以增加很多玩法&#xff0c;&#xff0c;但是这里为了加深前端的样式和JS点击事件&#xff0c;用该案例做练习。 首先需要掌握手机端的自适应&#xff0c;我们是只做手机端玩家页面 。需要允许自适应手机端页面&#xff0c; 用…

OpenCV||超详细的图像处理模块

一、颜色变换cvtColor dst cv2.cvtColor(src, code[, dstCn[, dst]]) src: 输入图像&#xff0c;即要进行颜色空间转换的原始图像。code: 转换代码&#xff0c;指定要执行的颜色空间转换类型。这是一个必需的参数&#xff0c;决定了源颜色空间到目标颜色空间的转换方式。dst…

实现元素定位:掌握Selenium八大定位方法

文章目录 0. 八大定位方法1. id2. name3. xpath4. css_selector 0. 八大定位方法 当实现测试自动化&#xff0c;编写测试用例时&#xff0c;首先需要在web界面找到对应元素位置&#xff0c;而Selenium提供了一套对应的API&#xff0c;被封装在WebDriver类中。如下图&#xff0…

JAVA字符串刷题(力扣经典算法及题解)

练习一&#xff1a; 输入字符串aba,依次输出各个字符 import java.util.Scanner;public class StringTomrs {public static void main(String[] args) {Scanner scnew Scanner(System.in);String numbersc.next();System.out.println("输入的字符串是"number);for(i…