八数码问题的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!

八数码问题是一种经典的搜索问题,可以使用 A* 算法来求解。A* 算法是一种启发式搜索算法,可以在保证搜索最优解的同时尽可能减少搜索的时间和空间成本。下面是一个使用 Python 实现八数码问题 A* 算法的示例代码: ``` from queue import PriorityQueue class Puzzle: def __init__(self, board): self.board = board self.moves = [] self.cost = 0 def get_distance(self, target_board): distance = 0 for i in range(3): for j in range(3): value = self.board[i][j] if value != 0 and value != target_board[i][j]: target_i, target_j = divmod(value - 1, 3) distance += abs(i - target_i) + abs(j - target_j) return distance def get_next_puzzles(self): next_puzzles = [] i, j = next((i, j) for i in range(3) for j in range(3) if self.board[i][j] == 0) for di, dj in [(0, 1), (0, -1), (1, 0), (-1, 0)]: if 0 <= i + di < 3 and 0 <= j + dj < 3: next_board = [row[:] for row in self.board] next_board[i][j], next_board[i + di][j + dj] = next_board[i + di][j + dj], next_board[i][j] next_puzzle = Puzzle(next_board) next_puzzle.moves = self.moves + [(i + di, j + dj)] next_puzzle.cost = len(next_puzzle.moves) + next_puzzle.get_distance(target_board) next_puzzles.append(next_puzzle) return next_puzzles def __lt__(self, other): return self.cost < other.cost def solve_puzzle(start_board, target_board): start_puzzle = Puzzle(start_board) start_puzzle.cost = start_puzzle.get_distance(target_board) queue = PriorityQueue() queue.put(start_puzzle) visited = set() while not queue.empty(): puzzle = queue.get() if puzzle.board == target_board: return puzzle.moves for next_puzzle in puzzle.get_next_puzzles(): if tuple(map(tuple, next_puzzle.board)) not in visited: visited.add(tuple(map(tuple, next_puzzle.board))) queue.put(next_puzzle) return None start_board = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] target_board = [[1, 2, 3], [4, 5, 6], [7, 0, 8]] moves = solve_puzzle(start_board, target_board) if moves: for move in moves: print(move) else: print("No solution found.") ``` 在这个示例代码中,我们首先定义了一个 `Puzzle` 类来表示每个状态,其中包含了当前的棋盘状态以及移动序列和代价(即移动序列的长度加上当前状态到目标状态的估计距离)。然后我们使用 A* 算法来搜索最优解,其中使用了一个优先队列来进行状态的扩展,以保证每次扩展的状态都是代价最小的。最后,我们输出找到的移动序列,或者在无解的情况下输出提示信息。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值