10分钟教你用python动画演示深度优先算法搜寻逃出迷宫的路径


Posted in Python onAugust 12, 2019

深度优先算法(DFS 算法)是什么?

寻找起始节点与目标节点之间路径的算法,常用于搜索逃出迷宫的路径。主要思想是,从入口开始,依次搜寻周围可能的节点坐标,但不会重复经过同一个节点,且不能通过障碍节点。如果走到某个节点发现无路可走,那么就会回退到上一个节点,重新选择其他路径。直到找到出口,或者退到起点再也无路可走,游戏结束。当然,深度优先算法,只要查找到一条行得通的路径,就会停止搜索;也就是说只要有路可走,深度优先算法就不会回退到上一步。

如果你依然在编程的世界里迷茫,可以加入我们的Python学习扣qun:784758214,看看前辈们是如何学习的!交流经验!自己是一名高级python开发工程师,从基础的python脚本到web开发、爬虫、django、数据挖掘等,零基础到项目实战的资料都有整理。送给每一位python的小伙伴!分享一些学习的方法和需要注意的小细节,点击加入我们的python学习者聚集地

下图是使用 DFS 算法搜寻出来的一条路径:

10分钟教你用python动画演示深度优先算法搜寻逃出迷宫的路径

总结一下:

从起点开始,查询下一步走得通的节点,将这些可能的节点压入堆栈中,已经走过的节点不再尝试。查询完毕之后,从堆栈中取出一个节点,查询该节点周围是否存在走得通的节点。如果不存在可能的节点,就继续从堆栈中取一个节点。重复以上操作,直到当前节点为终点,或者堆栈中再无节点。

定义数据:

  • 起始节点与目标节点
  • 存储节点的堆栈

定义辅助函数

  • 获取下一节点的函数: successor
  • 判断是否为终点的函数: test_goal

首先,我们来定义栈这种数据结构,栈是一种后进先出的数据结构。

因为之后的广度优先搜索会使用到队列,A* 算法会用到优先队列,我们定义了抽象基类,以便后续使用。deque 是双端队列,与内置类型 list 操作类似,但头部与尾部插入和删除操作的时间复杂度均为 O(1)。

# utils.py
from abc import abstractmethod, ABC
from collections import deque
class Base(ABC):
  def __init__(self):
    self._container = deque()
  @abstractmethod
  def push(self, value):
    """push item"""
  @abstractmethod
  def pop(self):
    """pop item"""
  def __len__(self):
    return len(self._container)
  def __repr__(self):
    return f'{type(self).__name__}({list(self._container)})'
class Stack(Base):
  def push(self, value):
    self._container.append(value)
  def pop(self):
    return self._container.pop()

下面我们来定义 dfs 函数。其中,initial 为初始节点, s 为栈,marked 用来记录经过的节点。successor 函数用来搜寻下一个可能的节点,test_goal 函数用来判断该节点是否为目标节点。children 为可能的节点列表,遍历这些节点,将没有走过的节点压入栈中,并做记录。

# find_path.py
from utils import Stack
def dfs(initial, _next = successor, _test = test_goal):
  s: Stack = Stack()
  marked = {initial}
  s.push(initial)
  while s:
    parent: state = s.pop()
    if _test(parent):
      return parent
    children = _next(parent)
    for child in children:
      if child not in marked:
        marked.add(child)
        s.push(child)

接下来,我们使用 DFS 算法寻找迷宫路径,并对搜寻到的迷宫路径进行可视化演示。

首先使用枚举,来表示路径的颜色, EMPTY 为正常节点,BLOCKED 为障碍节点,START 为迷宫入口,END 为迷宫出口,PATH 为搜寻的路径。

from enum import IntEnum
class Cell(IntEnum):
  EMPTY = 255
  BLOCKED = 0
  START = 100
  END = 200
  PATH = 150

接下来,我们来定义迷宫。首先,我们采用 Namedtuple 来定义迷宫每个节点的坐标:

class MazeLocation(NamedTuple):
  row: int
  col: int

首先为了方便确定节点之间的关系,我们在 Maze 类中定义了一个内部类 _Node, 用来记录节点的状态,及节点的父节点。

class _Node:
  def __init__(self, state, parent):
    self.state = state
    self.parent = parent

接着初始化,确定入口与出口的坐标,使用 np.random.choice 函数随机生成迷宫,并标记入口和出口。

def __init__(self, rows: int = 10, cols: int = 10,
       sparse: float = 0.2, seed: int = 365,
       start: MazeLocation = MazeLocation(0, 0),
       end: MazeLocation = MazeLocation(9, 9), *,
       grid: Optional[np.array] = None) -> None:
  np.random.seed(seed)
  self._start: MazeLocation = start
  self._end: MazeLocation = end
  self._grid: np.array = np.random.choice([Cell.BLOCKED, Cell.EMPTY],
                        (rows, cols), p=[sparse, 1 - sparse])
  self._grid[start] = Cell.START
  self._grid[end] = Cell.END

其次是 test_goal 方法,只要该节点坐标与目标节点相即可。

def _test_goal(self, m1: MazeLocation) -> bool:
  return m1 == self._end

再就是 successor 方法,只要上下左右方向的节点不是障碍节点且在边界之内,就纳入考虑范围,加入列表之中。

List[MazeLocation]:
  location: List[MazeLocation] = []
  row, col = self._grid.shape
  if m1.row + 1 < row and self._grid[m1.row + 1, m1.col] != Cell.BLOCKED:
    location.append(MazeLocation(m1.row + 1, m1.col))
  if m1.row - 1 >= 0 and self._grid[m1.row - 1, m1.col] != Cell.BLOCKED:
    location.append(MazeLocation(m1.row - 1, m1.col))
  if m1.col + 1 < col and self._grid[m1.row, m1.col + 1] != Cell.BLOCKED:
    location.append(MazeLocation(m1.row, m1.col + 1))
  if m1.col - 1 >= 0 and self._grid[m1.row, m1.col - 1] != Cell.BLOCKED:
    location.append(MazeLocation(m1.row, m1.col - 1))
  return location

显示路径, pause 为显示图像的间隔,plot 为是否绘图标志。通过目标节点出发,遍历每一个节点的父节点,直到到达初始节点,并绘制路径图。

None:
  if pause <= 0:
    raise ValueError('pause must be more than 0')
  path: Maze._Node = self._search()
  if path is None:
    print('没有找到路径')
    return
  path = path.parent
  while path.parent is not None:
    self._grid[path.state] = Cell.PATH
    if plot:
      self._draw(pause)
    path = path.parent
  print('Path Done')

为了使用 DFS 算法,我们定义了 DepthFirstSearch 类,继承迷宫类。DepthFirstSearch 类重写了基类的 _search 方法,与我们之前定义的 dfs 函数定义相差无几。

class DepthFirstSearch(Maze):
  def _search(self):
    stack: Stack = Stack()
    initial: DepthFirstSearch._Node = self._Node(self._start, None)
    marked: Set[MazeLocation] = {initial.state}
    stack.push(initial)
    while stack:
      parent: DepthFirstSearch._Node = stack.pop()
      state: MazeLocation = parent.state
      if self._test_goal(state):
        return parent
      children: List[MazeLocation] = self._success(state)
      for child in children:
        if child not in marked:
          marked.add(child)
          stack.push(self._Node(child, parent))

总结

以上所述是小编给大家介绍的10分钟教你用python动画演示深度优先算法搜寻逃出迷宫的路径,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
学习python (1)
Oct 31 Python
python正则表达式抓取成语网站
Nov 20 Python
零基础写python爬虫之爬虫框架Scrapy安装配置
Nov 06 Python
Python 中 Meta Classes详解
Feb 13 Python
Python处理文本文件中控制字符的方法
Feb 07 Python
python itchat实现微信好友头像拼接图的示例代码
Aug 14 Python
Python温度转换实例分析
Jan 17 Python
Django 实现图片上传和显示过程详解
Jul 18 Python
在python image 中安装中文字体的实现方法
Aug 22 Python
python 回溯法模板详解
Feb 26 Python
Jupyter Notebook折叠输出的内容实例
Apr 22 Python
Python 整行读取文本方法并去掉readlines换行\n操作
Sep 03 Python
python利用itertools生成密码字典并多线程撞库破解rar密码
Aug 12 #Python
Python实现网页截图(PyQT5)过程解析
Aug 12 #Python
python实现知乎高颜值图片爬取
Aug 12 #Python
python3 enum模块的应用实例详解
Aug 12 #Python
Python一键查找iOS项目中未使用的图片、音频、视频资源
Aug 12 #Python
django+echart数据动态显示的例子
Aug 12 #Python
Flask框架学习笔记之使用Flask实现表单开发详解
Aug 12 #Python
You might like
亲密接触PHP之PHP语法学习笔记1
2006/12/17 PHP
PHP5中使用PDO连接数据库的方法
2010/08/01 PHP
php中通过Ajax如何实现异步文件上传的代码实例
2011/05/07 PHP
PHP数组实例总结与说明
2011/08/23 PHP
ThinkPHP模板循环输出Volist标签用法实例详解
2016/03/23 PHP
php中使用websocket详解
2016/09/23 PHP
PHP pthreads v3在centos7平台下的安装与配置操作方法
2020/02/21 PHP
php中加密解密DES类的简单使用方法示例
2020/03/26 PHP
PHP设计模式(三)建造者模式Builder实例详解【创建型】
2020/05/02 PHP
让IE6支持min-width和max-width的方法
2010/06/25 Javascript
深入理解JavaScript系列(14) 作用域链介绍(Scope Chain)
2012/04/12 Javascript
Javascript玩转继承(二)
2014/05/08 Javascript
javascript制作的cookie封装及使用指南
2015/01/02 Javascript
Windows系统中安装nodejs图文教程
2015/02/28 NodeJs
IE浏览器下PNG相关功能
2015/07/05 Javascript
用JS实现图片轮播效果代码(一)
2016/06/26 Javascript
浅谈JavaScript中面向对象的的深拷贝和浅拷贝
2016/08/01 Javascript
浅谈Web页面向后台提交数据的方式和选择
2016/09/23 Javascript
基于JS实现二维码图片固定在右下角某处并跟随滚动条滚动
2017/02/08 Javascript
利用angularjs1.4制作的简易滑动门效果
2017/02/28 Javascript
Webpack如何引入bootstrap的方法
2017/06/17 Javascript
jQuery+Ajax请求本地数据加载商品列表页并跳转详情页的实现方法
2017/07/12 jQuery
Js面试算法详解
2018/04/08 Javascript
JS字符串常用操作方法实例小结
2019/06/24 Javascript
python 多线程实现检测服务器在线情况
2015/11/25 Python
python连接mongodb集群方法详解
2020/02/13 Python
Python 判断时间是否在时间区间内的实例
2020/05/16 Python
使用Python快速打开一个百万行级别的超大Excel文件的方法
2021/03/02 Python
莫斯科高科技在线商店:KremlinStore
2019/03/13 全球购物
公司领导推荐信
2013/11/12 职场文书
幼儿园实习生辞职信
2014/01/20 职场文书
师范教师专业大学生职业生涯规划范文
2014/03/02 职场文书
计算机网络工程专业职业生涯规划书
2014/03/10 职场文书
四风个人对照检查材料思想汇报(办公室通用版)
2014/10/07 职场文书
安全保证书
2015/01/16 职场文书
赞美教师的句子
2019/09/02 职场文书