实验目的:
- 理解人工智能系统中搜索策略的含义。
- 熟悉盲目搜索和启发式搜索算法的实际应用。
- 结合八数码问题的建模、求解及编程语言的应用,掌握启发式搜索算法的应用。
代码:
import numpy as np
end = np.array([[1, 2, 3], [8, 0, 4], [7, 6, 5]]) # 最终节点
# 宽度优先搜索求解八数码问题
class BFS:
def __init__(self, arr, parent=None):
self.arr = arr
self.end = end
self.parent = parent
# 按顺序定义四个移动的方向
self.directions = ['left', 'up', 'right', 'down']
self.space = 0
# 打印路线
def showLine(self):
for i in range(3):
for j in range(3):
print(self.arr[i, j], end=' ')
print("\n")
print('------>')
return
# 获取零所在的位置
def getZeroPos(self):
row, col = np.where(self.arr == self.space)
return row, col
# 扩展节点
def generateSubstates(self):
subStates = []
flag = 0 # flag用来判断该节点是否有后继节点
row, col = self.getZeroPos()
for direction in self.directions:
if 'left' == direction and col > 0:
# 判断零是否能向左移动
s = self.arr.copy()
s[row, col], s[row, col - 1] = s[row, col - 1], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
# 判断扩展出来的新节点是否与其父节点的父节点相同
# 若不相同则扩展,否则不扩展
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
else:
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
if 'up' == direction and row > 0:
# 判断零是否能向上移动
s = self.arr.copy()
s[row, col], s[row - 1, col] = s[row - 1, col], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
else:
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
if 'down' == direction and row < 2:
# 判断零是否能下左移动
s = self.arr.copy()
s[row, col], s[row + 1, col] = s[row + 1, col], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
else:
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
if 'right' == direction and col < 2:
# 判断零是否能向右移动
s = self.arr.copy()
s[row, col], s[row, col + 1] = s[row, col + 1], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
else:
news = BFS(s, parent=self)
subStates.append(news)
flag += 1
return subStates, flag
# 搜索路径
def search(self):
opentable = [] # open表
closedtable = [] # closed表
opentable.append(self) # 将起始节点加入open表
generatednodes = 0
expendednodes = 0 # 生成节点个数、扩展节点个数
while len(opentable):
n = opentable.pop(0)
closedtable.append(n)
subStates, flag = n.generateSubstates() # 节点n扩展出来的子节点
generatednodes = generatednodes + len(subStates)
if flag:
expendednodes += 1
path = []
for s in subStates:
if (s.arr == s.end).all():
# 判断该子节点是否为目标节点
# 若是则返回路径
path.append(s)
while s.parent and s.parent != s0:
path.append(s.parent)
s = s.parent
path.reverse()
return path, generatednodes, expendednodes
opentable.extend(subStates)
return None, None, None
# 深度优先搜索求解八数码问题
class DFS:
def __init__(self, arr, depth, parent=None):
self.arr = arr
self.end = end
self.parent = parent
self.maxdepth = 15
self.directions = ['left', 'up', 'down', 'right']
self.space = 0
self.depth = depth
def getZeroPos(self):
row, col = np.where(self.arr == self.space)
return row, col
def showLine(self):
for i in range(3):
for j in range(3):
print(self.arr[i, j], end=' ')
print("\n")
print('------>')
return
def generateSubstates(self):
subStates = []
flag = 0
row, col = self.getZeroPos()
for direction in self.directions:
if 'left' == direction and col > 0:
s = self.arr.copy()
s[row, col], s[row, col - 1] = s[row, col - 1], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
else:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
if 'up' == direction and row > 0:
s = self.arr.copy()
s[row, col], s[row - 1, col] = s[row - 1, col], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
else:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
if 'down' == direction and row < 2:
s = self.arr.copy()
s[row, col], s[row + 1, col] = s[row + 1, col], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
else:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
if 'right' == direction and col < 2:
s = self.arr.copy()
s[row, col], s[row, col + 1] = s[row, col + 1], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
else:
news = DFS(s, self.depth + 1, parent=self)
subStates.append(news)
flag += 1
return subStates, flag
def search(self):
opentable = []
closedtable = []
opentable.append(self)
generatednodes = 0;
expendednodes = 0
while len(opentable):
n = opentable.pop(0)
closedtable.append(n)
subStates, flag = n.generateSubstates()
generatednodes = generatednodes + len(subStates)
if flag:
expendednodes += 1
path = []
for s in subStates:
if (s.arr == s.end).all():
path.append(s)
while s.parent and s.parent != s0:
path.append(s.parent)
s = s.parent
path.reverse()
return path, generatednodes, expendednodes
if s.depth < s.maxdepth:
# 判断是否超过最大搜索深度
# 若没有则将扩展的子节点加入open表头
subStates.reverse()
for node in subStates:
opentable.insert(0, node)
return None, None, None
# A*算法解决八数码难题
class Astar:
def __init__(self, arr, depth, f, parent=None):
self.arr = arr
self.end = end
self.depth = depth
self.parent = parent
self.space = 0
self.f = f # 估价函数的值
self.flag = 1
self.directions = ['left', 'up', 'right', 'down']
def h1(self, node):
# 第一种估价函数表示为节点n中不在目标状态中相应位置的数码个数
arr1 = node - Astar.end
h = len(arr1[arr1 != 0])
return h
def h2(self, node):
# 第二种估价函数表示为节点n的每一数码与其目标位置的距离总和
h = 0
for i in range(0, 9):
row1, col1 = self.getZeroPos(node, i)
row2, col2 = self.getZeroPos(self.end, i)
dis = abs(row1 - row2) + abs(col1 - col2)
h = h + dis
return h
def getZeroPos(self, node, space=0):
row, col = np.where(node == space)
return row, col
def showLine(self):
for i in range(3):
for j in range(3):
print(self.arr[i, j], end=' ')
print("\n")
print('------>')
return
@staticmethod
def sortOpentable(opentable):
# 对open表按估价函数f值的大小进行重排
i = len(opentable) - 1
while i > 0:
for j in range(0, i):
if opentable[j].f > opentable[j + 1].f:
opentable[j], opentable[j + 1] = opentable[j + 1], opentable[j]
i -= 1
return opentable
def generateSubstates(self):
subStates = [];
flag = 0
row, col = self.getZeroPos(self.arr)
for direction in self.directions:
if 'left' == direction and col > 0:
s = self.arr.copy()
s[row, col], s[row, col - 1] = s[row, col - 1], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
if self.flag == '1':
# 计算该节点的评估函数值
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
else:
if self.flag == '1':
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
if 'up' == direction and row > 0:
s = self.arr.copy()
s[row, col], s[row - 1, col] = s[row - 1, col], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
if self.flag == '1':
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
else:
if self.flag == '1':
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
if 'down' == direction and row < 2:
s = self.arr.copy()
s[row, col], s[row + 1, col] = s[row + 1, col], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
if self.flag == '1':
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
else:
if self.flag == '1':
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
if 'right' == direction and col < 2:
s = self.arr.copy()
s[row, col], s[row, col + 1] = s[row, col + 1], s[row, col]
if self.parent != None:
arr2 = s - self.parent.arr
if len(arr2[arr2 == 0]) < 9:
if self.flag == '1':
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
else:
if self.flag == '1':
f = self.depth + self.h1(s) + 1
else:
f = self.depth + self.h2(s) + 1
news = Astar(s, self.depth + 1, f, parent=self)
subStates.append(news)
flag += 1
return subStates, flag
def search(self):
opentable = []
closedtable = []
opentable.append(self)
generatednodes = 0
expendednodes = 0
while len(opentable):
n = opentable.pop(0)
closedtable.append(n)
subStates, flag = n.generateSubstates()
generatednodes = generatednodes + len(subStates)
if flag:
expendednodes += 1
path = []
for s in subStates:
if (s.arr == s.end).all():
print("%d" % generatednodes)
path.append(s)
while s.parent and s.parent != s0:
path.append(s.parent)
s = s.parent
path.reverse()
return path, generatednodes, expendednodes
opentable.extend(subStates)
opentable = self.sortOpentable(opentable)
return None, None, None
if __name__ == "__main__":
start = np.array([[1, 3, 4], [8, 0, 5], [7, 2, 6]]) # 起始节点
s0 = BFS(start)
bfs = BFS(start)
path, generatednodes, expendednodes = bfs.search()
for node in path:
node.showLine()
print("BFS生成的节点个数为:%d" % generatednodes)
print("BFS扩展的节点个数为:%d" % expendednodes)
s0 = DFS(start, 1)
bfs = DFS(start, 1)
path, generatednodes, expendednodes = bfs.search()
for node in path:
node.showLine()
print("DFS生成的节点个数为:%d" % generatednodes)
print("DFS扩展的节点个数为:%d" % expendednodes)
s0 = Astar(start, 0, 5)
AStar = Astar(start, 0, 5)
path, generatednodes, expendednodes = AStar.search()
for node in path:
node.showLine()
print("A*生成的节点个数为:%d" % generatednodes)
print("A*扩展的节点个数为:%d" % expendednodes)
三种方法,需要哪个用那个就行。