Python语言实现A搜索算法求解八数码问题

八数码问题

在3×3的九宫格,摆有1至8的数字,和一个空格。
要求:给出一个初始状态和一个最终状态,找出从初始状态转变成最终状态的最少移动步数。

A搜索算法

1.A算法是基于估价函数的一种加权启发式图搜索算法。
2.估价函数 f(n) 定义为从初始结点经过 n 结点到达目的结点的路径的最小代价估计值。
3.启发式策略是利用与问题有关的启发信息引导搜索。

代码实现

  • eightDigits.py
import copy

# [[向上 , 向下 , 向左 , 向右],
#  [f(n) , 巽宫 , 离宫 , 坤宫],
#  [d(n) , 震宫 , 中宫 , 兑宫],
#  [w(n) , 艮宫 , 坎宫 , 乾宫],
#  [空格的行下标 , 空格的列下标, 父状态的编号, 本状态的编号]

# f(n) 该状态的估值函数值
# d(n) 状态的深度
# w(n) 不在最终位置的数码个数
# f(n) = d(n) + w(n)
# 扩展方向的标志量,0:不行,1:可以

# 初始状态
Init = [[0, 1, 1, 1],
        [7, 2, 0, 8],
        [0, 1, 6, 3],
        [7, 7, 5, 4],
        [1, 2, 0, 0]]
# 最终状态
Final = [[0, 0, 0, 0],
         [0, 1, 2, 3],
         [0, 8, 0, 4],
         [0, 7, 6, 5],
         [2, 2, 0, 0]]

# 在open表中保留所有已生成而未扩展的状态
openList = list()
# 在closed表中记录已扩展过的状态。
closedList = list()
# 空格 上、下、左、右 移动时,横、纵坐标的变化量
move = [[-1, 0], [1, 0], [0, -1], [0, 1]]
# 记录当前八数码状态的编号的变量
num = [1]

def expandState():
    # 父状态
    parent = copy.deepcopy(closedList[-1])
    # 空格 上、下、左、右 移动, 扩展状态
    for k in range(4):
        # 判断是否向该方向扩展
        if parent[0][k] == 0:
            continue
        
        # 新的子扩展状态
        child = copy.deepcopy(closedList[-1])
        
        # 空格移动前的坐标
        x1 = child[4][0]
        y1 = child[4][1]
        # 空格移动后的坐标
        x2 = x1 + move[k][0]
        y2 = y1 + move[k][1]
        # 移动空格
        temp = child[x2][y2]
        child[x2][y2] = child[x1][y1]
        child[x1][y1] = temp
        # 更新空格的坐标
        child[4][0] = x2
        child[4][1] = y2
        
        # 更新 d(n)
        child[2][0] = child[2][0] + 1
        # 计算 w(n)
        wn = 0
        for i in [1, 2, 3]:
            for j in [1, 2, 3]:
                if child[i][j] != Final[i][j]:
                    wn = wn + 1
        # 中宫数码不在位,不计算在 w(n) 内
        wn = wn-1 if child[2][2] != Final[2][2] else wn
        # 更新 w(n)
        child[3][0] = wn
        # 更新 f(n)
        child[1][0] = child[2][0] + wn
        
        # 更新 上、下、左、右 是否扩展的标志量
        child[0][0] = 1 if child[4][0] > 1 else 0
        child[0][1] = 1 if child[4][0] < 3 else 0
        child[0][2] = 1 if child[4][1] > 1 else 0
        child[0][3] = 1 if child[4][1] < 3 else 0
        # 不可以向反方向扩展
        back = k-move[k][0]-move[k][1]
        child[0][back] = 0
        
        # 设置父状态的编号
        child[4][2] = parent[4][3]
        # 设置本状态的编号
        child[4][3] = num[0]
        # 编号加一, 待用
        num[0] = num[0] + 1
        
        # 扩展得到的子状态添加进 open 表
        openList.append(child.copy())
        # 清空
        child.clear()

# 展示 open 表
def showOpen():
    for ol in openList:
        for i in [1, 2, 3]:
            print("{}  {}  {}".format(ol[i][1], ol[i][2], ol[i][3]))
        print("         f(n):{}  d(n):{}  w(n):{}".format(ol[1][0], ol[2][0], ol[3][0]))

# 展示 closed 表
def showClosed():
    for cl in closedList:
        for i in [1, 2, 3]:
            print("{}  {}  {}".format(cl[i][1], cl[i][2], cl[i][3]))
        print("         f(n):{}  d(n):{}  w(n):{}".format(cl[1][0], cl[2][0], cl[3][0]))

# 删除先前走错的状态结点,留下最终的路径
def find():
    n = len(closedList)
    m = closedList[-1][4][2]
    # 从尾往前排查
    for i in range(n-1, 0, -1):
        # 如果前一个状态的编号不是当前状态的父状态编号
        if closedList[i-1][4][3] != m:
            # 不是当前状态的父状态编号,则删除
            closedList.pop(i-1)
        else:
            # 是当前状态的父状态编号,则取得父状态的父状态编号
            m = closedList[i-1][4][2]

# 算法具体步骤如下:
# Step 1:把附有f(S0)的初始结点 S0放入 open 表
# Step 2:若 open 表为空, 则搜索失败, 退出
# Step 3:移出 open 表中第一个结点 N 放入 closed 表
# Step 4:若目标结点把附有 f(S0) 的初始 S0 = N, 则搜索成功, 结束
# Step 5:若 N 不可扩展, 则转 Step 2
# Step 6:扩展 N, 生成一组附有 f(x) 的子结点, 加入 open 表
# Step 7:对 open 表按 f(x) 值以升序排序, 转 Step 2。

# Step 1
openList.append(Init)

# 循环调用
while (1):
    #showOpen()
    #showClosed()
    
    # Step 2
    if (len(openList) == 0):
        print("搜索失败,退出程序!")
        break
    # Step 3
    closedList.append(openList.pop(0))
    # Step 4
    if closedList[-1][3][0] == 0:
        find()
        print("closedList")
        showClosed()
        print("搜索成功!")
        break
    # Step 5
    if (sum(closedList[-1][0]) == 0):
        continue
    # Step 6
    expandState();
    # Step 7
    openList.sort(key = lambda x:(x[1][0], x[2][0], x[3][0]))

# 结束
print("\nEND !")
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值