八数码问题(python实现)

问题的表示

八数码问题(python实现)_第1张图片

我们使用一个二维数组arr表示一个状态空间,数组的元素含有0-8,0表示可以移动的空格,1-8表示其余待排序的方块。【一个二维数组表示八数码的每个位置的可移动方向】通过对空格0的上、下、左、右移动,得到最终的目标状态。

为实现BFS和DFS搜索算法,我们需要实现一些辅助函数:

① Cal_sameNum(self, state):传入一个状态state,返回当前节点”在位“的棋格数。

② NeedStop(self, state, stop):传入一个状态state和一个状态列表stop,若状态state与列表stop中某一状态完全一致,则返回True,意味着终止搜索。

③ Move(self):将self.arr所在状态的下一步可能移动到的状态的集合,存入一个列表Move_list并返回。

【Move函数可以用字典进行优化】

问题的解

奇偶性一致才有解

http://www.voidcn.com/article/p-gvpoulek-dd.html

DFS深度优先

通过栈stack(先进后出)进行实现。

① 起始状态放入栈中。

② 判断栈是否为空,栈空则直接无解,失败退出。

③ 显示栈顶元素的状态

④ 判断栈顶元素状态是否到达目标状态,若到达则意味着找到了最终解,未到达则继续循环操作。

⑤ 通过Move函数得到栈顶元素的下一步状态列表,然后将下一步状态依次入栈,计数+1

由于仅使用上述实现方法效率过低,甚至会进入死循环,我们将其进行优化

每次入栈前增加两个判断:

① 是否超出一个节点拓展的最大深度,如最大深度为5。

② 是否在stop列表中出现,防止出现与前面出现过的状态相同,以此进入死循环。

BFS广度优先

通过队列queue(先进先出)进行实现,其余通DFS。

UCS一致代价搜索

https://blog.csdn.net/Suyebiubiu/article/details/101194332

改进优先队列

A*启发式搜索

f(x) = g(x) + h(x)

g(x)为节点的深度。

h(x)可以有多种选择,例如“不在位”数、曼哈顿距离、欧氏距离等。

完整代码如下:

# -*- encoding: utf-8 -*-
# %%
import numpy as np
import time
import queue

# @ 判断是否有解->计算状态的逆序数,若逆序数为奇数则有解(与目标状态的逆序数同为奇数)
def HaveSolution(arr):
    numList, sum = [], 0
    for i in range(3):
        for j in range(3):
            numList.append(arr[i, j])
    for index, val in enumerate(numList):
        if index < 9:
            for i in range(index, 9):
                if val > numList[i] and numList[i] != 0:
                    sum += 1
    return sum % 2 == 1

# @ 展示到达目标的最短路径
def Display_path(res, count, node):
    result = []
    if res:
        print('经过%d次变换结束' % count)
        while node:
            result.append(node)
            node = node.parent
        result.reverse()
        for node in result:
            for i in range(3):
                for j in range(3):
                    print(node.arr[i,j], end='\t')
                print("\n")
            print('->')
    else:
        print('未找到合适路径!')
    print('----------------------------------------------')

# @ 计算"不在位"的棋个数
def Cal_diffNum(arr, target):
    pos = np.where(arr != target)
    return len(arr[pos])

# @ A*中优先队列的pop(根据"不在位"棋个数)
def pop_smallest_costNum(nodes,target):
    cost_list = []
    for node in nodes:
        cost_list.append(Cal_diffNum(node.arr,target))
    temp = nodes[cost_list.index(min(cost_list))]
    nodes.remove(nodes[cost_list.index(min(cost_list))])
    return temp

# @ 计算某个状态下每个棋子到目标位置的曼哈顿距离之和
def Cal_distance(arr, taregt):
    sum = 0
    for cols in taregt:
        for val in cols:
            i,j = np.where(arr==val)[0], np.where(arr==val)[1]
            si,sj = np.where(taregt==val)[0], np.where(taregt==val)[1]
            sum += abs(i-si)+abs(j-sj)
    return sum

# @ A*中优先队列的pop(根据曼哈顿距离之和)
def pop_smallest_costDis(nodes, target):
    cost_list = []
    for node in nodes:
        cost_list.append(Cal_distance(node.arr, target))
    temp = nodes[cost_list.index(min(cost_list))]
    nodes.remove(nodes[cost_list.index(min(cost_list))])
    return temp

# @ 计算cost
def compute_cost(depth, type, arr, target):
    if type=='DFS' or type=='BFS':
        return depth+1
    elif type=='UCS':
        return Cal_diffNum(arr, target)
    elif type=='A_star1':
        return depth + Cal_diffNum(arr, target)
    elif type=='A_star2':
        return depth + Cal_distance(arr, target)
    else:
        print('ERROR!')
        return 0

# @ 八数码节点类,即某一状态
class EightNumArr(object):
    directions = ['left','up','right','down']
    directions2 = ['down','right','up','left']
    def __init__(self, arr, target, type, cost=0, parent=None, depth=0):
        self.arr = arr
        self.target = target
        self.type = type
        self.cost = cost
        self.parent = parent
        self.depth = depth

    def __lt__(self, other):
        # return Cal_diffNum(self.arr, self.target)
        return compute_cost(self.depth,self.type,self.arr,self.target) < compute_cost(other.depth,self.type,other.arr,other.target)

    # @ 打印八数码
    def Display(self):
        for i in range(3):
            for j in range(3):
                print(self.arr[i, j], end='\t')
            print("\n")
        print('->')

    # @ 返回该状态与目标状态中数字位置相同的个数-->返回9时则结束
    def Cal_sameNum(self, state, target):
        pos = np.where(state.arr == target)
        return len(state.arr[pos])

    # @ 计算曼哈顿距离,作为后续A*2中每轮循环下排序的标准
    def Cal_Dis(self, state, target):
        sum = 0
        for cols in target:
            for val in cols:
                i, j = np.where(state.arr == val)[0], np.where(state.arr== val)[1]
                si, sj = np.where(target == val)[0], np.where(target == val)[1]
                sum += abs(i - si) + abs(j - sj)
        return sum

    # @ 是否在close表中已出现,若已出现还放入open表,则会进入死循环。
    def isInClose(self, state, stop):
        for val in stop:
            pos = np.where(state.arr == val.arr)
            if len(state.arr[pos]) == 9:
                return True
        return False
    # @ 某一状态下进行四个方向上的移动空格
    def Move(self, type):
        row, col = np.where(self.arr == 0)
        if type=='DFS':
            directs, Move_list = self.directions2,[]
        else:
            directs, Move_list = self.directions,[]
        for direct in directs:
            if 'left' == direct and col > 0:
                arr_copy = self.arr.copy()
                arr_copy[row, col], arr_copy[row, col - 1] = arr_copy[row, col - 1], arr_copy[row, col]
                Move_list.append(EightNumArr(arr_copy, self.target,type,compute_cost(self.depth, type, arr_copy, target),self, self.depth + 1))
            elif 'up' == direct and row > 0:
                arr_copy = self.arr.copy()
                arr_copy[row, col], arr_copy[row - 1, col] = arr_copy[row - 1, col], arr_copy[row, col]
                Move_list.append(EightNumArr(arr_copy, self.target,type,compute_cost(self.depth, type, arr_copy, target), self, self.depth + 1))
            elif 'down' == direct and row < 2:
                arr_copy = self.arr.copy()
                arr_copy[row, col], arr_copy[row + 1, col] = arr_copy[row + 1, col], arr_copy[row, col]
                Move_list.append(EightNumArr(arr_copy, self.target,type,compute_cost(self.depth, type, arr_copy, target), self, self.depth + 1))
            elif 'right' == direct and col < 2:
                arr_copy = self.arr.copy()
                arr_copy[row, col], arr_copy[row, col + 1] = arr_copy[row, col + 1], arr_copy[row, col]
                Move_list.append(EightNumArr(arr_copy, self.target,type,compute_cost(self.depth, type, arr_copy, target), self, self.depth + 1))
        return Move_list

    # @ DFS深度优先搜索,返回True/False, count搜索次数
    def DFS(self,max_depth):
        start = time.time()
        stack, close, count, node = [], [], 0, None
        stack.append(self)
        while stack:
            node = stack.pop()               # 后进先出,pop出栈顶元素
            close.append(node)
            # node.Display()
            if self.Cal_sameNum(node,self.target) == 9:  # 到达目标状态,直接返回
                end = time.time()
                return True, count, node, end-start
            next_nodes = node.Move('DFS')    # 一次移动的状态列表
            for next_node in next_nodes:
                if next_node.depth < max_depth:       # 最大深度
                    if self.isInClose(next_node, close):
                        continue
                    stack.append(next_node)
            count += 1
        end = time.time()
        return False, count, node, end-start

    # @ BFS广度优先搜索,返回True/False, count搜索次数
    def BFS(self,max_depth):
        start = time.time()
        queue, close, count, node = [], [], 0, None
        queue.append(self)
        while queue:
            node = queue.pop(0)              # 先进先出,pop出队首元素
            close.append(node)
            # node.Display()
            if self.Cal_sameNum(node,self.target) == 9:  # 到达目标状态,直接返回
                end = time.time()
                return True, count, node, end-start
            next_nodes = node.Move('BFS')    # 一次移动的状态列表
            for next_node in next_nodes:
                if next_node.depth < max_depth:       # 最大深度
                    if self.isInClose(next_node, close):
                        continue
                    queue.append(next_node)
            count += 1
        end = time.time()
        return False, count, node, end-start

    # @ UCS一致代价搜索,返回True/False,count搜索次数
    def UCS(self,max_depth):
        start = time.time()
        close, count, node = [], 0, None
        que = queue.PriorityQueue()
        que.put(self)
        while que:
            node = que.get()
            close.append(node)
            # node.Display()
            if self.Cal_sameNum(node, self.target) == 9:  # 到达目标状态,直接返回
                end = time.time()
                return True, count, node, end-start
            next_nodes = node.Move('UCS')    # 一次移动的状态列表
            for next_node in next_nodes:
                if next_node.depth < max_depth:
                    if self.isInClose(next_node, close):
                        continue
                    que.put(next_node)
            count += 1
        end = time.time()
        return False, count, node, end-start

    # @ A_star启发式搜索--h(x):不在位数量/曼哈顿距离,返回True/False,count搜索次数
    def A_star(self,max_depth,A_starType):
        start = time.time()
        close, count, node = [], 0, None
        que = queue.PriorityQueue()
        que.put(self)
        while que:
            node = que.get()
            close.append(node)
            if self.Cal_sameNum(node, self.target) == 9:  # 到达目标状态,直接返回
                end = time.time()
                return True, count, node, end-start
            next_nodes = node.Move(A_starType)     # 一次移动的状态列表
            for next_node in next_nodes:
                if next_node.depth < max_depth:      # 最大深度
                    if self.isInClose(next_node, close):
                        continue
                    que.put(next_node)
            count += 1
        end = time.time()
        return False, count, node, end-start

你可能感兴趣的:(机器学习,python,算法)