利用python求解八数码难题

来源:互联网 发布:淘宝粉星便利店靠谱么 编辑:程序博客网 时间:2024/05/01 19:13

实验目的

  • 实验内容
    八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始状态转变成目标状态的移动棋子步数最少的移动步骤。
  • 实验要求
    分别利用宽度优先搜索有序搜索算法求解八数码难题,给出搜索树,并给出从初始节点到目标节点的路径。

实验设备及软件环境

1. 电脑配置:

(1)处理器 : Intel i5-4210U CPU @ 1.70GHz, 2.40GHz
(2)安装内存: 8.00GB
(3)操作系统: Windows 10
(4)编程语言: Python
(5)软件环境: python 3.5 、numpy、matplotlib、scipy、Axure 7.0
(6)IDE : PyCharm 5.0.1

实验方法

  • 算法描述

    (1) 宽度优先搜索
    如果搜索是以接近起始节点的程度依次扩展节点的,那么这种搜索就叫做宽度优先搜索。这种搜索是逐层进行的,在对下一层的任一节点进行搜索之前,必须搜索完本层的所有节点。
    (2) 有序搜索
    f(n) 表示节点n的估价函数值,估算节点希望程度的量度。本次实验选择的f(n)的函数形式为:

    f(n)=g(n)+h(n)

    其中, g(n)为初始节点到当前节点的路径长度(深度), h(n)为当前节点“不在位”的将牌数。
    有序搜索(ordered search),即最好优先搜索, 选择Open表上具有最小f值的节点作为下一个要扩展的节点。

  • 流程图
    (1) 宽度优先搜索
Created with Raphaël 2.1.0开始把起始节点S 放入OPEN表Open表为空?失败把第一个节点(n)从Open表移至Closed表扩展n,把n的后继节点放入Open表的末端,提供返回节点n的指针是否有后继节点为目标节点?成功并结束yesnoyesno
(2) 有序搜索
Created with Raphaël 2.1.0开始把起始节点S放入OPEN表,计算估计函数f(s)Open表为空?失败并退出选取Open表中f值最小的节点i移至Closed表i为目标节点?成功并结束扩展i,得后继节点j,计算f(j),提供返回节点i的指针,利用f(j)重排Open表yesnoyesno
  • 程序代码 (python)
    (1) 宽度优先搜索
__author__ = 'ysc'import numpy as npclass State:    def __init__(self, state, directionFlag=None, parent=None):        self.state = state                # state is a ndarray with a shape(3,3) to storage the state        self.direction = ['up', 'down', 'right', 'left']        if directionFlag:            self.direction.remove(directionFlag)         # record the possible directions to generate the sub-states        self.parent = parent        self.symbol = ' '    def getDirection(self):        return self.direction    def showInfo(self):        for i in range(3):            for j in range(3):                print(self.state[i, j], end='  ')            print("\n")        print('->')        return    def getEmptyPos(self):        postion = np.where(self.state == self.symbol)        return postion    def generateSubStates(self):        if not self.direction:            return []        subStates = []        boarder = len(self.state) - 1                 # the maximum of the x,y        row, col = self.getEmptyPos()        if 'left' in self.direction and col > 0:        #it can move to left             s = self.state.copy()            temp = s.copy()            s[row, col] = s[row, col-1]            s[row, col-1] = temp[row, col]            news = State(s, directionFlag='right', parent=self)            subStates.append(news)        if 'up' in self.direction and row > 0:            #it can move to upper place            s = self.state.copy()            temp = s.copy()            s[row, col] = s[row-1, col]            s[row-1, col] = temp[row, col]            news = State(s, directionFlag='down', parent=self)            subStates.append(news)        if 'down' in self.direction and row < boarder:        #it can move to down place            s = self.state.copy()            temp = s.copy()            s[row, col] = s[row+1, col]            s[row+1, col] = temp[row, col]            news = State(s, directionFlag='up', parent=self)            subStates.append(news)        if self.direction.count('right') and col < boarder:    #it can move to right place            s = self.state.copy()            temp = s.copy()            s[row, col] = s[row, col+1]            s[row, col+1] = temp[row, col]            news = State(s, directionFlag='left', parent=self)            subStates.append(news)        return subStates    def solve(self):        # generate a empty openTable        openTable = []                         # generate a empty closeTable        closeTable = []              # append the origin state to the openTable                 openTable.append(self)            steps = 1        # start the loop        while len(openTable) > 0:                  n = openTable.pop(0)            closeTable.append(n)            subStates = n.generateSubStates()            path = []            for s in subStates:                if (s.state == s.answer).all():                    while s.parent and s.parent != originState:                        path.append(s.parent)                        s = s.parent                    path.reverse()                    return path, steps+1            openTable.extend(subStates)            steps += 1        else:            return None, Noneif __name__ == '__main__':    # the symbol representing the empty place    # you can change the symbol at here    symbolOfEmpty = ' '           State.symbol = symbolOfEmpty         # set the origin state of the puzzle    originState = State(np.array([[2, 8, 3], [1, 6 , 4], [7, symbolOfEmpty, 5]]))     # and set the right answer in terms of the origin    State.answer = np.array([[1, 2, 3], [8, State.symbol, 4], [7, 6, 5]])            s1 = State(state=originState.state)    path, steps = s1.solve()    if path:    # if find the solution        for node in path:                    # print the path from the origin to final state                      node.showInfo()        print(State.answer)        print("Total steps is %d" % steps)

(2)有序搜索算法

__author__ = 'ysc'import numpy as npclass State:    def __init__(self, state, directionFlag=None, parent=None, depth=1):        self.state = state                # state is a ndarray with a shape(3,3) to storage the state        self.direction = ['up', 'down', 'right', 'left']        if directionFlag:            self.direction.remove(directionFlag)              # record the possible directions to generate the sub-states        self.parent = parent        self.symbol = ' '        self.answer = np.array([[1, 2, 3], [8, State.symbol, 4], [7, 6, 5]])        self.depth = depth        # calculate the num of elements which are not in the proper position        num = 0        for i in range(len(state)):            for j in range(len(state)):                if self.state[i, j] != ' 'and self.state[i, j] != self.answer[i, j]:                    num += 1        self.cost = num + self.depth    def getDirection(self):        return self.direction    def showInfo(self):        for i in range(3):            for j in range(3):                print(self.state[i, j], end='  ')            print("\n")        print('->')        return    def getEmptyPos(self):        postion = np.where(self.state == self.symbol)        return postion    def generateSubStates(self):        if not self.direction:            return []        subStates = []        # the maximum of the x,y        row, col = self.getEmptyPos()        if 'left' in self.direction and col > 0:            #it can move to left place            s = self.state.copy()            temp = s.copy()            s[row, col] = s[row, col-1]            s[row, col-1] = temp[row, col]            news = State(s, directionFlag='right', parent=self, depth=self.depth+1)            subStates.append(news)        if 'up' in self.direction and row > 0:            #it can move to upper place            s = self.state.copy()            temp = s.copy()            s[row, col] = s[row-1, col]            s[row-1, col] = temp[row, col]            news = State(s, directionFlag='down', parent=self, depth=self.depth+1)            subStates.append(news)        if 'down' in self.direction and row < boarder:            #it can move to down place               s = self.state.copy()            temp = s.copy()            s[row, col] = s[row+1, col]            s[row+1, col] = temp[row, col]            news = State(s, directionFlag='up', parent=self, depth=self.depth+1)            subStates.append(news)        if self.direction.count('right') and col < boarder:            #it can move to right place                s = self.state.copy()            temp = s.copy()            s[row, col] = s[row, col+1]            s[row, col+1] = temp[row, col]            news = State(s, directionFlag='left', parent=self, depth=self.depth+1)            subStates.append(news)        return subStates    def solve(self):        # generate a empty openTable        openTable = []        # generate a empty closeTable                         closeTable = []        # append the origin state to the openTable                        openTable.append(self)        # denote the steps it travels                 steps = 0                            while len(openTable) > 0:     # start the loop            n = openTable.pop(0)            closeTable.append(n)            subStates = n.generateSubStates()            path = []            for s in subStates:                if (s.state == s.answer).all():                    while s.parent and s.parent != originState:                        path.append(s.parent)                        s = s.parent                    path.reverse()                    return path, steps+1            openTable.extend(subStates)            # sort the openTable in terms of the cost            openTable.sort(key=lambda x: x.cost)              steps += 1        else:            return None, Noneif __name__ == '__main__':    # the symbol representing the empty place    symbolOfEmpty = ' '    # you can change the symbol at here                  State.symbol = symbolOfEmpty    # set the origin state of the puzzle        originState = State(np.array([[2, 8, 3], [1, 6 , 4], [7, symbolOfEmpty, 5]]))      State.answer = np.array([[1, 2, 3], [8, State.symbol, 4], [7, 6, 5]])           s1 = State(state=originState.state)    path, steps = s1.solve()    if path:                        # if find the solution        for node in path:                # print the path from the origin to final state                         node.showInfo()        print(State.answer)        print("Total steps is %d" % steps)

实验结果

绘图软件为:Axure 7.0

  • 搜索树
    (1)DFS
    宽度优先搜索树
    (2) 有序搜索
    有序搜索搜索树
  • 搜索路径
    DFS及有序搜索的搜索路径

实验分析

  • 结果分析
    (1) 宽度优先搜索
    由实验结果可知,宽度优先搜索扩展节点个数为4,生成节点个数为26。
    (扩展节点:路径中的节点数-1;生成节点:搜索树中节点数目-1)
    (2) 有序搜索
    由实验结果可知,有序搜索扩展节点个数为4,生成节点个数为12。
    (扩展节点:路径中的节点数-1;生成节点:搜索树中节点数目-1)
    2. 方法特点
    (1) 宽度优先搜索
  • 宽度优先搜索又称广度优先搜索,“广度”一词形象地揭示了这种搜索是逐层进行的:在对下一层的任一节点进行搜索之前,必须搜索完本层的所有节点。
  • 不需要重排Open表
  • 一般只适用于求解比较简单的问题。
  • 若问题有解,宽度优先搜索一定可以求得,并且求出的是最优解。
    (2) 有序搜索
    有序搜索利用到启发式信息,对Open表中元素进行了重排,选择最有希望的节点加以扩展,搜索效率较盲目搜索大为提高。
    3. 区别
    宽度优先搜索属于盲目搜索,没有利用到启发信息,故效率较低;而有序搜索利用到节点本身的特性,将其作为启发式信息,对Open表进行重排,每一次选择都是最优的,具有贪心性质,搜索效率大为提高。

结论

综上所述,可以明显看出宽度优先搜索与有序搜索的效率差异。这启示我们在解决生活问题时,不仅仅是需要找到一个通用的(general)算法框架,因为虽然可以求出解,但是效率很低,我们更需要根据实际问题具体分析,通过问题本身,提炼出更有效的启发式信息,这样才能提高解的效率。

1 0
原创粉丝点击