八数码问题 python实现 BFS宽度优先搜索 极大优化速度版

上一年的人工智能课就已经把八数码的BFS DFS A* 遗传算法都试了一遍.昨天上传旧的时候觉得实现的不是很优雅,现在想重新用python来一遍.这次我实现了一个很大的突破,起码比原来的算法实现的速度提高了几十倍~
在这里插入图片描述
题目如图,首先我们先建立一个棋盘类来进行八数码问题的操作.

class board:
    def __init__(self):
        # self.groud = [1,0,2,3,4,5,6,7,8]
        # 棋盘,0代表空
        self.groud = [7, 2, 4, 5, 0, 6, 8, 3, 1]
        #移动的路径
        self.route = []
# 是否到达正确状态
    def guiwei(self):
        flag = True
        for i in range(9):
            if self.groud[i] != i:
                flag = False
                break
        return flag
# 两个位置数字进行交换
    def exchange(self, index1, index2):
        temp = self.groud[index1]
        self.groud[index1] = self.groud[index2]
        self.groud[index2] = temp
# 空格向左移动
    def left(self):
        index = self.groud.index(0)
        if index % 3 != 0:
            self.exchange(index, index-1)
# 空格向右移动
    def right(self):
        index = self.groud.index(0)
        if index % 3 != 2:
            self.exchange(index, index+1)
# 空格向上移动
    def up(self):
        index = self.groud.index(0)
        if int(index/3) != 0:
            self.exchange(index, index-3)
# 空格向下移动
    def down(self):
        index = self.groud.index(0)
        if int(index/3) != 2:
            self.exchange(index, index+3)
# 返回空格能移动的方位的列表,比如[0,1,2,3]代表空格能向左右上下进行移动
    def can_move(self):
        index = self.groud.index(0)
        can = []
        if index % 3 != 0:
            can.append(0)
        if index % 3 != 2:
            can.append(1)
        if int(index/3) != 0:
            can.append(2)
        if int(index/3) != 2:
            can.append(3)
        return can
# 展示棋盘
    def show_board(self):
        print(self.groud)
# 路径
    def show_route(self):
        print(self.route)
        print(len(self.route))
# 通过route路径进行移动,route是路径列表
    def move(self, route):
        for i in route:
            if i == 0:
                self.left()
            elif i == 1:
                self.right()
            elif i == 2:
                self.up()
            else:
                self.down()
# 仅移动一步
    def move_one(self, i):
        if i == 0:
            self.left()
        elif i == 1:
            self.right()
        elif i == 2:
            self.up()
        else:
            self.down()
# 测试如果向目标方向移动,棋盘的变化,返回变化的棋盘
    def test(self, i):
        groud = []
        if i == 0:
            self.left()
            groud = self.groud[:]
            self.right()
        elif i == 1:
            self.right()
            groud = self.groud[:]
            self.left()
        elif i == 2:
            self.up()
            groud = self.groud[:]
            self.down()
        else:
            self.down()
            groud = self.groud[:]
            self.up()
        return groud

然后我先写出来了一个没有任何优化的极简BFS

from board import board
def BFS_waste_time():
    deq = []
    b = board()
    for x in b.can_move():
        deq.append([x])
    flag = 0
    while True:
        flag+=1
        temp = deq.pop(0)
        if flag%10000==0:
            flag=0
            print(temp)
            print(len(temp)) 
        b_temp = board()
        b_temp.move(temp)
        if  b_temp.guiwei():
            print(temp)
            break
        for x in b_temp.can_move():
            new_temp=temp[:]
            new_temp.append(x)
            deq.append(new_temp)   

很简单通过队列,先进后出就能实现.这个算法是没有问题的,实现也没有问题,就是这么个暴力计算的话需要算很久才能算出来.接下来这个是经过我优化的.

import copy
def list2str(li):
    s = ""
    for x in li:
        s+=str(x)
    return s
def BFS():
    deq = []
    appear = set()#使用set进行检索极快,hash检索
    temp = board()
    appear.add(list2str(temp.groud))
    for x in temp.can_move():
        b = board()
        b.move_one(x)
        b.route.append(x)
        appear.add(list2str(b.groud))
        deq.append(b)
    flag = 0
    while True:
        flag+=1
        temp = deq.pop(0)
        if flag%10000==0:
            flag=0
            temp.show_board() 
            temp.show_route()

        if  temp.guiwei():
            temp.show_board() 
            temp.show_route()
            break
        for x in temp.can_move():
            # 筛选掉重复的棋盘情况,加快搜索速度
            if list2str(temp.test(x)) not in appear:
                new_temp=copy.deepcopy(temp)
                new_temp.move_one(x)
                new_temp.route.append(x)
                deq.append(new_temp)
                appear.add(list2str(new_temp.groud)) 

提高的速度比简单的BFS提升速度少说几千倍~比优化过的JAVA版快了几十倍.优化重点:

  • 重复的棋盘就不要入队了.在移动过程中大概率会出现这种情况,移动了几步回到了原来的棋盘的情况.我们将出现过的棋盘情况全部记录下来放在appear,移动后如果是新棋盘就入队,如果不是就不再入队进行搜索.
  • 由于每次都需要进行appear的检索,如果单纯地使用路径列表进行检索的话很慢很慢.我搜索了一下知道一件事情,set()集合的搜索是可以使用哈希值进行搜索的,复杂度为O(1),所以,将路径转化为字符串(字符串才是可哈希的),然后使用字符串进行检索,速度直接起飞~

其它尝试了的优化,这些优化可能影响不大

  • 尝试过使用cython提高效率,但是不是很明显
  • test函数的出现是因为觉得复制可能比较耗时,所以就使用test函数进行.
代码时间
JAVA没注意哈希检索版658.932 s
python哈希优化版13.831229 s

在这里插入图片描述
在这里插入图片描述
说明:JAVA的路径是指0的位置(设计的不是很优雅,前面两个4就是0一开始就在4的位置) python的路径是移动的方向

总结:在BFS中尝试了很多方法进行速度优化,从算法层面来讲,剪枝应该就是BFS中比较好用的优化方法了,后面的哈希表以及cython个人觉得属于代码方面的优化了.不过哈希也是算法,第一次切身体会到数据结构上的优化带来的巨大提升.

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

rglkt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值