八数码问题的Python实现(A算法及A*算法)

一、A算法代码如下:

#! python3
#名称:八数码问题算法
#用途:输入八数码初始状态和预期状态,搜索其解
#节点基本序列:字符0-8,以及字符' '。
#节点数据结构:本节点序列、父节点序列、本节点与目标的偏差、本节点ID、父节点ID
#其中节点ID为全局唯一。
#
#版本:1.0
#更新日期:2018.11.19
#实现方式:采用A算法,广度优先方式
import copy
import os
import time

#将一个八数码序列用3x3的阵列打印出来
def prtNum(src):
    for x in range(3):
        for y in range(3):
            print(str(src[x*3+y]+' '),end='')
        print()

#判断两个数码序列之间的差别,完全相同返回0,有一个字符不相同则返回值+1
def diff(src,dst):
  total=0
  for x in range(len(src)):
    if src[x]!=dst[x]:
      total=total+1
  return total

#返回一个序列中空‘’在序列中的位置,以及在3x3阵列中的行、列位置。
def position(src):
    flag=src.index(' ')
    row=int(flag/3)
    col=int(flag%3)
    return[flag,row,col]

#将一个3x3阵列中两个位置的数值调换,并返回调换后的序列
def exchange(src,x,y,x2,y2):
    flag=x*3+y
    flag2=x2*3+y2
    tmp1=src[flag]
    tmp2=src[flag2]
    dst=copy.copy(src)
    dst[flag]=tmp2
    dst[flag2]=tmp1
    return dst

#用于判定一个序列状态能否变换成另一个序列状态的依据,
#根据理论,转换前后的序列应具有这个特征:假设flag(n)等于
#该序列中数字n前面所有大于它的数字的和,则可以转换的两个序列
#其flag(1)+...+flag(8)之后的奇偶性应该相同。例如,原始状态
#1234 5678的flag()之和为:0, 其可以转换为12345 678,flag()之和
#也为0,因此互相可以转化。
#本函数初始化序列后,返回某个序列的flag()之和。
#

def judge(number):
        total=0
        data=[9,9,9,9,9,9,9,9,9]
        for i in range(9):
                if number[i]!=' ':
                        data[i]=int(number[i])
                else:
                        data[i]=0
#        print('number is',number)
#        print('data is',data)
        for i in range(9):
                for j in range(i):
                        if data[i] * data[j]!=0:
                                if data[j] > data[i]:
                                        total=total+1
#                print(i,total)
        return total

#用于处理Open表
#方法是:如果Open表非空,则:
#1)按照顺序对open表的每个node中的空格进行所有方向的移动,
#将移动后的新状态节点添加进open表;如果过程中找到了满足条件
#的目的状态节点,则停止处理并返回打印结果;
#如果新获得的序列已存在与open、close表,则不再添加。
#2)将该节点加入close表;
#3)从open表中删除该节点;
#
def handleOpen():
    global nodeid
    global open
    while True:
        if len(open)==0:
                break
#       x=0
        for x in range(len(open)):
          tmp=move(open[x][0],'')
#          print(tmp)
#          print(open)
#          print('tmp length is',len(tmp))
          for y in range(len(tmp)):
                  flag=False
                  for jj in range(len(open)):
#                        print('tmp[y][0]is',tmp[y][0])
#                        print('open[x][0]is',open[x][0])
                        if tmp[y][0]==open[jj][0]:
                                flag=True
#                                print('falg open set to True')
                  for kk in range(len(closed)):
#                         print('tmp[',y,'][0]is',tmp[y][0])
#                         print('closed[',kk,'][0]is',closed[kk][0])
                         if tmp[y][0]==closed[kk][0]:
                                flag=True
#                                print('falg close set to True')
                  if flag==False:
                        open.append([tmp[y][0],tmp[y][1],tmp[y][2],tmp[y][3],open[x][3]])
#                        print('add open node',open[-1])
#                  else:
#                        print('node',tmp[y][0], 'already exists in open or closed!')

                  if tmp[y][2]==0:
                    closed.append(open[x])
                    closed.append(open[-1])
                    open.remove(open[x])
#                    print('add close node',open[x])
                    print('Totally',nodeid,'nodes ayalyzed,find the result.')
                    prtResult()
                    print('Success!')
                    exit("We find it!")
          closed.append(open[x])
#          print('add close node',open[x])
          open.remove(open[x])
          
#基于输入的序列进行移动,并返回所有可能的移动后目的序列;
#每条数据:节点序列、前一节点序列、与目标序列偏差值、当前节点序列ID
def move(src,side):
    global crt
    global nodeid
    pos=position(src)
    flag=pos[0]
    x=pos[1]
    y=pos[2]
    leftDiff=999
    rightDiff=999
    upDiff=999
    downDiff=999
#    print('Node being analyzed is:')
#    prtNum(src)
    rtResult=[]
    if side=='left' or side=='':
      if y>0:
        crtLeft=exchange(src,x,y,x,y-1)
#        print('Can move to LEFT,after move result is:')
#        prtNum(crtLeft)
        leftDiff=diff(numberFinal,crtLeft)
#        print('different factor is',leftDiff)
#        addOpen(crtLeft,src,leftDiff)
#        return [crtLeft,src,leftDiff]
        nodeid=nodeid+1
        rtResult.append([crtLeft,src,leftDiff,nodeid])
#      else:
#        print('Cannot move to LEFT!')

    if side=='right' or side=='':
      if y<2:
        crtRight=exchange(src,x,y,x,y+1)
#        print('Can move to Right,after move result is:')
#        prtNum(crtRight)
        rightDiff=diff(numberFinal,crtRight)
#        print('different factor is',rightDiff)
#        return(crtRight,src,rightDiff)
        nodeid=nodeid+1
        rtResult.append([crtRight,src,rightDiff,nodeid])
#      else:
#        print('Cannot move to RIGHT!')

    if side=='up' or side=='':
      if x>0:
#        print('Can move to UP,after move result is:')
        crtUp=exchange(src,x,y,x-1,y)
#        prtNum(crtUp)
        upDiff=diff(numberFinal,crtUp)
#        print('different factor is',upDiff)
#        return(crtUp,src,upDiff)
        nodeid=nodeid+1
        rtResult.append([crtUp,src,upDiff,nodeid])
#      else:
#        print('Cannot move to UP!')

    if side=='down' or side=='':
      if x<2:
#        print('Can move to DOWN,after move result is:')
        crtDown=exchange(src,x,y,x+1,y)
#        prtNum(crtDown)
        downDiff=diff(numberFinal,crtDown)
#        print('different factor is',downDiff)
#        return(crtDown,src,downDiff)
        nodeid=nodeid+1
        rtResult.append([crtDown,src,downDiff,nodeid])
#      else:
#        print('Cannot move to DOWN!')
    if nodeid%1000>=0 and nodeid%1000 < 3:
            print(int(nodeid/1000)*1000,'nodes analyzed!')
    return rtResult

#打印结果,方法是从close表最后一条开始,查找其前一个节点,
#直到前一节点为0,并将所有查到的序列写入step,打印出step
#即得到所有的变化过程。
def prtResult():
      step=[closed[-1]]
      nodePrt=closed[-1][4]
      while True:
            for x in range(len(closed)):
                  if nodePrt==closed[x][3]:
                        step.insert(0,closed[x])
                        nodePrt=closed[x][4]
            if nodePrt==0:
                  break            
      for x in range(len(step)):
            print('Step',x,':')
            prtNum(step[x][0])
      print('Finished!')
      time.sleep(10)
            
#numberOrig=['1','2','3','4','7',' ','6','5','8']

#numberOrig=[' ','7','2','5','1','6','8','3','4']

#numberOrig=['4','1','6','7','2','8','5',' ','3']

#numberFinal=['1','2','3','4','5','6','7','8',' ']

open=[]

closed=[]

nodeid=1


#主程序
#输入初始和目标序列,并打印出来供确认,如不正确可重新输入
while True:
        print('Please input Original state:',end='\t')
        tmp=input()
        numberOrig=[tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5],tmp[6],tmp[7],tmp[8]]
        print('Please input Final state:',end='\t')
        tmp=input()
        numberFinal=[tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5],tmp[6],tmp[7],tmp[8]]
        print('Orig is')
        prtNum(numberOrig)
#        print('Orig judge is',judge(numberOrig))
        print('Final is')
        prtNum(numberFinal)
#        print('Final judge is',judge(numberFinal))
        print('Is it correct?',end='\t')
        confirm=input()
        if confirm=='y':
              break
#如果初始和目标序列的判定值奇偶性一致,则存在解,开始处理
if (judge(numberOrig)+judge(numberFinal))%2==0:
        print('Have answer! Orig is ',judge(numberOrig),', Final is',judge(numberFinal))
#处理方式:将初始节点加入open表,开始处理。
        open.append([numberOrig,'NULL',diff(numberOrig,numberFinal),1,0])
        handleOpen()
#否则,不存在解,直接退出。
else:
        print('No answer! Orig is ',judge(numberOrig),', Final is',judge(numberFinal))

二、运行示意如下:

1、无解的情况:

Please input Original state:    1234 5678
Please input Final state:    123 45687
Orig is
1 2 3
4   5
6 7 8
Final is
1 2 3
  4 5
6 8 7
Is it correct?    y
No answer! Orig is  0 , Final is 1

2、有解的情况:

====================== RESTART: C:\Python34\bashuma.py ======================
Please input Original state:    1234 5678
Please input Final state:    12 345678
Orig is
1 2 3
4   5
6 7 8
Final is
1 2   
3 4 5
6 7 8
Is it correct?    y
Have answer! Orig is  0 , Final is 0
1000 nodes analyzed!
2000 nodes analyzed!
2000 nodes analyzed!
3000 nodes analyzed!
4000 nodes analyzed!
5000 nodes analyzed!
6000 nodes analyzed!
7000 nodes analyzed!
Totally 7440 nodes ayalyzed,find the result.
Step 0 :
1 2 3
4   5
6 7 8
Step 1 :
1 2 3
  4 5
6 7 8
Step 2 :
  2 3
1 4 5
6 7 8
Step 3 :
2   3
1 4 5
6 7 8
Step 4 :
2 3   
1 4 5
6 7 8
Step 5 :
2 3 5
1 4   
6 7 8
Step 6 :
2 3 5
1   4
6 7 8
Step 7 :
2   5
1 3 4
6 7 8
Step 8 :
  2 5
1 3 4
6 7 8
Step 9 :
1 2 5
  3 4
6 7 8
Step 10 :
1 2 5
3   4
6 7 8
Step 11 :
1 2 5
3 4   
6 7 8
Step 12 :
1 2   
3 4 5
6 7 8
Finished!
Success!
>>>

三、改进:A*算法的实现
A*算法,实际是在将新节点插入open表时,按照估值函数的大小,将更优的节点插入到更靠前的位置。
在A算法的基础上,做少量修改即可。
1、增加addOpen函数
#V1.1 新增,用于将新的节点按照判定值(diff值)插入现有open表
#判定值小的node靠前
def addOpen(node):
        if len(open)==0 or node[2]>=open[-1][2]:
            open.append(node)
#            print('append open',node)
        else:
            for i in range(len(open)):
                if node[2]<open[i][2]:
                    open.insert(i,node)
                    break

2、在handleOpen()中做适当修改即可,handleOpen函数修改为如下内容:

def handleOpen():
    global nodeid
    global open
    while True:
          if len(open)==0:
                break
#v1.1修改
          x=0
#        for x in range(len(open)):
          tmpOpen=open[0]
          
          tmp=move(open[0][0],'')
#          print(tmp)
#          print(open)
#          print('tmp length is',len(tmp))
          for y in range(len(tmp)):
                  flag=False
                  for jj in range(len(open)):
#                        print('tmp[y][0]is',tmp[y][0])
#                        print('open[x][0]is',open[x][0])
                        if tmp[y][0]==open[jj][0]:
                                flag=True
#                                print('falg open set to True')
                  for kk in range(len(closed)):
#                         print('tmp[',y,'][0]is',tmp[y][0])
#                         print('closed[',kk,'][0]is',closed[kk][0])
                         if tmp[y][0]==closed[kk][0]:
                                flag=True
#                                print('falg close set to True')
                  if flag==False:

#V1.1 修改
#                        open.append([tmp[y][0],tmp[y][1],tmp[y][2],tmp[y][3],open[x][3]])
                         addOpen([tmp[y][0],tmp[y][1],tmp[y][2],tmp[y][3],open[x][3]])

#                        print('add open node',open[-1])
#                  else:
#                        print('node',tmp[y][0], 'already exists in open or closed!')

                  if tmp[y][2]==0:
#V1.0
#                    closed.append(open[x])
#                    closed.append(open[-1])
#                    open.remove(open[x])
#                    print('add close node',open[x])
#V1.1
                    closed.append(tmpOpen)
                    closed.append(open[0])
                    open.remove(open[0])
#                    print('add close node',open[x])

                    print('Totally',nodeid,'nodes ayalyzed,find the result.')
                    prtResult()
                    print('Success!')
                    exit("We find it!")
#V1.0          closed.append(open[x])
#V1.1
          closed.append(tmpOpen)
#          print('add close node',open[x])
#v1.0      open.remove(open[x])
#V1.1
          open.remove(tmpOpen)

对比:A*算法后者可大大提高计算效率和速度,以原始状态1234 5678,目标状态8765 4321为例,
A*算法通过分析5028个节点,很快可得到结果(转换需要72步,如下),但A算法分析10万个以上的节点也无法得到结果。
Please input Original state:    1234 5678
Please input Final state:    8765 4321
Orig is
1 2 3
4   5
6 7 8
Final is
8 7 6
5   4
3 2 1
Is it correct?    y
Have answer! Orig is  0 , Final is 28
1000 nodes analyzed!
1000 nodes analyzed!
2000 nodes analyzed!
3000 nodes analyzed!
4000 nodes analyzed!
5000 nodes analyzed!
Totally 5028 nodes ayalyzed,find the result.
Step 0 :
1 2 3
4   5
6 7 8
Step 1 :
1 2 3
  4 5
6 7 8
Step 2 :
  2 3
1 4 5
6 7 8
Step 3 :
2   3
1 4 5
6 7 8
Step 4 :
2 3   
1 4 5
6 7 8
Step 5 :
2 3 5
1 4   
6 7 8
Step 6 :
2 3 5
1   4
6 7 8
Step 7 :
2   5
1 3 4
6 7 8
Step 8 :
  2 5
1 3 4
6 7 8
Step 9 :
1 2 5
  3 4
6 7 8
Step 10 :
1 2 5
3   4
6 7 8
Step 11 :
1 2 5
3 7 4
6   8
Step 12 :
1 2 5
3 7 4
  6 8
Step 13 :
1 2 5
  7 4
3 6 8
Step 14 :
1 2 5
7   4
3 6 8
Step 15 :
  2 5
1 7 4
3 6 8
Step 16 :
2   5
1 7 4
3 6 8
Step 17 :
2 7 5
1   4
3 6 8
Step 18 :
2 7 5
1 6 4
3   8
Step 19 :
2 7 5
1 6 4
3 8   
Step 20 :
2 7 5
1 6   
3 8 4
Step 21 :
2 7 5
1   6
3 8 4
Step 22 :
2   5
1 7 6
3 8 4
Step 23 :
2 5   
1 7 6
3 8 4
Step 24 :
2 5 6
1 7   
3 8 4
Step 25 :
2 5 6
1   7
3 8 4
Step 26 :
2 5 6
1 7 4
3 8   
Step 27 :
2 5 6
1 7 4
3   8
Step 28 :
2 5 6
1   4
3 7 8
Step 29 :
2 5 6
  1 4
3 7 8
Step 30 :
  5 6
2 1 4
3 7 8
Step 31 :
5   6
2 1 4
3 7 8
Step 32 :
5 1 6
2   4
3 7 8
Step 33 :
5 1 6
2 7 4
3   8
Step 34 :
5 1 6
2 7 4
  3 8
Step 35 :
5 1 6
  7 4
2 3 8
Step 36 :
5 1 6
7   4
2 3 8
Step 37 :
  1 6
5 7 4
2 3 8
Step 38 :
1   6
5 7 4
2 3 8
Step 39 :
1 7 6
5   4
2 3 8
Step 40 :
1 7 6
5 3 4
2   8
Step 41 :
1 7 6
5 3 4
  2 8
Step 42 :
1 7 6
5 3 4
2 8   
Step 43 :
1 7 6
5 3   
2 8 4
Step 44 :
1 7 6
5   3
2 8 4
Step 45 :
1 7 6
5 8 3
2   4
Step 46 :
1 7 6
5 8 3
  2 4
Step 47 :
1 7 6
  8 3
5 2 4
Step 48 :
1 7 6
8   3
5 2 4
Step 49 :
1   6
8 7 3
5 2 4
Step 50 :
  1 6
8 7 3
5 2 4
Step 51 :
8 1 6
  7 3
5 2 4
Step 52 :
8 1 6
7   3
5 2 4
Step 53 :
8 1 6
7 3   
5 2 4
Step 54 :
8 1 6
7 3 4
5 2   
Step 55 :
8 1 6
7 3 4
5   2
Step 56 :
8 1 6
7   4
5 3 2
Step 57 :
8 1 6
  7 4
5 3 2
Step 58 :
8 1 6
5 7 4
  3 2
Step 59 :
8 1 6
5 7 4
3   2
Step 60 :
8 1 6
5 7 4
3 2   
Step 61 :
8 1 6
5   4
3 7 2
Step 62 :
8   6
5 1 4
3 7 2
Step 63 :
8 6   
5 1 4
3 7 2
Step 64 :
8 6 4
5 1   
3 7 2
Step 65 :
8 6 4
5   1
3 7 2
Step 66 :
8 6 4
5 7 1
3   2
Step 67 :
8 6 4
5 7 1
3 2   
Step 68 :
8 6 4
5 7   
3 2 1
Step 69 :
8 6 4
5   7
3 2 1
Step 70 :
8 6   
5 7 4
3 2 1
Step 71 :
8   6
5 7 4
3 2 1
Step 72 :
8 7 6
5   4
3 2 1
Finished!
Success!

  • 11
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: a*算法是一种启发式搜索算法,可以用于解决八数码问题八数码问题是一种经典的游戏,目标是将一个3*3的九宫格中的数字按照特定顺序排列,其中一个格子为空。每次操作可以将与空格相邻的一个数字移动到空格中,直到达到目标状态。 使用a*算法解决八数码问题主要有以下几个步骤: 1. 定义状态空间:将九宫格中的数字排列看作状态,即每个状态由一个长度为9的数组来表示。 2. 定义启发函数:启发函数用来评估当前状态距离目标状态的距离,通常使用曼哈顿距离或欧几里得距离。在八数码问题中,使用曼哈顿距离计算两个状态之间的距离。 3. 定义操作:定义将一个数字移动到空格中的操作,以及对应的代价。在八数码问题中,每次操作的代价都为1。 4. 使用优先队列存储状态:使用优先队列存储每个状态以及与起始状态的距离 + 启发函数值。 5. 开始搜索:从初始状态开始,每次取出距离 + 启发函数值最小的状态,并对其进行操作,得到一系列可能的下一个状态。将这些状态加入优先队列,并继续搜索,直到找到目标状态。 6. 输出解:当找到目标状态后,可以通过反向遍历得到从目标状态到初始状态的一条路径,即为解。将路径输出即可。 使用Python实现a*算法解决八数码问题具体实现可以参考相关教程或代码库。 ### 回答2: 在八数码问题中,有一个3x3的矩阵,其中包含1-8号数字,以及一个空位。基本目标是将矩阵重排、使得排列成指定的形式。 a*算法,是一种基于启发式搜索的算法,它可以在有较大状态空间的问题中找到最优解。在求解八数码问题时,a*算法可以被用来搜索空位所处位置的不同状态,并采用估价函数来判断哪些状态更有可能走向正确的解决方案。 基于估价函数,a*算法被用来搜索状态时已经做好了最小化搜索路径长度的准备,也就是说,它可以尽可能快地找到最优解。 实现a*算法解决八数码问题Python代码,可以分多层解决。首先,需要定义一个函数,用于获取空格的位置。通过该函数,可以确定出当前状况空格往四个方向所能到达的状态。 下一步,需要判断每一个移动后的状态是否合法。移动状态需要计算出一个估价函数的值,来评估该状态是否最有可能走向目标正确状态。 用Python实现时,估价函数可以定义为当前状态离目标状态越远,则评估函数值越大。估价函数的实现可以使用曼哈顿距离来计算两个状态之间的距离。 接下来,通过a*算法进行搜索,直到找到最优解。在算法中,首先通过一个优先级队列(priority queue)来对状态进行排序和筛选。在每一个移动后的状态中,选择估价函数值最小的状态进行搜索。最终,可以找到最优的解决方案。 ### 回答3: A*算法是一种用于路径规划的算法,它能够有效地解决八数码问题八数码问题是指在 3×3 的九宫格中,一个初始状态可以移到目标状态的谜题。在八数码问题中,每个格子可以放置数字1-8或空格,规则是只能上下左右移动,将空格移到目标状态,同时保证空格移动路径最短。 在Python中,构建A*算法解决八数码问题的步骤如下: 1.构建初始的状态和目标状态 定义一个 3 * 3 的列表,用0表示空格,用1-8表示数字。例如,一个样例状态为:[1,2,3,4,5,6,0,7,8]。 2.计算需要移动的步数 通过计算当前状态和目标状态之间不同的数字的个数,即曼哈顿距离(Manhattan distance),来计算出当前状态的评估函数(f(n))。 3.确定移动的方向 向当前空格的周围四个方向依次移动,计算移动后的状态的评估函数f(n)。 4.加入已探索列表 将移动后的状态加入已探索的状态列表中。 5.重复步骤2-4,直到找到目标状态。 如果当前状态和目标状态一致,则搜索结束。否则,重复步骤2-4直到找到目标状态。此时,需要返回最短路径。 6.输出最终答案 输出从初始状态到目标状态的路径。 总体来说,A*算法是一种有效的搜索算法,在处理八数码问题中有着不错的应用效果。在实现A*算法时,要注意选择正确的数据结构和算法实现方法,并严格控制代码的时间复杂度,以提高算法的效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值