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 Mysql数据库操作 Perl操作Mysql数据库
Jan 12 Python
python动态监控日志内容的示例
Feb 16 Python
分析Python编程时利用wxPython来支持多线程的方法
Apr 07 Python
Python变量和字符串详解
Apr 29 Python
Python实现基本数据结构中栈的操作示例
Dec 04 Python
Linux CentOS7下安装python3 的方法
Jan 21 Python
python队列Queue的详解
May 10 Python
python GUI库图形界面开发之PyQt5窗口背景与不规则窗口实例
Feb 25 Python
在pycharm中创建django项目的示例代码
May 28 Python
python中rc1什么意思
Jun 19 Python
Python logging模块异步线程写日志实现过程解析
Jun 30 Python
Pandas对每个分组应用apply函数的实现
Dec 13 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将数据导入到Foxmail
2006/10/09 PHP
php的慢速日志引起的Mysql错误问题分析
2014/05/13 PHP
PHP扩展模块memcached长连接使用方法分析
2014/12/24 PHP
使用JavaScript创建新样式表和新样式规则
2016/06/14 PHP
详解PHP素材图片上传、下载功能
2019/04/12 PHP
jQuery ajax在GBK编码下表单提交终极解决方案(非二次编码方法)
2010/10/20 Javascript
js实现无需数据库的县级以上联动行政区域下拉控件
2013/08/14 Javascript
jquery制作图片时钟特效
2020/03/30 Javascript
jQuery实现进度条效果代码
2015/12/17 Javascript
jQuery ajax的功能实现方法详解
2017/01/06 Javascript
AngularJS的ng-click传参的方法
2017/06/19 Javascript
JavaScript实现左侧菜单效果
2017/12/14 Javascript
vue组件详解之使用slot分发内容
2018/04/09 Javascript
详解easyui基于 layui.laydate日期扩展组件
2018/07/18 Javascript
Python实现多行注释的另类方法
2014/08/22 Python
python实现拓扑排序的基本教程
2018/03/11 Python
Flask框架配置与调试操作示例
2018/07/23 Python
使用Python获取网段IP个数以及地址清单的方法
2018/11/01 Python
Python3.4学习笔记之 idle 清屏扩展插件用法分析
2019/03/01 Python
对Python3之方法的覆盖与super函数详解
2019/06/26 Python
python模拟实现分发扑克牌
2020/04/22 Python
Python3.7安装pyaudio教程解析
2020/07/24 Python
python opencv肤色检测的实现示例
2020/12/21 Python
Ubuntu20下的Django安装的方法步骤
2021/01/24 Python
移动端HTML5 input常见问题(小结)
2020/09/28 HTML / CSS
Levi’s美国官网:美国著名的牛仔裤品牌
2016/08/19 全球购物
Bodum官网:咖啡和茶壶、玻璃器皿、厨房电器等
2018/08/01 全球购物
Etam德国:内衣精品店
2019/08/25 全球购物
基层干部十八大感言
2014/01/19 职场文书
教师个人剖析材料
2014/02/05 职场文书
感恩教育活动总结
2014/05/05 职场文书
教师暑期培训感言
2014/08/15 职场文书
党员志愿者活动方案
2014/08/28 职场文书
爱情保证书
2015/01/17 职场文书
酒会开场白大全
2015/06/01 职场文书
《弟子规》读后感:知廉耻、明是非、懂荣辱、辨善恶
2019/12/03 职场文书