Python实现A*搜索输出八数码问题

import numpy as np
from queue import PriorityQueue, Queue
import time
# 保存了其各个格子的坐标
# 从起点状态到本状态生成过程的路径信息
# 历史移动总的代价
class StateInfo():
def __init__(self, state, goal_state, level=0, parent=None):
# 表示当前状态
self.__state = state
# 表示终点状态
self.__goal_state = goal_state
# 表示从起点状态到当前的状态的移动步数总和
self.__level = level
# 表示当前状态的移动步数+启发式算法步数的总步数
self.__heuristic_score = level
# 表示父节点
self.__parent = parent
# 表示由输入条件计算当前状态的启发式算法的得分以及赋值
self.calculate_fitness()
# 返回当前的状态的哈希值
def __hash__(self):
return hash(str(self.__state))
# 用于对状态类对象排序,按照启发式算法计算对应当前状态的得分,从小到大排序
def __lt__(self, other):
return self.__heuristic_score < other.__heuristic_score
def __eq__(self, other):
return self.__heuristic_score == other.__heuristic_score
def __gt__(self, other):
return self.__heuristic_score > other.__heuristic_score
# 取得当前状态
def get_state(self):
return self.__state
# 取得当前状态的移动步数+启发式算法步数的总步数(低步数优先)
def get_score(self):
return self.__heuristic_score
# 取得从起点状态到当前的状态的移动步数总和
def get_level(self):
return self.__level
# 取得父节点
def get_parent(self):
 return self.__parent
# 从上一个状态到这一个状态的过程里是哪个牌发生了移动的信息
def get_change_way(self):
# 如果__level==0,表示没有
if self.__level == 0:
return ""
parent = self.__parent.__state
# 用Node标记当前节点
node = self.__state
# 移动之前,空位置就是i将要移动的位置
new_index = parent.index(0)
# 移动之后,空位置就是i移动前的位置
old_index = node.index(0)
# 这就是与空位置交换了位置的那个数字牌i
the_change = node[new_index]
# 将一维信息转换为对应棋盘的行列信息
old_i, old_j = old_index // int(np.sqrt(len(node))), old_index % int(np.sqrt(len(node)))
new_i, new_j = new_index // int(np.sqrt(len(parent))), new_index %
int(np.sqrt(len(parent)))
direction = f"数码牌{the_change}往 "
if new_i - old_i == 1:
direction += "下"
elif new_i - old_i == -1:
direction += "上"
elif new_j - old_j == 1:
direction += "右"
else:
direction += "左"
return direction
# 计算曼哈顿距离 就是d(i,j)=|xi-xj|+|yi-yj|
def calculate_manhattan(self, x1, y1, x2, y2):
"""
曼哈顿距离——两点在南北方向上的距离加上在东西方向上的距离
d(i,j)=|xi-xj|+|yi-yj|。
对于一个具有X、Y方向规则布局的平面,
从一点到达另一点的距离正是在X方向上行走的距离
加上在Y方向上行走的距离就是----曼哈顿距离
"""
return abs(x1 - x2) + abs(y1 - y2)
# 打印输出 __repr__() 会返回和调用者有关的 “类名+object at+内存地址”信息
def __repr__(self):
message = self.get_change_way()
message += "\n从起点状态到本状态的移动次数为:{}\n当前状态到终点状态的分值为:
{}".format(self.__level,
self.__heuristic_score -
self.__level)
message += "\n移动后:\n"
node = np.array(self.__state).reshape(3, 3)
# 对节点的行进行遍历
for row in node:
 # 对节点的每一行的列进行遍历
for column in row:
# 如果遍历出0,那么代表这个位置是空位置
if column == 0:
# 对0表示的节点进行标记成空格
column = " "
message += "{} ".format(column)
message += "\n"
return message
# 启发式算法得到的代价步数
def calculate_fitness(self):
# 循环遍历当前状态的一个格子,把他当作当前的格子
for cur_tile in self.__state:
# 给cur_idx赋值当前状态下这个格子的下标位置
cur_idx = self.__state.index(cur_tile)
# 给goal_idx赋值终点状态下这个格子的下标位置
goal_idx = self.__goal_state.index(cur_tile)
# 在N阶矩阵中得到表当前格子的行列位置坐标(cur_i, cur_j )
cur_i, cur_j = cur_idx // int(np.sqrt(len(self.__state))), cur_idx %
int(np.sqrt(len(self.__state)))
# 终点格子在N*N表示下的行列位置
goal_i, goal_j = goal_idx // int(np.sqrt(len(self.__state))), goal_idx %
int(np.sqrt(len(self.__state)))
# 当前状态的启发式算法得分即当前状态里所有格子转移到对应终点状态的最小移动步骤和
self.__heuristic_score += self.calculate_manhattan(cur_i, cur_j, goal_i, goal_j)
# 1.运行主函数main之后,首先开始调用构造函数 solver = Solver(init_state, goal_state)
class Solver():
def __init__(self, init_state, goal_state, max_iter=2500):
# 起点状态
self.__init_state = init_state
# 终点状态
self.__goal_state = goal_state
# 探索出发状态队列的容量
self.__MAX = 100000
# 对探索的状态总数的最大值设置
self.__max_iter = max_iter
# 用于存放从起点状态到终点状态经过的记录
self.__path = []
# 输出算法的最后结果
self.__summary = ""
# 取得探索的状态总数的最大值
def set_max_iter(self, max_iter):
self.__max_iter = max_iter
# 取得存放从起点状态到终点状态经过的记录
def get_path(self):
return self.__path
 # 取得算法最后的结果
def get_summary(self):
return self.__summary
# 2.然后第二步开始调用path = solver.solve_a_star()
def solve_a_star(self):
# x轴
x_axis = [1, 0, -1, 0]
# y轴
y_axis = [0, 1, 0, -1]
# 标记搜索时0移动的步骤数
level = 0
# 保存已经访问的结点
visited_nodes = set()
# 记录起始时间
start_time = time.perf_counter()
# 优先队列使用nodes标记
nodes = PriorityQueue(self.__MAX)
# 当结点加入队列的时候会自动排序,每次新的探索都是从历史代价最小的状态出发进行探索
init_node = StateInfo(self.__init_state.flatten().tolist(),
self.__goal_state.flatten().tolist(), level,
parent=None)
# 将起点状态结点加入队列
nodes.put(init_node)
# 次数=0
epochs = 0
# # qsize()函数返回队列的结点数,探索的深度小于人为设定的最大次数时
while nodes.qsize() and epochs <= self.__max_iter:
epochs += 1 # 探索新状态的次数+1
# 结点出队
cur_node = nodes.get()
cur_state = cur_node.get_state()
# 判断当前的状态是否是已经出现过的
if str(cur_state) in visited_nodes:
# 若是则跳过本轮循环
continue
visited_nodes.add(str(cur_state))
# 当这个状态是想要的终点结果时,输出最终的结果和过程
if cur_state == self.__goal_state.flatten().tolist():
self.__summary = str(
"A* 搜索算法移动了 " + str(cur_node.get_level()) + " 步,一共进行了 " +
str(epochs) + " 个状态, 一共用时为 " + str(
np.round(time.perf_counter() - start_time, 4)) + " 秒")
while cur_node.get_parent():
self.__path.append(cur_node)
cur_node = cur_node.get_parent()
break
# 在本算法中,0表示空,这里得到的就是空的格子在八数码棋盘转为一维数组表示时对应的下标
 empty_tile = cur_state.index(0)
# 从刚刚得到的下标,得到空位置,也就是0在八数码棋盘的行列坐标
i, j = empty_tile // self.__goal_state.shape[0], empty_tile %
self.__goal_state.shape[0]
cur_state = np.array(cur_state).reshape(self.__goal_state.shape[0],
self.__goal_state.shape[0])
# zip() 将对象中对应的元素打包成一个个元组,返回由这些元组组成的对象
for x, y in zip(x_axis, y_axis):
# 拷贝当前状态
new_state = np.array(cur_state)
# i , j 分别为当前状态空位置的行列下标
# 进行边界条件判断
if i + x >= 0 and i + x < self.__goal_state.shape[0] and j + y >= 0 and j + y <
self.__goal_state.shape[
0]:
# 空位置与其他位置的互换
new_state[i, j], new_state[i + x, j + y] = new_state[i + x, j + y],
new_state[i, j]
# 进行空格子位置互换的状态
game_state = StateInfo(new_state.flatten().tolist(),
self.__goal_state.flatten().tolist(),
cur_node.get_level() + 1, cur_node)
# 这个新状态是没有出现过的
if str(game_state.get_state()) not in visited_nodes:
# 就将这个状态加入优先队列nodes
nodes.put(game_state)
if epochs > self.__max_iter:
print('无法实现八数码问题,请重新制定把数码牌的起点状态和终点状态')
return reversed(self.__path)
def main():
# 起点状态
init_state = [1, 8, 7, 3, 0, 5, 4, 6, 2]
# 终点状态
goal_state = [1, 2, 3, 4, 5, 6, 7, 8, 0]
n = 3 # 表示3*3的八数码格式
print("\n\n**************A*搜索算法八数码问题**************\n\n")
print("起点状态")
for i in range(3):
# reshap(i,j)指的是将一维的init_state转变为i行j列
print(np.array(init_state).reshape(3, 3)[i, :])
print()
print("终点状态")
for i in range(3):
# reshap(i,j)指的是将一维的init_state转变为i行j列
print(np.array(goal_state).reshape(3, 3)[i, :])
print()
# 初始化起点与终点
init_state = np.array(init_state).reshape(n, n)
goal_state = np.array(goal_state).reshape(n, n)
# 传入起点和终点的参数
solver = Solver(init_state, goal_state)
path = solver.solve_a_star()
for node in path:
print(node)
print()
# 输出A*算法的总结
print(solver.get_summary())
# 主函数
main()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值