【如何用人工智能玩拼图游戏】八数码难题|Python|知识表示|状态空间法|深度优先算法|广度优先算法|无信息搜索|启发式函数|A*搜索算法

本次实验围绕求解八数码问题展开,将问题通过状态空间法进行知识表示,并对比分析了深度优先算法、广度优先算法和基于不同启发式函数的A*搜索算法的实现方案。思维导图如下:



Oops! You forgot to select a pdf file.



完整代码如下:

import time
Statu_saving= {}#用来表名当前状态和前一个状态,便于回溯
MoveString = {}#存储每个可能发生的动作
Movement_Output =[]#输出动作的结果
Statu_distance={}#记录当前状态到目标状态的距离
Fn= {}#总代价函数

class MoveStruct: #定义状态结构 xxf
    def __init__(self):
        self.NextLoc = [];
        self.Movement ={};

Moveable=MoveStruct()
Moveable.NextLoc=[[1,3],[0, 2, 4],[1, 5],[0,4,6],[1,3,5,7],[2,4,8],[3,7],[4,6,8],[5,7]]
Movement={0:[0,"Left",0,"Up"], 1:["Right",0, "Left",0,"Up"],2:[0,"Right",0,0,0, "Up"],
                    3:["Down",0,0,0,"Left",0,"Up"],4:[0,"Down",0,"Right",0,"Left",0,"Up"],5:[0,0,"Down",0,"Right",0,0,0,"Up"],
                  6:[0,0,0,"Down",0,0,0,"Left"],  7:[0,0,0,0,"Down",0,"Right",0,"Left"],8:[0,0,0,0,0,"Down",0,"Right"]}

def swap_chr(a, i, j):#无信息搜索算法:用于移动位置的交换函数 xxf
    if i > j:
        i, j = j, i
    b = a[:i] + a[j] + a[i+1:j] + a[i] + a[j+1:]#切片方法,返回交换后的状态
    return b

def swap_chr_A(a, i, j, distance, destLayout):#A*(Distance)xxf
    if i > j:
        i, j = j, i
    b = a[:i] + a[j] + a[i+1:j] + a[i] + a[j+1:]
    fn = distance+Distance_Compute(b, destLayout) #fn,储存代价值
    return b, fn

def swap_chr_a(a, i, j, distance, destLayout):#A*(Number)xxf
    if i > j:
        i, j = j, i
    b = a[:i] + a[j] + a[i+1:j] + a[i] + a[j+1:]
    fn = distance+Number_Compute(b, destLayout) #fn,储存代价值
    return b, fn

def Distance_Compute(srcLayout,destLayout):#基于曼哈顿距离计算代价,返回代价值 xxf
    sum=0
    a= srcLayout.index("0")#排除空格的下标位置,防止干扰
    for i in range(0,9):#循环其他每个位置
        if i!=a:
            sum=sum+abs(i-destLayout.index(srcLayout[i]))#计算当前状态和目标状态的代价
    return sum

def Number_Compute(srcLayout,destLayout):#返回错码和正确码错位个数之和 xxf
    sum=0
    a= srcLayout.index("0")
    for i in range(0,9):
        if i!=a:
            if(srcLayout[i]!=destLayout[i]):#如果发生错位
                 sum+=1#计算当前状态和目标状态的代价
    return sum

def judge(srcLayout, destLayout):# 判断起始状态是否能够到达目标状态 xxf
    src = 0
    dest= 0
    for i in range(1, 9):
        fist = 0
        for j in range(0, i):
            if srcLayout[j] > srcLayout[i] and srcLayout[i] != '0':
                fist = fist + 1
        src = src + fist
    for i in range(1, 9):
        fist = 0
        for j in range(0, i):
            if destLayout[j] > destLayout[i] and destLayout[i] != '0':
                fist = fist + 1
        dest = dest + fist
    if (src % 2) != (dest % 2):  # 同奇同偶时才可达的
        return -1, None
    else:return 0

def solvePuzzle_DFS(srcLayout, destLayout):#深度优先搜索算法 xxf
    Statu_saving[srcLayout] = -1 #初始化字典
    Answer_Solving = []
    Answer_Solving.append(srcLayout)#当前状态存入列表

    while len(Answer_Solving) > 0: #如果栈中存在状态
        curLayout = Answer_Solving.pop()#出栈
        if curLayout == destLayout:#判断当前状态是否为目标状态
            break#如果是目标状态,则出栈

        ind_slide = curLayout.index("0")#获得空格位置的下标,
        lst_shifts = Moveable.NextLoc[ind_slide]#当前可进行交换的位置集合
        for nShift in lst_shifts:
            newLayout = swap_chr(curLayout, nShift, ind_slide)#进行交换
            if Statu_saving.get(newLayout) == None:#判断交换后的状态是否没有查询过
                Statu_saving[newLayout] = curLayout#如果状态没有查询过,则将其存到字典里面,
                Answer_Solving.append(newLayout)#存入集合
                MoveString[newLayout]=Movement[ind_slide][nShift]#每一个动作存在对应的MoveString中
                if newLayout == destLayout:  # 判断当前状态是否为目标状态
                    break  # 如果是目标状态,则出栈

def solvePuzzle_BFS(srcLayout, destLayout):#广度优先搜索算法BFS xxf
    Statu_saving[srcLayout] = -1
    Answer_Solving = []
    Answer_Solving.append(srcLayout)

    while len(Answer_Solving) > 0:
        curLayout = Answer_Solving.pop(0) #跟DFS一样,只需将此处修改为队列实现
        if curLayout == destLayout:
            break

        ind_slide = curLayout.index("0")
        lst_shifts = Moveable.NextLoc[ind_slide]
        for nShift in lst_shifts:
            newLayout = swap_chr(curLayout, nShift, ind_slide)
            if Statu_saving.get(newLayout) == None:
                Statu_saving[newLayout] = curLayout
                Answer_Solving.append(newLayout)
                MoveString[newLayout]=Movement[ind_slide][nShift]
                if newLayout == destLayout:
                    break


def solvePuzzle_A(srcLayout, destLayout):#A*(Distance+Number) xxf
    Statu_saving[srcLayout] = -1 #初始化字典
    Statu_distance[srcLayout]= 1
    Fn[srcLayout] = 1 + Distance_Compute(srcLayout, destLayout) #Distance的A*算法,计算总代价:实际要走的代价+预测的代价
    #Fn[srcLayout] = 1 + Number_Compute(srcLayout, destLayout) #Number 的A*算法
    Answer_Solving = []
    Answer_Solving.append(srcLayout)#当前状态存入列表,跟无信息搜索一致
    while len(Answer_Solving) > 0:#当存在可移动的状态
        curLayout = min(Fn, key=Fn.get)#比较字典中value,返回Key,找到最小代价作为下一个起点
        del Fn[curLayout]#找到最小代价后删除curLayout中的变量
        Answer_Solving.remove(curLayout)#找到最小fn后还要移除“curlayout”这个对象
        if curLayout == destLayout:#判断当前状态是否为目标状态
            break
        ind_slide = curLayout.index("0")# 寻找0的位置。
        lst_shifts = Moveable.NextLoc[ind_slide]#当前可进行交换的位置集合
        for nShift in lst_shifts:#在可以交换的集合中
            #hn是目前的总代价(实际+预测)
            newLayout, hn = swap_chr_A(curLayout, nShift, ind_slide, Statu_distance[curLayout] + 1, destLayout)#Distance的A*算法
            #newLayout, hn = swap_chr_a(curLayout, nShift, ind_slide, Statu_distance[curLayout] + 1, destLayout)#Number的A*算法
            if Statu_saving.get(newLayout) == None:#判断交换后的状态是否已经查询过
                Statu_distance[newLayout] = Statu_distance[curLayout] + 1 #存入走的路程,走了1步
                Fn[newLayout] = hn#存入fn,当前状态对应的代价
                Statu_saving[newLayout] = curLayout#定义前驱结点
                Answer_Solving.append(newLayout)#存入集合
                MoveString[newLayout] = Movement[ind_slide][nShift]  # 动作存在对应的MoveString
                if newLayout == destLayout:  # 判断当前状态是否为目标状态
                    break  # 如果是目标状态,则出栈

def solvePuzzle(srcLayout, destLayout): #xxf
    Answer_Output = []#当全部状态完成
    Answer_Output.append(destLayout)
    curLayout=destLayout
    while Statu_saving[curLayout] != -1:#当等于-1时,意味着可以退出循环,因为第一个初始状态的字典value是-1
        curLayout = Statu_saving[curLayout]#按照最后状态往前逆推回溯
        Answer_Output.append(curLayout)#将每一个状态保存
    Answer_Output.reverse()#用reverse得到顺序输出
    for answer in Answer_Output:
        if(answer!=srcLayout):
            Movement_Output.append(MoveString[answer])
    return 0, Answer_Output#如同奇同偶,则可达

if __name__ == "__main__": #xxf
    start =time.time()
    srcLayout  ="012345678" #输入初始状态
    destLayout ="123045678" #输入目标状态
    if(judge(srcLayout,destLayout)==0):
        #solvePuzzle_DFS(srcLayout, destLayout)  #DFS
        #solvePuzzle_BFS(srcLayout, destLayout)   #BFS
        solvePuzzle_A(srcLayout, destLayout)    #A*
        retCode, lst_steps= solvePuzzle(srcLayout, destLayout)
        for number in range(len(lst_steps)):
            if(lst_steps[number]!=srcLayout):
                print("The " + str(number) + "th move is:")
                print(lst_steps[number][:3])  # 切片,为了显示美观
                print(lst_steps[number][3:6])
                print(lst_steps[number][6:])
                print("-------------")
        end = time.time()
        print("The move action is: ", Movement_Output)
        print("The number of moves is: ", len(Movement_Output))
        print("The time consumed is:" + str(end - start) + " s")  # 查看运行时间(秒)
    else:
        print("Cannot be achieved")#逆序性不满足

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇