Python利用BFS DFS UCS 贪婪 A*算法解决八数码问题

为了完成人工智能与机器学习实验报告 。。。 

本文只需要用到 四个 包

#import 相关包
import copy
import numpy as np
import random
from datetime import datetime

逆序数判断八数码问题是否有解

#逆序数判断:
def solution_or_not(initial,goal): 
    initial = initial.replace(" ","")   #剔除字符串内空格
    goal = goal.replace(" ","")
    init_num = 0    #initial逆序数
    goal_num = 0    #goal逆序数

    for i in range(1,9):    #计算initial逆序数
        temp = 0
        for j in range(0,i):
            if initial[j] > initial[i] and initial[i]!='0':
                temp=temp+1
        init_num = init_num + temp

    for i in range(1,9):    #计算goal逆序数
        temp = 0
        for j in range(0,i):
            if goal[j] > goal[i] and goal[i]!='0':
                temp=temp+1
        goal_num = goal_num + temp    

    if(init_num%2) != (goal_num%2): #判断两逆序数的奇偶性是否相等
        return 0
    else:
        return 1

为了解决八数码问题(移动和判断往哪里移动)需要的一些函数

#寻找target的位置
def find_local(arr,target):   #arr是节点的list,target是寻找的目标
    for i in arr:
        for j in i:
            if j == target:
                return arr.index(i),i.index(j)  #返回target的下标

#交换位置,并获得子节点
def get_child(arr,e):
    arr_new = copy.deepcopy(arr)      #深拷贝,复制一份新的节点
    r, c = find_local(arr_new,'0')    #寻找0的位置的坐标
    r1, c1 = find_local(arr_new,e)    #寻找可交换的位置的坐标

    #交换位置
    arr_new[r][c], arr_new[r1][c1] = arr_new[r1][c1], arr_new[r][c]
    return arr_new

#获取可与0交换的元素
def get_elements(arr):
    r,c =find_local(arr,'0')      #寻找0的的下标
    elements=[]
    if r > 0:
        elements.append(arr[r - 1][c])  # 上边的数
    if r < 2:
        elements.append(arr[r + 1][c])  # 下边的数
    if c > 0:
        elements.append(arr[r][c - 1])  # 左边的数
    if c < 2:
        elements.append(arr[r][c + 1])  # 右边的数
    return elements 

#曼哈顿距离
def mhd_distance(arr1,arr2):
    distance = []
    for i in arr1:
        for j in i:
            loc1 = find_local(arr1,j)
            loc2 = find_local(arr2,j)
            distance.append(abs(loc1[0] + loc2[0]) + abs(loc1[1] - loc2[1]))
    return sum(distance)

#不在位数
def not_digits(arr1,arr2):
    num=0
    for i in range(0,2):
        for j in range(0,2):
            if arr1[i][j] != arr2[i][j] and arr1[i][j] != 0 and arr2[i][j]!= 0:
               num = num+1 
    return num

创建一个节点类,用来储存每一个八数码的状态

#设置节点类
class state:
    #state为八码数的list表示,parent为父节点
    #deep为节点深度,cost为节点代价
    #distance为曼哈顿距离,nd_nums为不在位数
    def __init__(self, state, parent, deep, cost ,distance ,nd_nums):
        self.state = state
        self.parent = parent
        self.deep = deep
        self.cost = cost
        self.distance = distance
        self.nd_nums= nd_nums

    def get_children(self): #获取一层子节点
        children=[]
        for i in get_elements(self.state):
            #逐个元素与0交换位置,生成子节点child
            child = state(state=get_child(self.state,i), 
            parent = self, 
            deep = self.deep+1, 
            cost = self.cost+1,
            distance = self.distance + mhd_distance(self.state,goal_arr),
            nd_nums = not_digits(self.state,goal_arr))
              
            #将每一个交换结果(子节点)都存入children
            children.append(child)  
        return children

为了实现可视化输出

#打印最短路径
def best_path(n):
    if n.parent == None:
        return
    else:
        print("↑")
        print(np.array(n.parent.state))
        best_path(n.parent)

#画分割线
def draw_line():
    print('--' * 20)
    print('--' * 20)
    print('--' * 20)

#整一个搜索路径:
def search_line(close):
    print('搜索路径如下:')
    for i in close[:-1]:
        print(np.array(i.state))
        print('↓')
    print(np.array(close[-1].state))

将字符串输入转化为八数码

#将字符串转化为列表
def string_to_list(str):
    str_list=list(str)
    return [str_list[i:i+3] for i in range(0,len(str_list),3)]

广度优先搜索BFS

#广度优先搜索
def BFS(initial_arr,goal_arr):
    open = [initial_arr]
    close = []

    while len(open) > 0:   #OPEN表是否为空表
        open_1 = [i.state for i in open]    #访问open节点内的state
        close_1 = [i.state for i in close]

        n = open.pop(0)     #删除OPEN队头节点,并且赋值给n
        close.append(n)     #n注入CLOSE表
            
        if n.state == goal_arr:
            print('最优路径如下:')
            print(np.array(n.state))    #转换成矩阵打印最终节点
            best_path(n)
            break
        else:
            for i in n.get_children():  #添加子节点进OPEN
                if i.state not in open_1:
                    if i.state not in close_1:
                        open.append(i)

    draw_line()
    search_line(close)
    print('搜索步骤为',len(close) - 1)

深度优先搜索DFS

#深度优先搜索
def DFS(initial_arr,goal_arr):
    open = [initial_arr]
    close = []
    # limit = eval(input('请输入要搜索的深度:'))
    limit = 20

    while len(open) > 0:
        open_2 = [i.state for i in open]
        close_2 = [i.state for i in close]

        n = open.pop(0)
        close.append(n)
        
        if n.state == goal_arr:
            print('最优路径如下:')
            print(np.array(n.state))    #转换成矩阵打印最终节点
            best_path(n)
            break
        else:
            if n.deep < limit:
                for i in n.get_children():
                    if i.state not in open_2:
                        if i.state not in close_2:
                            open.insert(0, i)   #DFS从前端插入
    else:
        print('该深度下无解')   #循环出去后显示无解

    draw_line()
    search_line(close)
    print('深度为',close[-1].deep,'下的搜索步数为:',len(close) - 2)

一致代价优先搜索UCS

#一致代价优先搜索
def UCS(initial_arr,goal_arr):
    open = [initial_arr]
    close = []

    while len(open) > 0:   #OPEN表是否为空表
        open_3 = [i.state for i in open]    #访问open节点内的state
        close_3 = [i.state for i in close]

        open_4 = [i.cost for i in open]   #OPEN内每个节点的cost
        min_index = open_4.index(min(open_4))

        n = open.pop(min_index)     #删除OPEN队头节点,并且赋值给n
        close.append(n)     #n注入CLOSE表
            
        if n.state == goal_arr:
            print('最优路径如下:')
            print(np.array(n.state))    #转换成矩阵打印最终节点
            best_path(n)
            break
        else:
            for i in n.get_children():  #添加子节点进OPEN
                if i.state not in open_3:
                    if i.state not in close_3:
                        open.append(i)

    draw_line()
    search_line(close)
    print('搜索步骤为',len(close) - 1, '权重为',close[-1].cost)

贪婪算法

#贪婪算法
def Greedy(initial_arr,goal_arr):
    open = [initial_arr]
    close = []

    while len(open) > 0:   #OPEN表是否为空表
        open_1 = [i.state for i in open]    #访问open节点内的state
        close_1 = [i.state for i in close]

        n = open.pop(0)     #删除OPEN队头节点(此点排序后为最小距离和),并且赋值给n
        close.append(n)     #n注入CLOSE表
            
        if n.state == goal_arr:
            print('最优路径如下:')
            print(np.array(n.state))    #转换成矩阵打印最终节点
            best_path(n)
            break
        else:
            for i in n.get_children():  #添加子节点进OPEN
                if i.state not in open_1:
                    if i.state not in close_1:
                        open.insert(0,i)
                        open.sort(key = lambda x: x.distance)  #按曼哈顿距离进行排序

    draw_line()
    search_line(close)
    print('搜索步骤为',len(close) - 1,'总估价为',close[-1].distance)

A*算法-曼哈顿距离

#A*算法-曼哈顿距离
def AStar_MHT(initial_arr,goal_arr):
    open = [initial_arr]
    close = []

    while len(open) > 0:   #OPEN表是否为空表
        open_1 = [i.state for i in open]    #访问open节点内的state
        close_1 = [i.state for i in close]

        n = open.pop(0)     #删除OPEN队头节点(此点排序后为最小距离和),并且赋值给n
        close.append(n)     #n注入CLOSE表
            
        if n.state == goal_arr:
            print('最优路径如下:')
            print(np.array(n.state))    #转换成矩阵打印最终节点
            best_path(n)
            break
        else:
            for i in n.get_children():  #添加子节点进OPEN
                if i.state not in open_1:
                    if i.state not in close_1:
                        open.insert(0,i)
                        open.sort(key = lambda x: x.distance + x.cost)  #按曼哈顿距离+cost 进行排序

    draw_line()
    search_line(close)
    print('搜索步骤为',len(close) - 1,'总估价为',close[-1].cost+close[-1].distance)

 A*算法-不在位数

#A*算法-不在位数
def AStar_ND(initial_arr,goal_arr):
    open = [initial_arr]
    close = []

    while len(open) > 0:   #OPEN表是否为空表
        open_1 = [i.state for i in open]    #访问open节点内的state
        close_1 = [i.state for i in close]

        n = open.pop(0)     #删除OPEN队头节点(此点排序后为最小距离和),并且赋值给n
        close.append(n)     #n注入CLOSE表
            
        if n.state == goal_arr:
            print('最优路径如下:')
            print(np.array(n.state))    #转换成矩阵打印最终节点
            best_path(n)
            break
        else:
            for i in n.get_children():  #添加子节点进OPEN
                if i.state not in open_1:
                    if i.state not in close_1:
                        open.insert(0,i)
                        open.sort(key = lambda x: x.nd_nums + x.cost)  #按不在位数+cost 进行排序

    draw_line()
    search_line(close)
    print('搜索步骤为',len(close) - 1)

主函数如下

#主函数
if __name__=='__main__':
    initial='283 104 765'
    goal='123 804 765'
    if solution_or_not(initial,goal):
        goal_arr = string_to_list(goal)
        initial_arr = state(string_to_list(initial),
        parent=None, 
        deep=0, 
        cost=0, 
        distance=mhd_distance(string_to_list(initial),goal_arr), 
        nd_nums=not_digits(string_to_list(initial),goal_arr))
        
        DFS(initial_arr,goal_arr)  #深度优先搜索
        BFS(initial_arr,goal_arr)   #宽度优先搜索
        UCS(initial_arr,goal_arr)  #一致代价优先搜索
        Greedy(initial_arr,goal_arr)  #贪婪算法
        AStar_MHT(initial_arr,goal_arr)  #A*算法-曼哈顿距离
        AStar_ND(initial_arr,goal_arr)  #A*算法-不在位数
    else:
        print("从逆序数判断来看,该八数码问题无解") 
        
        

 部分运行结果如下:

Python利用BFS DFS UCS 贪婪 A*算法解决八数码问题_第1张图片

将八数码随机打乱  

#随机打乱八数码
def random_str(str):
    str_list = list(str)
    random.shuffle(str_list)
    return ''.join(str_list)

对比两个A*算法的时间复杂度  

#算法对比
if __name__=='__main__':
    initial='283104765'
    goal='123804765'
    time = []
    count = 50
    print("A*曼哈顿距离算法:")
    for i in range(0,count):
        initial = random_str(initial)
        goal = random_str(goal)
        if solution_or_not(initial,goal):
            goal_arr = string_to_list(goal)
            initial_arr = state(string_to_list(initial),
            parent=None, 
            deep=0, 
            cost=0, 
            distance=mhd_distance(string_to_list(initial),goal_arr), 
            nd_nums=not_digits(string_to_list(initial),goal_arr))
            start = datetime.now()
            AStar_MHT(initial_arr,goal_arr)  #A*算法-曼哈顿距离
            end = datetime.now()
            print("第",i+1,"次迭代结束,时间为",end - start)
            time.append(end - start)
        else:
            print("第",i+1,"次迭代结束,该八数码无解")
    print('A*曼哈顿距离算法平均耗时:',np.mean(time))

    initial='283104765'
    goal='123804765'
    time = []
    print("A*不在为数算法:")
    for i in range(0,count):
        initial = random_str(initial)
        goal = random_str(goal)
        if solution_or_not(initial,goal):
            goal_arr = string_to_list(goal)
            initial_arr = state(string_to_list(initial),
            parent=None, 
            deep=0, 
            cost=0, 
            distance=mhd_distance(string_to_list(initial),goal_arr), 
            nd_nums=not_digits(string_to_list(initial),goal_arr))
            start = datetime.now()
            AStar_ND(initial_arr,goal_arr)  #A*算法-不在位数
            end = datetime.now()
            print("第",i+1,"次迭代结束,时间为",end - start)
            time.append(end - start)
        else:
            print("第",i+1,"次迭代结束,该八数码无解")
    print('A*不在位数算法平均耗时:',np.mean(time))

部分运行结果如下 :

Python利用BFS DFS UCS 贪婪 A*算法解决八数码问题_第2张图片

八数码问题的代码基于博主AlphaJun_zero的思路做出部分修改,完善与改进

AlphaJun_zerohttps://blog.csdn.net/Juuunn

源代码下载(Jupyter)

人工智能八数码问题一次全通多解法_人工智能八数码-深度学习文档类资源-CSDN下载

你可能感兴趣的:(宽度优先,深度优先,算法,1024程序员节)