当前位置:   article > 正文

骑士巡游问题:常规解法与启发式方法优化_启发式搜索在解决骑士环游问题上的优势

启发式搜索在解决骑士环游问题上的优势

The knight’s tour puzzle is played on a chess board with a single chess piece, the knight. The object of the puzzle is to find a sequence of moves that allow the knight to visit every square on the board exactly once. One such sequence is called a “tour.” The knight’s tour puzzle has fascinated chess players, mathematicians and computer scientists alike for many years. The upper bound on the number of possible legal tours for an eight-by-eight chessboard is known to be 1.305×10351.305×1035; however, there are even more possible dead ends. Clearly this is a problem that requires some real brains, some real computing power, or both.

Although researchers have studied many different algorithms to solve the knight’s tour problem, a graph search is one of the easiest to understand and program. Once again we will solve the problem using two main steps:

Represent the legal moves of a knight on a chessboard as a graph.
Use a graph algorithm to find a path of length rows×columns−1rows×columns−1 where every vertex on the graph is visited exactly once.
解题代码:

"""
knight tour problem
"""
from pythonds.graphs import Graph


class KnightProblem(object):
    """
    solution class
    bd_size: 方阵棋盘的边长(以格数计数)
    step: knight的步伐(移动方式)
    """

    def __init__(self, bd_size):
        self.bd_size_ = bd_size
        self.knight_graph_ = Graph()
        self.step_ = [(-2, -1), (-2, 1),
                      (-1, -2), (-1, 2),
                      (1, -2), (1, 2),
                      (2, -1), (2, 1)]

    def build_knight_graph(self):
        """
        构建 knight graph
        """
        for row in range(self.bd_size_):
            for col in range(self.bd_size_):
                current_id = self.gener_node_id(row, col)
                for nid in self.get_next_nodes(row, col):
                    self.knight_graph_.addEdge(current_id, nid)

    def gener_node_id(self, row, col):
        """
        根据当前所在行、列生成该结点的序号
        算法:序号 = row * bd_size + col
        """
        return row * self.bd_size_ + col

    def get_next_nodes(self, row, col):
        """
        获取从当前所在结点的下一个合法移动所到达的结点
        """
        next_nodes = []
        for move in self.step_:
            new_row = row + move[0]
            new_col = col + move[1]
            if self.is_legal_node(new_row, new_col):
                nid = self.gener_node_id(new_row, new_col)
                next_nodes.append(nid)
        return next_nodes

    def is_legal_node(self, row, col):
        """
        判断此结点是不是一个合法结点
        算法:row/col > 0 and < bd_size
        """
        if row < 0 or row > self.bd_size_:
            return False
        elif col < 0 or col > self.bd_size_:
            return False
        else:
            return True

    def dfs(self, current_vertex, vertex_path, current_depth=0):
        """
        深度优先搜索
        recursion
        """
        current_vertex.setColor('gray')
        vertex_path.append(current_vertex)

        # base case
        if current_depth < self.bd_size_ * self.bd_size_ - 1:
            done = False
            # 获取当前结点的所有相邻结点
            i = 0
            # 常规解法,不采取启发式优化,找出遍历8*8规格的棋盘,
            #一般笔记本可能需要半小时
            # nbrs = list(current_vertex.getConnections())
            # 采用启发式优化,则在1s内完成
            nbrs = self.order_by_avail(current_vertex)
            while not done and i < len(nbrs):
                if nbrs[i].getColor() == 'white':
                    done = self.dfs(nbrs[i], vertex_path, current_depth + 1)
                i += 1
            if not done:  # perparing to trace back
                vertex_path.pop()
                current_vertex.setColor('white')
        else:
            done = True
        return done

    def order_by_avail(self, current_vertex):
        """
        启发式优化
        根据各邻近结点的未访问子节点数 n_avail
        对当前结点的邻近结点进行ascending排序
        """
        res_list = []
        # nbr为当前结点的邻近结点
        for nbr in current_vertex.getConnections():
            if nbr.getColor() == 'white':
                c = 0
                for n in nbr.getConnections():
                    if n.getColor() == 'white':
                        c += 1
                res_list.append((nbr, c))
        res_list.sort(key=lambda x:x[1])
        return [y[0] for y in res_list]


if __name__ == '__main__':
    kf_graph = KnightProblem(8)
    kf_graph.build_knight_graph()
    path = []
    print(kf_graph.dfs(kf_graph.knight_graph_.getVertex(0), path))
    print(path)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118

启发式优化的思想是:让knight一开始尽量绕着棋盘的边缘游走。
因为棋盘边缘的可走路径少(2或3),而棋盘中间可走路径多(8),因此能先遍历边缘结点,使迭代次数减少。到了knight游走后期,虽然knight不得不走到棋盘中间,但因为边缘的结点已被遍历过(游戏规则是一个结点只能遍历一次),此时即使在棋盘中间,可走的路径也大大减少了。通过人类的智慧,启发式方法的达到了极其优化的效果。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/573516
推荐阅读
相关标签
  

闽ICP备14008679号