Primera búsqueda en profundidad de figura

Primera búsqueda en profundidad de figura

Pregunta de viaje de caballero:

  1. En una montaña de tablero de ajedrez, una pieza de ajedrez "caballo", según la regla del "día de paseo a caballo", partiendo de una casilla, debe recorrer todos los tableros de ajedrezExactamente una vez, Llame a una secuencia de movimientos como un "recorrido"

  2. El uso del algoritmo de búsqueda de gráficos es una de las soluciones más fáciles para comprender y programar el problema del caballo.

  3. Solución:

    1. Primero, la secuencia de movimientos legales se representa como un gráfico

      1. Usa tablero de ajedrez como vértices
      2. Siga los pasos de la regla del "día de la caminata a caballo" como borde de conexión
      3. Establecer un diagrama de tablero de ajedrez al que se pueda acceder con todos los movimientos legales de cada tablero de ajedrez.
    2. Utilice el algoritmo de búsqueda de gráficos para buscar una ruta con una longitud de (fila × columna-1), que contiene cada vértice exactamente una vez

    3. La idea clave de la búsqueda en profundidad para resolver el viaje del caballero

      Si la búsqueda en profundidad a lo largo de una sola rama no puede continuar (se han realizado todos los movimientos legales), la longitud del camino no ha alcanzado el valor predeterminado (el tablero de ajedrez de 8 × 8 es 63), entonces la marca de color se borra y la capa anterior es regresó, cambie una rama para continuar explorando en profundidad

    4. Introducir una pila para registrar la ruta para facilitar la implementación de la operación de retroceso de regresar a la capa anterior

  4. El algoritmo de búsqueda de gráficos utilizado para resolver el problema del viaje del caballero es la búsqueda en profundidad primero

    La búsqueda en profundidad es buscar lo más lejos posible en una sola rama del árbol. Si no puede continuar encontrando la solución al problema, vuelva a la capa anterior y busque la siguiente rama.

    Se utiliza un algoritmo DFS para resolver el problema del viaje del caballo, que se caracteriza por visitar cada vértice una sola vez.

    Otro algoritmo DFS es más general, lo que permite acceder a los vértices repetidamente, que se pueden utilizar como base para otros algoritmos de grafos.

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-w2KN4USk-1614326500835) (C: \ Users \ 93623 \ AppData \ Roaming \ Typora \ typora-user-images \ image-20210224145518265.png)]

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-8V1VhFNp-1614326500839) (C: \ Users \ 93623 \ AppData \ Roaming \ Typora \ typora-user-images \ image-20210220175359142.png)]

def knightTour(n, path, u, limit):
    """

    :param n: 层次
    :param path: 路径
    :param u: 当前顶点
    :param limit: 搜索总深度
    :return:
    目前实现的算法,其复杂度为O(k^n),其中n是棋盘格数目
    """
    u.setColor('gray')
    # 当前顶点加入路径
    path.append(u)
    if n < limit:
        # 对所有合法移动逐一深入
        nbrList = list(u.getConnections())
        i = 0
        done = False
        while i < len(nbrList) and not done:
            # 选择白色未经过的顶点深入
            if nbrList[i].getColor() == 'white':
                # 层次加1,递归深入
                done = knightTour(n+1, path, nbrList[i], limit)
            i = i+1
        if not done:
            # 都无法完成总深度,回溯,试本层下一个顶点
            path.pop()
            u.setColor('white')
    else:
        done = True
    return done

Aspectos destacados del código anterior:

Uno es el bucle while

La segunda es una llamada recursiva.

El tercero es usar gris y blanco para asegurar una sola visita.

El código completo es el siguiente:

# coding: utf-8
# from . import graph_ccc
from GraphCode.graph_ccc import *


def genLegalMoves(x, y, bdsize):
    newMoves = []
    # 马走日8个格子
    moveOffsets = [(-1, -2), (-1, 2), (-2, -1), (-2, 1),
                   (1, -2), (1, 2), (2, -1), (2, 1)]
    for i in moveOffsets:
        newX = x + i[0]
        newY = y + i[1]
        if legalCoord(newX, bdsize) and legalCoord(newY, bdsize):
            newMoves.append((newX, newY))
    return newMoves


# 确认不会走出棋盘
def legalCoord(x, bdsize):
    if 0 <= x < bdsize:
        return True
    else:
        return False


# 构建走棋关系图
def knightGraph(bdsize):
    ktGrapth = Graph()
    # 遍历每个格子
    for row in range(bdsize):
        for col in range(bdsize):
            nodeId = posToNodeId(row, col, bdsize)
            # 单步合法走棋
            newPositions = genLegalMoves(row, col, bdsize)
            for e in newPositions:
                nid = posToNodeId(e[0], e[1], bdsize)
                # 添加边和顶点
                ktGrapth.addEdge(nodeId, nid)
    return ktGrapth


def posToNodeId(row, col, bdsize):
    """
    将坐标转化为id, row
    row和col都是从0开始的
    pos:  (0,0)(0,1)(0,2)(0,3),(0,4)
    id:    0     1     2    3     4
    :param row:
    :param col:
    :param bdsize:
    :return:
    """
    return row * bdsize + col

def orderbyAvail(n):
    resultList = []
    for v in n.getConnections():
        if v.getColor() == 'white':
            c = 0
            for w in v.getConnections():
                if w.getColor() == 'white':
                    c += 1
            resultList.append((c,v))
    resultList.sort(key=lambda x:x[0])
    return [y[1] for y in resultList]

def knightTour(n, path, u, limit):
    """
    knightTour(0, [], 4, 63)
    :param n: 层次, 是搜索树的当前深度
    :param path: 路径, 是到目前为止访问到的顶点列表
    :param u: 当前顶点, 是希望在图中访问的顶点
    :param limit: 搜索总深度, 路径上的顶点总数
    :return:
    目前实现的算法,其复杂度为O(k^n),其中n是棋盘格数目
    """
    u.setColor('gray')
    # 当前顶点加入路径
    path.append(u)
    if n < limit:
        # 对所有合法移动逐一深入
        # nbrList = list(u.getConnections())
        nbrList = list(orderbyAvail(u))
        i = 0
        done = False
        while i < len(nbrList) and not done:
            # 选择白色未经过的顶点深入
            if nbrList[i].getColor() == 'white':
                # 层次加1,递归深入
                done = knightTour(n + 1, path, nbrList[i], limit)
            i = i + 1
        if not done:
            # 都无法完成总深度,回溯,试本层下一个顶点
            path.pop()
            u.setColor('white')
    else:
        done = True
    return done


if __name__ == '__main__':
    g = knightGraph(8)
    # for i in g:
    #     print(i)

    path = []
    startVertex = g.getVertex(4)
    knightTour(0, path, startVertex, 63)
    # print(path)
    for node in path:
        print(node.getId(), end=" ")


El algoritmo actualmente implementado tiene una complejidad de O (kn) \ mathbf (O) \ left (\ mathbf (k) ^ (n) \ right)LA( kn ), donde n es el número de fichas, comoTablero de ajedrez 8 * 8, n es 64, k es el número promedio de ramas, para cada una de las 8 * 8, el número promedio de ramas es 5 (es decir, hay 5 formas de moverse en cada cuadrícula en promedio)

Este es un algoritmo de complejidad temporal exponencial, y su proceso de búsqueda se representa como un árbol con nivel n

  1. Incluso los algoritmos de complejidad de tiempo exponencial se pueden mejorar enormemente en el rendimiento real

    1. La estructura inteligente de nbrList, que organiza el orden de los vértices de una manera específica, puede reducir el tiempo de búsqueda de la ruta de viaje de la placa 8 × 8 al segundo nivel.
  2. Este algoritmo mejorado se llama algoritmo de Warnsdorff


Primera búsqueda en profundidad general

  1. El objetivo general de la búsqueda en profundidad es buscar lo más profundo posible en el gráfico, conectar tantos vértices como sea posible, ramificar si es necesario (crear árboles) y, a veces, crear varios árboles, lo que se denomina "bosque en profundidad primero".

  2. La búsqueda en profundidad también utiliza el atributo "precursor" del vértice para construir un árbol o bosque.

    1. Además, debe establecer los atributos "tiempo de descubrimiento" y "tiempo de finalización". El primero significa que el vértice se visita en los primeros pasos (establecido en gris) y el segundo se refiere a la finalización de la exploración de este vértice en los primeros pasos (establecido en negro)
    2. Estos dos nuevos atributos son muy importantes para el siguiente algoritmo gráfico
  3. El gráfico con el algoritmo DFS se implementa como una subclase de Graph

    1. Vertex agrega miembros Discovery y Finish
    2. Graph Graph agrega tiempo de miembro para registrar el número de pasos ejecutados por el algoritmo
  4. El árbol construido por DFS tiene los atributos de "tiempo de descubrimiento" y "tiempo de finalización" en sus vértices, que son similares a los paréntesis.

    1. Es decir, el "tiempo de descubrimiento" de un vértice es siempre menor que el "tiempo de descubrimiento" de todos los sub-vértices.
    2. La "hora de finalización" es mayor que la "hora de finalización" de todos los sub-vértices, y se descubre antes que el sub-vértice, y la exploración finaliza más tarde.
  5. El tiempo de ejecución de DFS también incluye dos aspectos

    1. Hay dos bucles en la función dfs, cada uno de los cuales es | V | veces, por lo que es o (| V |)
    2. El bucle en la función dfsvisit se realiza en los vértices conectados al vértice actual, y la llamada recursiva solo se realiza cuando el vértice es blanco, por lo que solo se ejecuta un paso para cada borde, por lo que es o (| E |)
    3. Sume al mismo o (| V | + | E |) que BFS

BFS usa colas para almacenar vértices a los que se accede

DFS usa llamadas recursivas e implícitamente usa la pila

# coding: utf-8

from pythonds.graphs import Graph


class DFSGraph(Graph):
    def __init__(self):
        super().__init__()
        self.time = 0

    def dfs(self):
        # 颜色的初始化
        for aVertex in self:
            aVertex.setColor('white')
            aVertex.setPred(-1)
        for aVertex in self:
            # 如果还有未包括的顶点,则键森林
            if aVertex.getColor() == 'white':
                self.dfsvisit(aVertex)

    def dfsvisit(self, startVertex):
        startVertex.setColor('gray')
        # 算法的步数加一
        self.time += 1
        startVertex.setDiscovery(self.time)
        for nextVertex in startVertex.getConnections():
            if nextVertex.getColor() == 'white':
                nextVertex.setPred(startVertex)
                # 深度优先递归访问
                self.dfsvisit(nextVertex)
        startVertex.setColor('black')
        self.time += 1
        startVertex.setFinish(self.time)

Supongo que te gusta

Origin blog.csdn.net/weixin_46129834/article/details/114137276
Recomendado
Clasificación