一、问题描述

二、算法分析

在搜索的每一步都利用估价函数 f(n)= g(n)+h(n)对 Open 表中的节点进行排序表中的节点进行排序, 找出一个最有希望的节点作为下一次扩展的节点。且满足条 件:h(n)≤h*(n)。其中 g(n) 是在状态空间中从初始状态到状态 n 的实际代价, h(n) 是从状态 n 到目标状态的最佳路径的估计代价。

算法过程如下:

读入初始状态和目标状态,并计算初始状态评价函数值 f;

初始化两个 open 表和 closed 表,将初始状态放入 open 表中

如果 open 表为空,则查找失败;

否则:

①在 open 表中找到评价值最小的节点,作为当前结点,并放入 closed 表中;

② 判断当前结点状态和目标状态是否一致,若一致,跳出循环;否则跳转到③;

③ 对当前结点,分别按照上、下、左、右方向移动空格位置来扩展新的状态结 点,并计算新扩展结点的评价值 f 并记录其父节点;

④ 对于新扩展的状态结点,进行如下操作: A.新节点既不在 open 表中,也不在 closed 表中,则添加进 OPEN 表; B.新节点在 open 表中,则计算评价函数的值,取最小的。 C.新节点在 closed 表中,则计算评价函数的值,取最小的。

⑤ 把当前结点从 open 表中移除;

 三.深度优先算法:

(1)从图中某顶点 v 出发,访问顶点 v;

(2)依次从 v 的未被访问的邻接点出发,对图进行深度优先遍历;直至图中 和 v 有路径相通的顶点都被访问;

(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行 深度优先遍历,直到图中所有顶点均被访问过为止。

A*算法

#-*-coding:utf-8-*-import heapq
import copy
import time
import math
import argparse# 初始状态
# S0 = [[11, 9, 4, 15],
#       [1, 3, 0, 12],
#       [7, 5, 8, 6],
#       [13, 2, 10, 14]]
S0 = [[5, 1, 2, 4],[9, 6, 3, 8],[13, 15, 10, 11],[0, 14, 7, 12]]# 目标状态
SG = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 0]]# 上下左右四个方向移动
MOVE = {'up': [1, 0],'down': [-1, 0],'left': [0, -1],'right': [0, 1]}# OPEN表
OPEN = []# 节点的总数
SUM_NODE_NUM = 0# 状态节点
class State(object):def __init__(self, deepth=0, rest_dis=0.0, state=None, hash_value=None, father_node=None):'''初始化:参数 deepth: 从初始节点到目前节点所经过的步数:参数 rest_dis: 启发距离:参数 state: 节点存储的状态 4*4的列表:参数 hash_value: 哈希值,用于判重:参数 father_node: 父节点指针'''self.deepth = deepthself.rest_dis = rest_disself.fn = self.deepth + self.rest_disself.child = []  # 孩子节点self.father_node = father_node  # 父节点self.state = state  # 局面状态self.hash_value = hash_value  # 哈希值def __lt__(self, other):  # 用于堆的比较,返回距离最小的return self.fn < other.fndef __eq__(self, other):  # 相等的判断return self.hash_value == other.hash_valuedef __ne__(self, other):  # 不等的判断return not self.__eq__(other)def cal_M_distence(cur_state):'''计算曼哈顿距离:参数 state: 当前状态,4*4的列表, State.state:返回: M_cost 每一个节点计算后的曼哈顿距离总和'''M_cost = 0for i in range(4):for j in range(4):if cur_state[i][j] == SG[i][j]:continuenum = cur_state[i][j]if num == 0:x, y = 3, 3else:x = num / 4  # 理论横坐标y = num - 4 * x - 1  # 理论的纵坐标M_cost += (abs(x - i) + abs(y - j))return M_costdef cal_E_distence(cur_state):'''计算曼哈顿距离:参数 state: 当前状态,4*4的列表, State.state:返回: M_cost 每一个节点计算后的曼哈顿距离总和'''E_cost = 0for i in range(4):for j in range(4):if cur_state[i][j] == SG[i][j]:continuenum = cur_state[i][j]if num == 0:x, y = 3, 3else:x = num / 4  # 理论横坐标y = num - 4 * x - 1  # 理论的纵坐标E_cost += math.sqrt((x - i)*(x - i) + (y - j)*(y - j))return E_costdef generate_child(sn_node, sg_node, hash_set, open_table, cal_distence):'''生成子节点函数:参数 sn_node:  当前节点:参数 sg_node:  最终状态节点:参数 hash_set:  哈希表,用于判重:参数 open_table: OPEN表:参数 cal_distence: 距离函数:返回: None'''if sn_node == sg_node:heapq.heappush(open_table, sg_node)print('已找到终止状态!')returnfor i in range(0, 4):for j in range(0, 4):if sn_node.state[i][j] != 0:continuefor d in ['up', 'down', 'left', 'right']:  # 四个偏移方向x = i + MOVE[d][0]y = j + MOVE[d][1]if x < 0 or x >= 4 or y < 0 or y >= 4:  # 越界了continuestate = copy.deepcopy(sn_node.state)  # 复制父节点的状态state[i][j], state[x][y] = state[x][y], state[i][j]  # 交换位置h = hash(str(state))  # 哈希时要先转换成字符串if h in hash_set:  # 重复了continuehash_set.add(h)  # 加入哈希表# 记录扩展节点的个数global SUM_NODE_NUMSUM_NODE_NUM += 1deepth = sn_node.deepth + 1  # 已经走的距离函数rest_dis = cal_distence(state)  # 启发的距离函数node = State(deepth, rest_dis, state, h, sn_node)  # 新建节点sn_node.child.append(node)  # 加入到孩子队列heapq.heappush(open_table, node)  # 加入到堆中# show_block(state, deepth) # 打印每一步的搜索过程def show_block(block, step):print("------", step, "--------")for b in block:print(b)def print_path(node):'''输出路径:参数 node: 最终的节点:返回: None'''print("最终搜索路径为:")steps = node.deepthstack = []  # 模拟栈while node.father_node is not None:stack.append(node.state)node = node.father_nodestack.append(node.state)step = 0while len(stack) != 0:t = stack.pop()show_block(t, step)step += 1return stepsdef A_start(start, end, distance_fn, generate_child_fn):'''A*算法:参数 start: 起始状态:参数 end: 终止状态:参数 distance_fn: 距离函数,可以使用自定义的:参数 generate_child_fn: 产生孩子节点的函数:返回: 最优路径长度'''root = State(0, 0, start, hash(str(S0)), None)  # 根节点end_state = State(0, 0, end, hash(str(SG)), None)  # 最后的节点if root == end_state:print("start == end !")OPEN.append(root)heapq.heapify(OPEN)node_hash_set = set()  # 存储节点的哈希值node_hash_set.add(root.hash_value)while len(OPEN) != 0:top = heapq.heappop(OPEN)if top == end_state:  # 结束后直接输出路径return print_path(top)# 产生孩子节点,孩子节点加入OPEN表generate_child_fn(sn_node=top, sg_node=end_state, hash_set=node_hash_set,open_table=OPEN, cal_distence=distance_fn)print("无搜索路径!")  # 没有路径return -1if __name__ == '__main__':# 可配置式运行文件parser = argparse.ArgumentParser(description='选择距离计算方法')parser.add_argument('--method', '-m', help='method 选择距离计算方法(cal_E_distence or cal_M_distence)', default = 'cal_M_distence')args = parser.parse_args()method = args.methodtime1 = time.time()if method == 'cal_E_distence':length = A_start(S0, SG, cal_E_distence, generate_child)else:length = A_start(S0, SG, cal_M_distence, generate_child)time2 = time.time()if length != -1:if method == 'cal_E_distence':print("采用欧式距离计算启发函数")else:print("采用曼哈顿距离计算启发函数")print("搜索最优路径长度为", length)print("搜索时长为", (time2 - time1), "s")print("共检测节点数为", SUM_NODE_NUM)

深度优先:

#-*-coding:utf-8-*-import copy
import time# 初始状态
# S0 = [[11, 9, 4, 15],
#       [1, 3, 0, 12],
#       [7, 5, 8, 6],
#       [13, 2, 10, 14]]
S0 = [[5, 1, 2, 4],[9, 6, 3, 8],[13, 15, 10, 11],[0, 14, 7, 12]]# 目标状态
SG = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 0]]# 上下左右四个方向移动
MOVE = {'up': [1, 0],'down': [-1, 0],'left': [0, -1],'right': [0, 1]}# OPEN表
OPEN = []# 节点的总数
SUM_NODE_NUM = 0# 状态节点
class State(object):def __init__(self, deepth=0, state=None, hash_value=None, father_node=None):'''初始化:参数 deepth: gn是初始化到现在的距离:参数 state: 节点存储的状态:参数 hash_value: 哈希值,用于判重:参数 father_node: 父节点指针'''self.deepth = deepthself.child = []  # 孩子节点self.father_node = father_node  # 父节点self.state = state  # 局面状态self.hash_value = hash_value  # 哈希值def __eq__(self, other):  # 相等的判断return self.hash_value == other.hash_valuedef __ne__(self, other):  # 不等的判断return not self.__eq__(other)def generate_child(sn_node, sg_node, hash_set):'''生成子节点函数:参数 sn_node:  当前节点:参数 sg_node:  最终状态节点:参数 hash_set:  哈希表,用于判重:参数 open_table: OPEN表:返回: None'''for i in range(0, 4):for j in range(0, 4):if sn_node.state[i][j] != 0:continuefor d in ['up', 'down', 'left', 'right']:  # 四个偏移方向x = i + MOVE[d][0]y = j + MOVE[d][1]if x < 0 or x >= 4 or y < 0 or y >= 4:  # 越界了continuestate = copy.deepcopy(sn_node.state)  # 复制父节点的状态state[i][j], state[x][y] = state[x][y], state[i][j]  # 交换位置h = hash(str(state))  # 哈希时要先转换成字符串if h in hash_set:  # 重复了continuehash_set.add(h)  # 加入哈希表# 记录扩展节点的个数global SUM_NODE_NUMSUM_NODE_NUM += 1deepth = sn_node.deepth + 1  # 已经走的距离函数node = State(deepth, state, h, sn_node)  # 新建节点sn_node.child.append(node)  # 加入到孩子队列OPEN.insert(0, node)# show_block(state, deepth)def show_block(block, step):print("------", step, "--------")for b in block:print(b)def print_path(node):'''输出路径:参数 node: 最终的节点:返回: None'''print("最终搜索路径为:")steps = node.deepthstack = []  # 模拟栈while node.father_node is not None:stack.append(node.state)node = node.father_nodestack.append(node.state)step = 0while len(stack) != 0:t = stack.pop()show_block(t, step)step += 1return stepsdef DFS_max_deepth(start, end, generate_child_fn, max_deepth):'''A*算法:参数 start: 起始状态:参数 end: 终止状态:参数 generate_child_fn: 产生孩子节点的函数:参数 max_deepth: 最深搜索深度:返回: None'''root = State(0, start, hash(str(S0)), None)  # 根节点end_state = State(0, end, hash(str(SG)), None)  # 最后的节点if root == end_state:print("start == end !")OPEN.append(root)node_hash_set = set()  # 存储节点的哈希值node_hash_set.add(root.hash_value)while len(OPEN) != 0:top = OPEN.pop(0)if top == end_state:  # 结束后直接输出路径return print_path(top)if top.deepth >= max_deepth:continue# 产生孩子节点,孩子节点加入OPEN表generate_child_fn(sn_node=top, sg_node=end_state, hash_set=node_hash_set)print("设置最深深度不合适,无搜索路径!")  # 没有路径return -1if __name__ == '__main__':time1 = time.time()length = DFS_max_deepth(S0, SG, generate_child, 25)time2 = time.time()if length != -1:print("搜索最优路径长度为", length)print("搜索时长为", (time2 - time1), "s")print("共检测节点数为", SUM_NODE_NUM)

广度优先

#-*-coding:utf-8-*-import heapq
import copy
import time# 初始状态
# S0 = [[11, 9, 4, 15],
#       [1, 3, 0, 12],
#       [7, 5, 8, 6],
#       [13, 2, 10, 14]]
S0 = [[5, 1, 2, 4],[9, 6, 3, 8],[13, 15, 10, 11],[0, 14, 7, 12]]# 目标状态
SG = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 0]]# 上下左右四个方向移动
MOVE = {'up': [1, 0],'down': [-1, 0],'left': [0, -1],'right': [0, 1]}# OPEN表
OPEN = []# 节点的总数
SUM_NODE_NUM = 0# 状态节点
class State(object):def __init__(self, deepth=0, state=None, hash_value=None, father_node=None):'''初始化:参数 deepth: 从初始节点到目前节点所经过的步数:参数 state: 节点存储的状态 4*4的列表:参数 hash_value: 哈希值,用于判重:参数 father_node: 父节点指针'''self.deepth = deepthself.child = []  # 孩子节点self.father_node = father_node  # 父节点self.state = state  # 局面状态self.hash_value = hash_value  # 哈希值def __lt__(self, other):  # 用于堆的比较,返回距离最小的return self.deepth < other.deepthdef __eq__(self, other):  # 相等的判断return self.hash_value == other.hash_valuedef __ne__(self, other):  # 不等的判断return not self.__eq__(other)def generate_child(sn_node, sg_node, hash_set, open_table):'''生成子节点函数:参数 sn_node:  当前节点:参数 sg_node:  最终状态节点:参数 hash_set:  哈希表,用于判重:参数 open_table: OPEN表:返回: None'''if sn_node == sg_node:heapq.heappush(open_table, sg_node)print('已找到终止状态!')returnfor i in range(0, 4):for j in range(0, 4):if sn_node.state[i][j] != 0:continuefor d in ['up', 'down', 'left', 'right']:  # 四个偏移方向x = i + MOVE[d][0]y = j + MOVE[d][1]if x < 0 or x >= 4 or y < 0 or y >= 4:  # 越界了continuestate = copy.deepcopy(sn_node.state)  # 复制父节点的状态state[i][j], state[x][y] = state[x][y], state[i][j]  # 交换位置h = hash(str(state))  # 哈希时要先转换成字符串if h in hash_set:  # 重复了continuehash_set.add(h)  # 加入哈希表# 记录扩展节点的个数global SUM_NODE_NUMSUM_NODE_NUM += 1deepth = sn_node.deepth + 1  # 已经走的距离函数node = State(deepth, state, h, sn_node)  # 新建节点sn_node.child.append(node)  # 加入到孩子队列heapq.heappush(open_table, node)  # 加入到堆中# show_block(state, deepth) # 打印每一步的搜索过程def show_block(block, step):print("------", step, "--------")for b in block:print(b)def print_path(node):'''输出路径:参数 node: 最终的节点:返回: None'''print("最终搜索路径为:")steps = node.deepthstack = []  # 模拟栈while node.father_node is not None:stack.append(node.state)node = node.father_nodestack.append(node.state)step = 0while len(stack) != 0:t = stack.pop()show_block(t, step)step += 1return stepsdef A_start(start, end, generate_child_fn):'''A*算法:参数 start: 起始状态:参数 end: 终止状态:参数 generate_child_fn: 产生孩子节点的函数:返回: 最优路径长度'''root = State(0, start, hash(str(S0)), None)  # 根节点end_state = State(0, end, hash(str(SG)), None)  # 最后的节点if root == end_state:print("start == end !")OPEN.append(root)heapq.heapify(OPEN)node_hash_set = set()  # 存储节点的哈希值node_hash_set.add(root.hash_value)while len(OPEN) != 0:top = heapq.heappop(OPEN)if top == end_state:  # 结束后直接输出路径return print_path(top)# 产生孩子节点,孩子节点加入OPEN表generate_child_fn(sn_node=top, sg_node=end_state, hash_set=node_hash_set,open_table=OPEN)print("无搜索路径!")  # 没有路径return -1if __name__ == '__main__':time1 = time.time()length = A_start(S0, SG, generate_child)time2 = time.time()if length != -1:print("搜索最优路径长度为", length)print("搜索时长为", (time2 - time1), "s")print("共检测节点数为", SUM_NODE_NUM)

四.运行截图

五.总结

通过对比分析,可以发现,A 星算法的搜索时长和检测节点数明显小于深度优先方法,可见 启发式信息对于搜索过程的重要性;另外,有界深度优先算法的算法性能差异较大,设置不 同的最深深度得到的结果有一定的差异,一般设置较大会造成内存爆炸的现象,所以通过该 方法进行搜索较为困难,对于任务较为复杂的情况,很难快速求解。

另外,广度优先算法, 针对较为简单问题,基本可以以最短路径给出答案,但同时搜索时间和搜索节点数一定会比 启发式搜索多一些,针对复杂问题,很难给出答案,每扩展一层,都会以指数的形式增加待 扩展节点的数量,很难得出答案。

综上所述,与深度优先算法相比,启发式搜索算法有很强的优越性,一般情况下要尽可能去 寻找启发函数,添加到代码中辅助进行算法的训练,尽可能缩短程序运行时间,提高程序效 率。

十五数码难题 A*算法及深度优先算法实现相关推荐

  1. 【某航】A*算法实现十五数码问题--人工智能课程大作业

    代码链接:github代码 1.问题要求 15数码问题是在4×4方格盘上,放有15个数码,剩下一个位置为空(方便起见,用0表示空),每一空格其上下左右的数码可移至空格.本问题给定初始位置和目标位置,要 ...

  2. 某航某个大作业:十五数码A*算法,Python实现

    引言 十五数码问题来源于美国的科学魔术大师萨姆·洛伊德,洛伊德的发明其实只是将重排九宫(即八数码问题)中的3 阶方阵扩大到4 阶方阵罢了.由于这个细微的变化,十五数码问题的规模远远大于八数码问题,八数 ...

  3. 【人工智能大作业】A*和IDA*搜索算法解决十五数码(15-puzzle)问题 (Python实现)(启发式搜索)

    Astar和IDAstar搜索算法解决十五数码(15-puzzle)问题 (文末附实现代码,此前为理论与具体问题分析) 文章目录 Astar和IDAstar搜索算法解决十五数码(15-puzzle)问 ...

  4. 【转】pacs定位线_C#开发PACS医学影像处理系统(十五):Dicom影像交叉定位线算法

    转自:https://www.cnblogs.com/Uncle-Joker/p/13686618.html 1.定位线概念:某个方位的影像在另一个方向的影像上的投影相交线,例如横断面(从头到脚的方向 ...

  5. 算法迷宫 深度优先算法

    1.1关于随机迷宫生成的问题 随机迷宫生成是算法中一个典型的例子,生成一个庞大且复杂的迷宫,人们进去以后很难走出来.一般来说,利用二维数组存储一个矩形迷宫,利用广度优先算法.深度优先算法.以及随机普里 ...

  6. 浅谈网络爬虫中广度优先算法和深度优先算法

    前言 做爬虫的,最怕业务简单粗暴的来一句"爬一下XXXX网".比如,"爬一下央广网"(示例链接:http://www.cnr.cn),看着密密麻麻的各种子分类, ...

  7. 广度优先算法和深度优先算法-树形结构(层级结构)-Java

    广度优先算法和深度优先算法-树形结构(层级结构)-Java 目录 文章目录 1.前言 2.递归 3.栈+深度优先算法 4.队列+广度优先算法 5.比较 ***后记*** : 内容 1.前言 在日常应用 ...

  8. BFS广度优先算法, DFS深度优先算法,Python,队列实现,栈实现

    来源:https://www.bilibili.com/video/BV1Ks411575U/?spm_id_from=333.788.videocard.0 BFS广度优先算法 graph = {& ...

  9. python 广度优先算法和深度优先算法遍历的实现

    用这个图为例子 用字典存储这个图 graph = {'A':['C','B'],'B':['A','C','D'],'C':['A','B','E','D'],'D':['B','C','E','F' ...

最新文章

  1. AI工程师面试知识点:TensorFlow 框架
  2. 《软件设计师》——计算机组成原理与体系结构
  3. 浅谈mysql数据库引擎
  4. 基于redis(v3.2+)实现“附近的人”功能
  5. 什么叫内部银团_MOS管和IGBT管有什么区别?
  6. Android JNI(三)——JNI数据结构之JNINativeMethod
  7. 手动升级 Confluence - 规划你的升级
  8. 【渝粤教育】电大中专中药制剂学作业 题库
  9. 个人微信api接口调用,微信好友收发消息
  10. 英语期刊写作-通往国际学术舞台的阶梯第六章答案
  11. dbf文件怎么还原到oracle中,oracle dbf文件恢复数据
  12. Node+puppeteer学习笔记(五)--API问题解决--使用功能强大的“ eval ”函数
  13. Android 版本对应 Version Code
  14. 明日之后营地14庄中式风房子上线,网友:都是氪金大佬
  15. 【众说区块链】公链是否一定要发币,Token到底应该怎么理解?
  16. 牛客第一场 H XOR —— 线性基
  17. Django大咖之路: 如何对付学习Django过程中所遇到的挫败感?
  18. Yii碰到“the file or directory to be published does not exist bower/jquery/dist”
  19. 一篇文章玩转 RNN 网络 原理
  20. 2016hctf Writeup.md

热门文章

  1. 图像紫边消除(depurple)
  2. 空间实景三维激光点云数据处理服务来了!
  3. 如何删除Mysql注册列表残余文件
  4. 不是所有的努力都会有结果,但是你若不努力,谁能替你坚强??
  5. 泊松融合(Poisson blend)
  6. 四面体棱切球的一些特殊正弦定理及其几何性质
  7. mysql -u root mysql_输入命令mysql -u root -p 报错
  8. 王者荣耀英雄皮肤官方高清图片爬虫
  9. 西门子1200PLC和Modbus485从站设备通讯
  10. 高企!2022年武汉市高新技术企业奖励补贴以及申报条件汇总!