搜索算法解决八数码问题

一、实验描述

  1. 实验目的:
    (1)熟悉人工智能系统中的问题求解过程;
    (2)熟悉状态空间中的盲目搜索策略;
    (3)掌握盲目搜索算法,重点是宽度优先搜索和深度优先搜索算法。
  2. 实验内容:
    用Python语言编程,采用宽度优先搜索和深度优先搜索方法,求解8数码问题
    (1)采用宽度优先算法,运行程序,要求输入初始状态
    假设给定如下初始状态 S 0 S_0 S0
    2 8 3
    1 6 4
    7 0 5
    和目标状态 S g S_g Sg
    2 1 6
    4 0 8
    7 5 3
    验证程序的输出结果,写出心得体会。
    (2)对代码进行修改(选作),实现深度优先搜索求解该问题
    提示:每次选扩展节点时,从数组的最后一个生成的节点开始找,找一个没有被扩展的节点。这样也需要对节点添加一个是否被扩展过的标志。

二、涉及知识点

  1. 问题的状态空间表示
    (1)状态
    事物是运动、变化的,为描述问题的运动,变化,定义一组变量描述问题的变化特征和属性。形式化表示: ( s 1 , s 2 , . . . , s i , . . . , S − n ) (s_1,s_2,...,s_i,...,S-n) (s1,s2,...,si,...,Sn)对每一个分量都给以确定的值时,就得到了一个具体的状态。
    (2)操作符(Operator)
    也称为算符,它是把问题从一种状态变换为另一种状态的手段。操作可以是一个机械步骤,一个运算,一条规则或一个过程。操作可理解为状态集合上的一个函数,它描述了状态之间的关系。
    (3)状态空间
    用来描述一个问题的全部状态以及这些状态之间的相互关系。常用一个三元组表示为 ( S , F , G ) (S,F,G) (S,F,G)
    其中:
    S为问题的所有初始状态的集合
    F为操作(函数、规则等)的集合
    G为目标状态的集合
    (4)状态空间图
    状态空间的有向图表示:
    结点(节点):节点表示问题的 状态
    弧(有向边):标记操作符;可能的路径代价
    (5)状态空间法求解问题的基本过程
    首先为问题选择适当的“状态”及“操作”的形式化描述方法;然后从某个初始状态出发,每次使用一个满足前提条件的“操作”,且此操作产生了新的状态,递增地建立起操作序列,直到达到目标状态为止。
  2. 状态空间地盲目搜索
    (1)基本思想:
    先把问题地初始状态作为当前扩展节点对其进行扩展,生成一组子节点,然后检查问题地目标状态是否出现在这些子结点中。若出现,则搜索成功,找出了问题地解;若没出现,则再按照某种搜索策略从已生成的子结点中选择一个节点作为当前扩展节点。重复上述过程,知道目标状态出现在子结点中或者没有可供操作的节点为止。所谓对一个节点进行“扩展”是指对该节点用某个可用操作进行作用,生成该节点的一组子节点。
    (2)扩展节点
    对某一节点(状态),选择合适的操作符作用在节点上,使产生后继状态(子节点)的操作。类似数据结构中的寻找邻接点,但这里的邻接点使选择操作后产生的。
    (3)Open表和Closed表
    这两个表用来存放节点,Open表存放未扩展节点,Closed表存放已扩展节点和待扩展节点。两个表的结构可以相同。
  3. 宽度优先搜索(BFS)
    (1)基本思想:
    从初始节点S开始,依据到S的深度,逐层扩展节点并考察其是否目标节点。在第n层节点没有完全扩展之前,不对第n+1层节点进行扩展。即:Open表排序策略为新产生的节点放到Open表的末端。
    (2)状态空间广度优先搜索
    a. 把初始节点 S 0 S_0 S0 放入Open表中;
    b. 如果Open表为空,则问题无解,失败退出;
    c. 把Open表的第一个节点取出放入Closed表,并记该节点为n;
    d. 考察节点n是否为目标节点。若是,则得到问题的解,成功退出;
    e.若节点n不可扩展,则转第b步;
    f. 扩展节点n,将其子节点放入Open表的尾部,并为每一个子节点设置指向父节点的指针,然后转第b步。
  4. 深度优先搜索(DFS)
    (1)基本思想
    从初始节点S开始,优先扩展最新产生的节点(最深的节点)。即:Open表的排序策略为新产生的节点放入Open表的前端,优先扩展。
    (2)状态空间深度优先搜索
    a. 把初始节点 S 0 S_0 S0 放入Open表中;
    b. 如果Open表为空,则问题无解,失败退出;
    c. 把Open表的第一个节点取出放入Closed表,并记该节点为n;
    d. 考察节点n是否为目标节点。若是,则得到问题的解,成功退出;
    e. 若节点n不可扩展,则转第b步;
    f. 扩展节点n,将其子节点放入Open表的首,并为每一个子节点设置指向父节点的指针,然后转第b步。

三、实验步骤

系统给出该实验部分代码,需要用户补全
用户补全代码示例:

查看子状态中有没有最终状态,如果有则输出之前的父状态到path中,输出step+1

     ###########开始1#############
     for s in subStates:
         if (s.state == s.answer).all():#all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。
             path = []#存放父状态地方
             while s.parent and s.parent != s1:
                 path.append(s.parent)
                 s = s.parent
             path.reverse()#reverse() 函数用于反向列表中元素。
             return path, steps+1
     ###########结束1#############
     # 将子状态添加到openTable中
     ###########开始2#############
     openTable.extend(subStates)
     steps += 1
     ###########结束2#############

用户点击运行,系统返回运行结果给用户,如下:
测试输入:
2 8 3
1 0 4
7 6 5
输出:
2 8 3
1 0 4
7 6 5
->
2 0 3
1 8 4
7 6 5
->
0 2 3
1 8 4
7 6 5
->
1 2 3
0 8 4
7 6 5
->
[[1 2 3]
[8 0 4]
[7 6 5]]
Total steps is 27

代码实现

import numpy as np

class 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


    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:#向左移动
            s = self.state.copy()
            #标志位symbol=0向左移动,产生新的状态节点,加入到subStates中
            
            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:
            s = self.state.copy()
            # 标志位symbol=0向上移动,产生新的状态节点,加入到subStates中
            
            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()
            # 标志位symbol=0向下移动,产生新的状态节点,加入到subStates中
            
            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()
            # 标志位symbol=0向右移动,产生新的状态节点,加入到subStates中
            
            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)
         
         
            

        #end1
        return subStates

    def BFS(self):
        # generate a empty openTable
        openTable = []  #存放状态的地方
        # append the origin state to the openTable
        openTable.append(self)#将初始状态加入
        steps = 1 #步骤
        while len(openTable) > 0:
            n = openTable.pop(0)#pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。   
            subStates = n.generateSubStates()

            # 查看子状态中有没有最终状态,如果有则输出之前的父状态到path中,输出step+1
            ###########开始1#############
            path = []
            for subState in subStates:
                if np.array_equal(subState.state, State.answer):
                    while subState.parent and subState.parent != self:
                        path.append(subState.parent)
                        subState = subState.parent
                    path.reverse()
                    return path, steps+1
               
            ###########结束1#############

            # 将子状态添加到openTable中
            ###########开始2#############
            openTable.extend(subStates)
            steps += 1
           
           
            ###########结束2#############
        else:
            return None, None


State.symbol = 0
State.answer = np.array([[1, 2, 3], [8, 0, 4], [7, 6, 5]])
s1 = State(np.array([[2, 8, 3], [1,6 , 4], [7, 0, 5]]))
path, steps = s1.BFS()
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)

  • 26
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用Python实现启发式搜索算法解决八数码问题的步骤: 1.定义状态类,包含当前状态、父状态、深度、评估函数值等属性。 2.定义评估函数,用于评估当前状态到目标状态的距离。 3.定义启发式搜索函数,使用优先队列存储状态,每次取出评估函数值最小的状态进行扩展,直到找到目标状态或者队列为空。 4.定义状态扩展函数,生成当前状态的所有可能子状态。 5.判断初始状态是否有解,若无解则无法进行搜索。 6.使用Pygame库实现图形化界面,方便用户进行交互。 以下是一个简单的Python代码示例,实现了八数码问题的启发式搜索算法: ```python import pygame from queue import PriorityQueue # 定义状态类 class State: def __init__(self, state, parent, depth, f): self.state = state self.parent = parent self.depth = depth self.f = f def __lt__(self, other): return self.f < other.f # 定义评估函数 def heuristic(state, target): distance = 0 for i in range(3): for j in range(3): if state[i][j] != target[i][j]: distance += 1 return distance # 定义状态扩展函数 def expand(state): states = [] for i in range(3): for j in range(3): if state[i][j] == 0: if i > 0: new_state = [row[:] for row in state] new_state[i][j], new_state[i-1][j] = new_state[i-1][j], new_state[i][j] states.append(new_state) if i < 2: new_state = [row[:] for row in state] new_state[i][j], new_state[i+1][j] = new_state[i+1][j], new_state[i][j] states.append(new_state) if j > 0: new_state = [row[:] for row in state] new_state[i][j], new_state[i][j-1] = new_state[i][j-1], new_state[i][j] states.append(new_state) if j < 2: new_state = [row[:] for row in state] new_state[i][j], new_state[i][j+1] = new_state[i][j+1], new_state[i][j] states.append(new_state) return states # 定义启发式搜索函数 def a_star(start, target): queue = PriorityQueue() queue.put(State(start, None, 0, heuristic(start, target))) visited = set() while not queue.empty(): current = queue.get() if current.state == target: path = [] while current: path.append(current.state) current = current.parent return path[::-1] visited.add(str(current.state)) for state in expand(current.state): if str(state) not in visited: queue.put(State(state, current, current.depth+1, current.depth+1+heuristic(state, target))) return None # 判断初始状态是否有解 def solvable(state): inversion = 0 for i in range(9): for j in range(i+1, 9): if state[i//3][i%3] and state[j//3][j%3] and state[i//3][i%3] > state[j//3][j%3]: inversion += 1 return inversion % 2 == 0 # 初始化Pygame pygame.init() size = width, height = 300, 300 screen = pygame.display.set_mode(size) pygame.display.set_caption("八数码问题") # 定义颜色 white = (255, 255, 255) black = (0, 0, 0) gray = (128, 128, 128) # 定义字体 font = pygame.font.SysFont("Arial", 20) # 定义方块 class Block: def __init__(self, value, row, col): self.value = value self.row = row self.col = col def draw(self): rect = pygame.Rect(self.col*100, self.row*100, 100, 100) pygame.draw.rect(screen, white, rect) pygame.draw.rect(screen, black, rect, 2) if self.value: text = font.render(str(self.value), True, black) text_rect = text.get_rect(center=rect.center) screen.blit(text, text_rect) # 定义游戏主循环 def main_loop(): start_state = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] target_state = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() return if event.type == pygame.MOUSEBUTTONUP: pos = pygame.mouse.get_pos() row, col = pos[1]//100, pos[0]//100 if start_state[row][col] == 0: continue for state in a_star(start_state, target_state): for i in range(3): for j in range(3): if state[i][j] == start_state[row][col]: start_state[row][col], start_state[i][j] = start_state[i][j], start_state[row][col] break draw_board(start_state) pygame.display.flip() pygame.time.wait(100) if start_state == target_state: text = font.render("You Win!", True, black) text_rect = text.get_rect(center=(width//2, height//2)) screen.blit(text, text_rect) pygame.display.flip() pygame.time.wait(1000) start_state = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] draw_board(start_state) pygame.display.flip() # 定义绘制游戏界面函数 def draw_board(state): screen.fill(gray) for i in range(3): for j in range(3): block = Block(state[i][j], i, j) block.draw() # 判断初始状态是否有解 if not solvable(start_state): print("无解") else: draw_board(start_state) pygame.display.flip() main_loop() ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十有久诚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值