八数码宽度优先不要逆序数_奇数数码问题

奇数数码与8数码问题不相同,就以3数码问题为例:

问题描述

在2*2的各自中有1-3的3个数和一个空格空格随机摆放在其中的各自中,如图,要求从初始状态图到目标状态读的求解。

03 02

21 13

初始状态 目标状态

其中0表示空

对问题分析

把三数码的初始状态从上至下,从左至右拍成一排,如下所示:

03

21 ——>0321

考虑到空格与相邻位置的交换有一下两种情况:

1)左右交换

2)上下交换

对于1)来说交换不会改变排序的逆序数。2)相党羽排序中向前或者向后跳了两个数字也不会改变这个序列的逆序数。如果3数码问题优解当且仅当初始状态与目标状态排列的逆序数同奇或同偶。

由于3数码是2*2为矩阵,左右交换不改变逆序,上下交换因为是2是偶数,所有上下交换一次奇偶性改变一次。称空格位置所在的行到目标空格所在的行步数为空格的距离,若两个状态可相互到大,那么这两个状态的逆序奇偶性相同且空格距离为偶数,或 逆序奇偶性不同且空格距离为奇数否则不能。即(状态1的逆序数+空格距离)的奇偶性==状态2的奇偶性。

通过上述条件判断三数码问题是否有解,如果无解,说明无解,否则,进行进行求解,下面通过宽度优先遍历和深度优先遍历来求解。

解题思路1:宽度优先

宽度优先是一层层遍历下来的,在对下层的任以节点进行搜索之前,对本层进行搜完成,可以队列先进先出的性质来模拟此过程。因为每进行搜索前都判断是否有解,第一个节点进入队列,依次遍历队列,直到求解结束。

Begin
​
1.判断初始状态是否能达到目标状态,若可以,进入2;不可以退出
​
2.把初始节点放入队列(先进先出)
loop
​
   取得队列最前面的元素;
​
   If 取出元素==目标元素
    成功返回并结束;
       Else do
Begin
​
   如果队首元素有子女,把队首元素的子女进入队列
​
    End
  Until 队列为空
​
End.

`

import 

结果:

84e69ca679d306bc670c29db5505ae0f.png

2.

c011c28f2355d6b32357bcf175b47b33.png

解题思路2:深度优先

以栈为容器,由于每次将可能的新状态入栈,并标记为已经搜索到,当一直深入是会遇到下一步可能搜索到的所有状态标记为搜索过,没有入栈的,这条分支路线路线结束。下次弹出栈顶,开启另一条搜索路线,因为每进行搜索前都判断是否有解,所以算法一直执行上述过程,直到找到目标状态

 begin: 
 
     1.判断初始状态是否能达到目标状态,若可以,进入2;不可以退出
​
     2.把初始节点压入栈(后进先出);
     While 栈不空
​
        Begin
​
         弹出栈顶元素;
​
         If 栈顶元素=goal,成功返回并结束;
​
            Else 以任意次序把栈顶元素的子女压入栈中;
​
     End While
import time as tm
​
​
#每个位置可交换的位置集合
g_dict_shifts = {0:[1, 2], 1:[0, 3], 2:[0],3:[1,2]}
​
def swap_chr(a, i, j):
    if i > j:
        i, j = j, i
    #得到ij交换后的数组
    b = a[:i] + a[j] + a[i+1:j] + a[i] + a[j+1:]
    return b
​
def solvePuzzle_depth(srcLayout, destLayout):
    #先进行判断srcLayout和destLayout逆序值
    #这是判断起始状态是否能够到达目标状态。
    src=0;dest=0
    for i in range(1,4):
        fist=0
        for j in range(0,i):
          if srcLayout[j]>srcLayout[i] and srcLayout[i]!='0':#0是false,'0'才是数字
              fist=fist+1
        src=src+fist
​
    for i in range(1,4):
        fist=0
        for j in range(0,i):
          if destLayout[j]>destLayout[i] and destLayout[i]!='0':
              fist=fist+1
        dest = dest + fist
​
    end=0
    start = 0
    for k in range(4):
        if srcLayout[k]=='0':
            start =int(k/2)
        if destLayout[k]=='0':
            end = int(k/2)
​
    dist = abs(end-start)
    if ((src+dist)%2)!=(dest%2) :#一个奇数一个偶数,不可达
        return -1, None
    g_dict_layouts = {}
   #初始化字典
    g_dict_layouts[srcLayout] = -1
    stack_layouts = []
    stack_layouts.append(srcLayout)#当前状态存入列表
​
    bFound = False
    while len(stack_layouts) > 0:
        curLayout = stack_layouts.pop()#出栈
        if curLayout == destLayout:#判断当前状态是否为目标状态
            break
​
        # 寻找0 的位置。
        ind_slide = curLayout.index("0")
        lst_shifts = g_dict_shifts[ind_slide]#当前可进行交换的位置集合
        for nShift in lst_shifts:
            newLayout = swap_chr(curLayout, nShift, ind_slide)
            if g_dict_layouts.get(newLayout) == None:#判断交换后的状态是否已经查询过
                g_dict_layouts[newLayout] = curLayout
                stack_layouts.append(newLayout)#存入集合
​
​
    lst_steps = []
    lst_steps.append(curLayout)
    while g_dict_layouts[curLayout] != -1:#存入路径
        curLayout = g_dict_layouts[curLayout]
        lst_steps.append(curLayout)
    lst_steps.reverse()
    return 0, lst_steps
​
​
if __name__ == "__main__":
    #测试数据输入格式
     a = []
    while len(a) < 4:
        x = np.random.randint(0, 5)
        if x not in a:
            a.append(x)
    d = ""
    for i in range(len(a)):
        d += str(a[i])
    srcLayout = d
    print('input :',srcLayout)
    destLayout = "1203"
    print('output :', destLayout)
​
    retCode, lst_steps = solvePuzzle_depth(srcLayout, destLayout)
    if retCode != 0:
        print("目标布局不可达")
    else:
        for nIndex in range(len(lst_steps)):
            print("step #" + str(nIndex + 1))
            print(lst_steps[nIndex][:2])
            print(lst_steps[nIndex][2:4])

结果:

1.

6a31692b9852e81a258f6b02cae78f91.png

2.

0530e1983422403ce639ee0182ea490c.png

两个算法比较:

宽度优先搜索:

时间复杂度

8a11dcb6f220b5f74ab84e716d2ef001.png

会产生许多不用的节点,效率较低,但是只要问题有解就能找出最优解

深度优先算法:

时间复杂度

9145635018c6e9dd4955c1a686ea84cd.png

如果目标节点不在搜索分支上,而分支不穷大,将得不到解,因此需要最大深度限制;但是如果目标节点在搜索的分枝上可以尽快的找到目标

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值