人工智能--智能搜索,使用图搜索算法求解八数码问题,搜索策略可选择深度优先或宽度优先。

实验目的:

  1. 理解人工智能系统中搜索策略的含义。
  2. 熟悉盲目搜索和启发式搜索算法的实际应用。
  3. 结合八数码问题的建模、求解及编程语言的应用,掌握启发式搜索算法的应用。

代码:

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)

 三种方法,需要哪个用那个就行。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值