Python - 深夜数据结构与算法之 Heuristic Search

目录

一.引言

二.启发式搜索简介

1.BFS 广度优先

2.A* Search

3.估价函数

三.经典算法实战

1.Binary-Shortest-Path [1091]

2.Sliding-Puzzle [773]

四.总结


一.引言

Heuristic Search 启发式搜索,又名 A* 搜索,其不基于广度、也不基于深度而是基于元素的优先级进行遍历搜索,不断优化搜索的方向提高搜索效率,下面我们看下启发式搜索的常用方式与算法例题。

二.启发式搜索简介

1.BFS 广度优先

2.A* Search

比起从 queue 里一个一个的顺序弹出,我们可以加入一些智能的元素,让弹出的元素使得搜索的方向更加优化,所以我们把 queue 替换成了 priotity_queue,而优先级的定义则需要我们根据实际问题构建一个估价函数 h(n)。

3.估价函数

上面的优先队列的评估标准需要基于 h(n),这个就是一个启发式的方法,其返回一个非负实数,我们可以认为返回的值越大,当前的节点相对于最终的结果搜索方向更优,即 h(n) 越大,元素优先级越高,越先遍历该元素。

三.经典算法实战

1.Binary-Shortest-Path [1091]

二进制最短路径: https://leetcode.cn/problems/shortest-path-in-binary-matrix

◆ 题目分析

根据 BFS 逐层遍历,当我们第一次到达终点时,也就是最短路径到达时。

◆ BFS

class Solution(object):def shortestPathBinaryMatrix(self, grid):""":type grid: List[List[int]]:rtype: int"""M = len(grid)if grid[0][0] == 1 or grid[M - 1][M - 1] == 1:return -1stack = [(0, 0, 1)]grid[0][0] = 1# 8联通direction = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]while stack:row, col, step = stack.pop(0)# 终止条件if row == M - 1 and col == M - 1:return step# 8 连通图for dx, dy in direction:i, j = row + dx, col + dyif 0 <= i < M and 0 <= j < M and grid[i][j] == 0:grid[i][j] = 1stack.append((i, j, step + 1))return -1

这里有的同学可能有疑问,明明8个方向,为什么直接就返回 step 了,下面提供两种思路:

A.广度优先每一层的行走距离是一样的,如果当前层率先走到终点,则其一定是最短的

B.我们对每个走过的点标为了 1,如果一个点需要先到这个标 1 的点再去终点,那么它一定没有标 1 这个点近。这个找了个示例,大家更好理解下,对于 [0,1] 位置,其可以走到 [0, 2] 和 [1, 2],如果先走 [0, 2] 再走 [1, 2] 到 [2, 2],那么一定没有 [1, 2] 到 [2, 2] 近。而标记过后我们也可以看出,[0, 2] 位置已经无路可走了,而 [1, 2] 还能走。

    grid = [[0, 0, 0],  => grid = [[1, 1, 1],[1, 1, 0],             [1, 1, 1],[1, 1, 0]]             [1, 1, 0]]

◆ A* x BFS

import heapqclass Solution(object):def shortestPathBinaryMatrix(self, grid):# 边界条件n = len(grid)if not grid or grid[0][0] == 1 or grid[n - 1][n - 1] == 1:return -1elif n <= 2:return n# 估价函数def heuristic(x, y):return max(abs(n - 1 - x), abs(n - 1 - y))h = []direction = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]# (val, (row, col, step))heapq.heappush(h, (0, (0, 0, 1)))while h:_, (row, col, step) = heapq.heappop(h)# 遍历点标记if grid[row][col] == 1:continuegrid[row][col] = 1for dx, dy in direction:i, j = row + dx, col + dyif i == n - 1 and j == n - 1:return step + 1if 0 <= i < n and 0 <= j < n and grid[i][j] == 0:# 估价函数为 步数 + 距离 最小即最短路径heapq.heappush(h, (step + heuristic(i, j), (i, j, step + 1)))return -1

heapq 是小根堆,所以我们的估价函数需要满足距离越近值越小,从而越先遍历近的点,上面的 L1 曼哈顿距离满足相距越近距离越小。

2.Sliding-Puzzle [773]

滑动谜题: https://leetcode.cn/problems/sliding-puzzle/description/

◆ 题目分析

这个题可以将 2x3 网格转换一个 len = 6 的字符串,然后每次遍历 0 可以交换的位置,依次 BFS 直到达到 012345 的状态,所以本题就可以转换为一个字符接龙的问题,可以回看前面双向 BFS 的章节。

◆ BFS

class Solution(object):def slidingPuzzle(self, board):""":type board: List[List[int]]:rtype: int"""# 记录下一次可走的位置moves = {0: [1, 3],1: [0, 2, 4],2: [1, 5],3: [0, 4],4: [1, 3, 5],5: [2, 4]}used = set()cnt = 0s = "".join(str(c) for row in board for c in row)# 格子以及0的位置q = [(s, s.index("0"))]while q:new = []for s, i in q:used.add(s)if s == "123450":return cntarr = [c for c in s]for move in moves[i]:new_arr = arr[:]new_arr[i], new_arr[move] = new_arr[move], new_arr[i]new_s = "".join(new_arr)if new_s not in used:new.append((new_s, move))cnt += 1q = newreturn -1

◆ 双向 BFS

class Solution(object):def slidingPuzzle(self, board):""":type board: List[List[int]]:rtype: int"""moves = {0: [1, 3],1: [0, 2, 4],2: [1, 5],3: [0, 4],4: [1, 3, 5],5: [2, 4]}used = set()cnt = 0s = "".join(str(c) for row in board for c in row)# 格子以及0的位置front = [s]back = ["123450"]while front and back:new = []for s in front:used.add(s)index = s.index("0")# 相遇了if s in back:return cntarr = [c for c in s]for move in moves[index]:new_arr = arr[:]new_arr[index], new_arr[move] = new_arr[move], new_arr[index]new_s = "".join(new_arr)if new_s not in used:new.append(new_s)cnt += 1if len(new) > len(back):front, back = back, newelse:front = newreturn -1

套用单词接龙模版,直接双向 BFS 即可。 

◆ A* x BFS

import heapq
M, N = 2, 3class Solution:def slidingPuzzle(self, board):def h(s):dist = 0s2 = "123450"i1 = s.index("0")i2 = s2.index("0")x1, y1 = i1 % N, i1 // Nx2, y2 = i2 % N, i2 // Ndist += abs(x1 - x2) + abs(y1 - y2)return distend_state = "123450"board = board[0] + board[1]s = "".join(str(c) for c in board)moves = [(1, 3), (0, 2, 4), (1, 5), (0, 4), (1, 3, 5), (2, 4)]visited = set()q = [] step = 0heapq.heappush(q, (h(s) + step, s, step))while q:sample = heapq.heappop(q)state, step = sample[1], sample[2]if state == end_state:return stepnow = state.index('0')for next0 in moves[now]:_state = list(state)_state[next0], _state[now] = _state[now], _state[next0]_state = "".join(_state)if _state not in visited: heapq.heappush(q, (h(_state) + step + 1, _state, step + 1))visited.add(state)return -1

和上面同理,把无脑 append + pop 改成使用 headq 进行 push 和 pop,距离采用曼哈顿距离计算, 这里由于网格 M/N 的大小比较小,所以 BFS 会比 A* 快一些,如果网格的增加,A* 会逐渐体现出其优势。

四.总结

根据场景的不同,我们可以选择 BFS -> 双向 BFS -> A* Search + h(n)  -> 双向 A* Search + h(n),这里 A* 主要区别在于估价函数的选择,下面链接了给出了常用的五种距离函数,对于我们常见的二维网格 + 抵达终点的情况,我们一般使用曼哈顿距离即可。

A* 常用距离函数: https://dataaspirant.com/five-most-popular-similarity/

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

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

相关文章

Retrieval-Augmented Generation for Large Language Models: A Survey

PS: 梳理该 Survey 的整体框架&#xff0c;后续补充相关参考文献的解析整理。本文的会从两个角度来分析总结&#xff0c;因此对于同一种技术可能在不同章节下都会有提及。第一个角度是从整体框架的迭代来看&#xff08;对应RAG框架章节&#xff09;&#xff0c;第二个是从RAG中…

Tensorflow Lite从入门到精通

TensorFlow Lite 是 TensorFlow 在移动和 IoT 等边缘设备端的解决方案&#xff0c;提供了 Java、Python 和 C API 库&#xff0c;可以运行在 Android、iOS 和 Raspberry Pi 等设备上。目前 TFLite 只提供了推理功能&#xff0c;在服务器端进行训练后&#xff0c;经过如下简单处…

【开源】基于JAVA+Vue+SpringBoot的超市账单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统设计3.1 总体设计3.2 前端设计3.3 后端设计在这里插入图片描述 四、系统展示五、核心代码5.1 查询供应商5.2 查询商品5.3 新增超市账单5.4 编辑超市账单5.5 查询超市账单 六、免责说明 一、摘要 1.1 项目介绍 基于…

使用pygame.draw绘制基本图形

import pygame# 初始化pygame pygame.init()# 创建显示窗口 screen pygame.display.set_mode((640, 480)) pygame.display.set_caption("绘制基本图形")# 定义颜色 BLACK (0, 0, 0) WHITE (255, 255, 255) RED (255, 0, 0) GREEN (0, 255, 0) BLUE (0, 0, 255)…

用java实现Client和Server之间的互相通信

概要&#xff1a;看过我之前文章的人都知道&#xff0c;client和server之间的通信必不可少的就是socket。而java已经帮我们做了很多事情。 创建Server端 第一步&#xff0c;创建ServerSocket 这个从名字上就可以看出来&#xff0c;服务器上的socket 0.0 ServerSocket ser…

(详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models

Haoran Wei1∗, Lingyu Kong2∗, Jinyue Chen2, Liang Zhao1, Zheng Ge1†, Jinrong Yang3, Jianjian Sun1, Chunrui Han1, Xiangyu Zhang1 1MEGVII Technology 2University of Chinese Academy of Sciences 3Huazhong University of Science and Technology arXiv 2023.12.11 …

Docker部署Homepage个人引导页

个人名片&#xff1a; 对人间的热爱与歌颂&#xff0c;可抵岁月冗长&#x1f31e; 个人主页&#x1f468;&#x1f3fb;‍&#x1f4bb;&#xff1a;念舒_C.ying 个人博客&#x1f30f; &#xff1a;念舒_C.ying Homepage | 主页 1. 安装环境2. Docker部署 原作者&#xff1a;無…

flutter release包使用adb查看日志排查错误实践

release包给出去后发现出现无法启动的情况&#xff0c;需要flutter开发排查 &#xff0c;直接将release包安装到模拟器 使用adb 去连接模拟器 我这边是MuMu模拟器 adb connect 127.0.0.1:7555 然后查看设备列表 adb devices 直接输入0 即选择第一个设备 然后使用 adb logcat …

Python爬虫---Scrapy项目的创建及运行

Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。 可以应用在包括数据挖 掘&#xff0c;信息处理或存储历史数据等一系列的程序中。 1. 安装scrapy&#xff1a; pip install scrapy 注意&#xff1a;需要安装在python解释器相同的位置,例如&#xf…

Spring原理-7.切点与切面

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

静态网页设计实践(HTML+CSS)

一、前端程序员必会三大编程语言 &#xff08;一&#xff09;HTML&#xff08;.html/.htm&#xff09; 超文本标记语言&#xff08;HyperText Markup Language&#xff0c;简称:HTML&#xff09;是一种用于创建网页的标准标记语言。HTML是一种基础技术&#xff0c;常与CSS、Ja…

高通rb5的fastboot设备识别不了及驱动安装问题

Android fastboot驱动无法安装和识别问题-CSDN博客 以上为转载出处。

使用global mapper将分块DSM/DOM合并导出

使用global mapper将分块DSM/DOM合并导出 使用context capture生产dom/dsm时通常因为内存问题而选择分块生产&#xff0c;那么得到的dsm/dom则是一块一块的&#xff0c;如下&#xff1a; 那么为了合并成一张影像&#xff0c;可以使用强大的地图软件 Global Mapper&#xff0c…

番外篇-区块链基础知识入门

今天聊聊番外篇之Web3、区块链的基础知识~ 1. 区块链是如何工作的&#xff1f; Hash算法 将输入的数据映射为一个固定长度的字符串字符串是64长度&#xff0c;16进制&#xff08;2^4&#xff09;&#xff0c;4 * 64 256【SHA256】hash演示&#xff1a;https://andersbrownwo…

android 9 reboot流程

机器出现开机 自动进入fastboot模式。可能是init 那个进程挂了 然后调用了 RebootSystem(ANDROID_RB_RESTART2, “bootloader”); 函数进入重启流程&#xff0c;然后重启后进入fastboot 浅读一下reboot流程和怎么进入的fastboot 比如说是那个进程挂了调用了这个函数&#xff0c…

实现目标检测中的数据格式自由(labelme json、voc、coco、yolo格式的相互转换)

在进行目标检测任务中&#xff0c;存在labelme json、voc、coco、yolo等格式。labelme json是由anylabeling、labelme等软件生成的标注格式、voc是通用目标检测框&#xff08;mmdetection、paddledetection&#xff09;所支持的格式&#xff0c;coco是通用目标检测框&#xff0…

cosmos及特定应用程序的区块链

特定应用程序的区块链,简单来说&#xff0c;一个区块链就是一个专门的应用程序。为了实现某一特定的去中心化应用而专门实现一个区块链。 传统的用智能合约构建去中心化应用不行吗&#xff1f; 灵活性不足&#xff1a;智能合约本质上受到虚拟机本身的限制。例如&#xff0c;以…

【Spring Boot】SpringBoot maven 项目创建图文教程

创建一个Spring Boot项目并使用Maven进行构建是一项相对简单的任务。以下是使用IntelliJ IDEA创建Spring Boot Maven项目的详细教程&#xff1a; 步骤 1&#xff1a;安装 IntelliJ IDEA 确保你已经安装了最新版本的 IntelliJ IDEA。你可以从官方网站下载并安装。 步骤 2&am…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(十一)

加入redis缓存 1. 缓存菜品1.1 问题说明1.2 实现思路1.3 代码开发1.4 功能测试 2. 缓存套餐2.1 Spring Cache2.1.1 介绍2.1.2 常用注解2.1.3 入门案例 2.2 具体实现思路2.3 代码开发 1. 缓存菜品 1.1 问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;…

MySQL一主一从读写分离

​ MySQL主从复制 一、主从复制概念 主从复制是指将主数据库的DDL和DML操作通过二进制日志传到从服务器中&#xff0c;然后在从服务器上对这些日志重新执行也叫重做&#xff0c;从而使得从数据库和主库的数据保持同步。 MySQL支持一台主库同时向多台从库进行赋值&#xff0c;从…