10 minutos para enseñarle cómo usar la animación de Python para demostrar el algoritmo de profundidad primero para encontrar la salida del laberinto

@Este artículo proviene del número público: csdn2299, me gusta prestar atención a la escuela de programadores de números públicos ¿Cuál es el
primer algoritmo de profundidad (algoritmo DFS)?

El algoritmo para encontrar la ruta entre el nodo inicial y el nodo objetivo a menudo se usa para buscar la ruta para escapar del laberinto. La idea principal es que a partir de la entrada, se buscan en orden las posibles coordenadas de los nodos circundantes, pero no pasarán por el mismo nodo repetidamente y no podrán pasar por los nodos de obstáculos. Si va a un nodo y descubre que no hay forma de hacerlo, volverá al nodo anterior y elegirá otra ruta. Hasta que se encuentre la salida, o no haya forma de volver al punto de partida, el juego termina. Por supuesto, el algoritmo de profundidad primero dejará de buscar siempre que encuentre una ruta de trabajo; es decir, mientras haya un camino por recorrer, el algoritmo de profundidad primero no volverá al paso anterior.

Si todavía está confundido en el mundo de la programación, puede unirse a nuestro botón de aprendizaje de Python qun: 784758214 para ver cómo aprendieron los predecesores. Intercambio de experiencia! Soy un ingeniero senior de desarrollo de python, desde scripts básicos de python hasta desarrollo web, rastreadores, django, minería de datos, etc., desde cero hasta proyectar información de combate real. ¡A cada pequeño amigo de Python! Comparta algunos métodos de aprendizaje y pequeños detalles que requieren atención, haga clic para unirse a nuestro lugar de reunión de estudiantes de Python

La siguiente figura es una ruta buscada utilizando el algoritmo DFS: Inserte la descripción de la imagen aquí
Resumir:

Comenzando desde el punto de partida, consulte los nodos que pueden pasar por el siguiente paso, e inserte estos posibles nodos en la pila, y los nodos que ya han pasado no volverán a intentarlo. Una vez completada la consulta, elimine un nodo de la pila y consulte si hay un nodo de trabajo alrededor del nodo. Si no hay un nodo posible, continúe tomando un nodo de la pila. Repita la operación anterior hasta que el nodo actual sea el punto final o no haya más nodo en la pila.

Definir los datos:

Nodo de inicio y nodo de destino
Pila de nodos de almacenamiento
Definir funciones auxiliares

Obtenga la función del siguiente nodo: función sucesora para
determinar si es el punto final: test_goal
Primero, definamos la estructura de datos de la pila. La pila es una estructura de datos de último en entrar, primero en salir.

Debido a que la búsqueda de amplitud primero usará la cola más tarde, y el algoritmo A * usará la cola de prioridad, hemos definido una clase base abstracta para su uso posterior. deque es una cola de doble extremo, similar a la operación de lista de tipos incorporada, pero la complejidad temporal de las operaciones de inserción y eliminación de cabeza y cola son 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()

A continuación, definamos la función dfs. Entre ellos, inicial es el nodo inicial, s es la pila, y marcado se usa para registrar los nodos que pasan. La función sucesora se usa para buscar el siguiente nodo posible, y la función test_goal se usa para determinar si el nodo es el nodo objetivo. children es una lista de posibles nodos, atraviesa estos nodos, empuja los nodos que no han viajado a la pila y haz un registro.

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

A continuación, usamos el algoritmo DFS para encontrar la ruta del laberinto y demostrar visualmente la ruta del laberinto buscado.

Primero use una enumeración para indicar el color de la ruta, VACÍO es el nodo normal, BLOQUEADO es el nodo de obstáculo, INICIO es la entrada del laberinto, FIN es la salida del laberinto y RUTA es la ruta de búsqueda.

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

A continuación, definamos el laberinto. Primero, usamos Namedtuple para definir las coordenadas de cada nodo del laberinto:

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

En primer lugar, para facilitar la determinación de la relación entre nodos, definimos una clase interna _Nodo en la clase Maze para registrar el estado del nodo y el nodo padre del nodo.

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

Luego inicialice, determine las coordenadas de la entrada y la salida, use la función np.random.choice para generar aleatoriamente el laberinto y marque la entrada y la salida.

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

El segundo es el método test_goal, siempre que las coordenadas del nodo sean relativas al nodo objetivo.

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

Luego está el método sucesor: siempre que los nodos en las direcciones arriba, abajo, izquierda y derecha no sean obstáculos y estén dentro del límite, se tendrán en cuenta y se agregarán a la lista.

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

La ruta de visualización, la pausa es el intervalo de visualización de la imagen y el trazado es la bandera de dibujo. Comenzando desde el nodo objetivo, atraviese el nodo padre de cada nodo hasta que llegue al nodo inicial y dibuje un mapa de ruta.

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

Para utilizar el algoritmo DFS, definimos la clase DepthFirstSearch, heredando la clase laberinto. La clase DepthFirstSearch reescribe el método _search de la clase base, que es casi lo mismo que la definición de la función dfs que definimos anteriormente.

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

Muchas gracias por leer
. Cuando decidí estudiar Python en la universidad, descubrí que me comía una mala base informática. No tenía una calificación académica. Esto
no es nada que hacer. Solo puedo compensarlo, así que comencé mi propio contraataque fuera de la codificación. El camino, continúe aprendiendo los conocimientos básicos de Python, el estudio en profundidad de los conceptos básicos de la computadora, resuelto, si no está dispuesto a ser mediocre, ¡únase a mí en la codificación y continúe creciendo!
De hecho, no solo hay tecnología aquí, sino también cosas más allá de esas tecnologías. Por ejemplo, cómo ser un programador exquisito, en lugar de "seda de gallo", el programador en sí es una existencia noble, ¿no? [Haz clic para unirte] ¡ Quieres ser tú mismo, quieres ser una persona noble, vamos!

34 artículos originales publicados · Me gusta12 · Visitantes más de 20,000

Supongo que te gusta

Origin blog.csdn.net/chengxun03/article/details/105476985
Recomendado
Clasificación