10 минут, чтобы научить вас, как использовать анимацию Python, чтобы продемонстрировать алгоритм первой глубины, чтобы найти выход из лабиринта

@ Эта статья взята из публичного номера: csdn2299, нравится обращать внимание на общедоступную школу программистов нумерации. Что такое
алгоритм « глубина в первую очередь» (алгоритм DFS)?

Алгоритм нахождения пути между начальным узлом и целевым узлом часто используется для поиска пути выхода из лабиринта. Основная идея заключается в том, что начиная со входа, возможные координаты окружающих узлов ищутся по порядку, но они не будут проходить через один и тот же узел повторно и не могут проходить через узлы препятствий. Если вы перейдете к узлу и обнаружите, что пути нет, то вы вернетесь к предыдущему узлу и выберете другой путь. Пока выход не найден, или нет возврата к исходной точке, игра окончена. Конечно, алгоритм первой глубины прекращает поиск до тех пор, пока он находит рабочий путь, то есть до тех пор, пока есть путь, алгоритм первой глубины не будет возвращаться к предыдущему шагу.

Если вы все еще не уверены в мире программирования, вы можете присоединиться к нашей кнопке изучения Python qun: 784758214, чтобы увидеть, как учились предшественники! Обмен опытом! Я старший инженер по разработке Python, от базовых сценариев Python до веб-разработки, сканеров, django, интеллектуального анализа данных и т. Д., С нуля до проектирования фактической боевой информации. Каждому маленькому другу питона! Поделитесь некоторыми методами обучения и небольшими деталями, которые требуют внимания, нажмите, чтобы присоединиться к нашему месту сбора учащихся Python

На следующем рисунке показан путь поиска с использованием алгоритма DFS: Вставьте описание изображения здесь
Summarize:

Начиная с начальной точки, запросите узлы, которые могут пройти через следующий шаг, и поместите эти возможные узлы в стек, и уже пройденные узлы не будут пытаться снова. После завершения запроса удалите узел из стека и запросите, есть ли рабочий узел вокруг узла. Если возможного узла нет, продолжайте извлекать узел из стека. Повторяйте вышеуказанную операцию до тех пор, пока текущий узел не станет конечной точкой или не останется больше узла в стеке.

Определите данные:

Начальный узел и целевой
узел Стек узла хранения
Определение вспомогательных функций

Получите функцию следующего узла: функция-преемник, чтобы
определить, является ли она конечной точкой: test_goal
Сначала давайте определим структуру данных стека. Стек является структурой данных «последний вошел , первым вышел».

Поскольку поиск в ширину будет использовать очередь позже, а алгоритм A * будет использовать очередь с приоритетами, мы определили абстрактный базовый класс для последующего использования. Deque - это двусторонняя очередь, аналогичная встроенной операции со списком типов, но временная сложность операций вставки и удаления заголовка и хвоста равна 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 - стек, а отмеченный - используется для записи проходящих узлов. Функция преемника используется для поиска следующего возможного узла, а функция 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

Прежде всего, чтобы упростить определение взаимосвязи между узлами, мы определяем внутренний класс _Node в классе Maze для записи состояния узла и родительского узла узла.

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

Тогда есть метод преемника: пока узлы в направлениях вверх, вниз, влево и вправо не являются препятствиями и находятся в пределах границы, они рассматриваются и добавляются в список.

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

Путь отображения, пауза - это интервал отображения изображения, а заговор - флаг рисования. Начиная с целевого узла, обойдите родительский узел каждого узла, пока он не достигнет начального узла, и нарисуйте карту пути.

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))
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))

Большое спасибо за чтение
. Когда я решил изучать python в университете, я обнаружил, что у меня плохой компьютерный фундамент. У меня не было академической квалификации. Это
ничего не значит. Я могу только компенсировать это, поэтому я начал свою собственную контратаку вне кодирования. Дорога, продолжать изучать основные знания Python, углубленное изучение основ компьютера, разобрались, если вы не хотите быть посредственным, то присоединяйтесь ко мне в коде, и продолжайте расти!
На самом деле, здесь есть не только технологии, но и вещи, выходящие за рамки этих технологий. Например, как быть изящным программистом, а не «петухом», сам программист - благородное существование, не так ли? [Нажмите, чтобы присоединиться] Хотите быть собой, хотите быть благородным человеком, давай!

Опубликовано 34 оригинальных статей · В избранное12 · Посетителей от 20 000+

рекомендация

отblog.csdn.net/chengxun03/article/details/105476985