八数码问题的深搜和广搜解法 | Python实现
八数码问题简介
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。
要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
数据结构
为了记录每个每一步走的状态,我们需要自己定义一个棋盘类Board
(类似结点)。
我们主要用到的属性有两个:前驱棋盘self.pre
和步数self.steps
。
class Board:
首先需要定义棋盘的构造方法
- 初始化它的前驱为空
- 初始化它的棋盘矩阵
- 同时更新它的步数(在一开始实例化的时候步数为0,因为前驱结点为空)
1
2
3
4
5
6
7
8
9
10
11# 初始化棋盘状态,matrix为棋盘的矩阵
def __init__(self, matrix):
self.pre = None
self.matrix = matrix
self.update_steps()
# 根据父结点的步数来计算当前结点走的步数
def update_steps(self):
if self.pre:
self.steps = self.pre.steps + 1
else:
self.steps = 0定义打印棋盘结点的方式
- 一行行打印即可
1
2
3
4
5
6
7
8# 打印棋盘
def show(self):
res = []
res.append("第" + str(self.steps)+"步:\n")
for i in range(3):
res.append(" ".join([str(j) for j in self.matrix[i]]) + "\n")
res.append("-" * 20+"\n")
return res从当前整个路径的根节点开始打印
- 不断找前驱结点,把每个棋盘的打印放入列表中
1
2
3
4
5
6
7
8
9# 打印从根节点开始到当前节点的路径
def show_path(self):
path = []
node = self
while node:
path = node.show()+path
node = node.pre
path = ["共需要"+str(self.steps)+"步\n"] + path
return path定位0的方法
- 返回0所在横坐标和纵坐标
1
2
3
4
5
6# 找到0当前在棋盘中的位置
def find_zero(self):
for i in range(3):
for j in range(3):
if self.matrix[i][j] == 0:
return i, j获取下一步可能的所有棋盘矩阵
- 先找到0的位置
- 然后尝试把0和上下左右的棋子交换位置,如果合法,就把可能的矩阵添加到数组中
- 最后返回一个三维的数组
1
2
3
4
5
6
7
8
9
10
11
12
13# directions数组用来记录0可能的走的四个方向
directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]
# 下一步可能的棋盘
def next_possible_boards(self):
res = []
i, j = self.find_zero()
for direction in self.directions:
x, y = direction
if i + x >= 0 and i + x <= 2 and j + y >= 0 and j + y <= 2:
next_board = copy.deepcopy(self.matrix)
next_board[i][j], next_board[i + x][j +y] = next_board[i + x][j + y], next_board[i][j]
res.append(next_board)
return res将棋盘序列化,即把棋盘的矩阵转成字符串
- 这是为了在遍历的时候更加方便地实现去重
1
2
3
4
5
6# 将当前棋盘序列化,二维数组=>字符串
def serialize(self):
tmp = ""
for i in self.matrix:
tmp += "".join([str(j) for j in i])
return tmp判断是否到达目标状态
- 直接将当前棋盘序列化后和
123804765
对比
- 直接将当前棋盘序列化后和
文件I/O
读取文件
1
2
3
4
5def read_file(file_name):
lines = []
with open(file_name, encoding="utf-8") as f:
lines = f.readlines()
return lines将从文件中读取的内容转换成矩阵
1
2
3
4
5
6
7def get_matrix(lines):
matrix = []
for line in lines:
line = line.strip()
tmp = [int(i) for i in line.split(" ")]
matrix.append(tmp)
return matrix写文件
1
2
3
4def write_file(file_name, lines):
with open(file_name, 'w', encoding="utf-8") as f:
for line in lines:
f.write(str(line))
BFS
- 整体流程
BFS_8puzzle.py
- 算法实现
- 用一个数组去模拟队列,每一次遍历一层
- 遍历的时候,获取每个可能的棋盘的下一步棋盘,添加到下一次遍历的队列里
- 为了避免重复遍历,我们定义了一个
visited
集合,里面存储了已经遍历过的可能性的棋盘序列
1 | def bfs(b): |
我也定义了一个函数,较清晰地展示每一层的情况
1 | def show_cur_level(queue, level): |
DFS
- 整体流程
- 算法实现
- 用一个递归函数来实现每次向下遍历
- 设定最大递归深度为10
- 同样的,利用
visited
集合来避免一条路上走回头路 - 向下遍历完之后运用回溯法的思想,消除回头路的证据
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ethanloo's!
评论