BFS求解N数码(python)

用python来写可以简单语法的问题,将精力集中到算法.

[1]其他要点都和A*算法里面一样,只是求解8数码的版本用的是传统BFS,求解16数码的时候因为太大了,所以用了dict进行hash查重.同时这里是用的康托展开来压缩状态。

import os
import time
N = 9
T = [1,2,3,4,5,6,7,8,0]#the original state
obj = [2,4,3,1,6,0,7,5,8]#the final state
factory = [1, 1, 2, 6, 24, 120,720, 5040,40320]
def formed_print(L):
    n = 0;
    for i in range(0,7,3):
        t = L[i:i+3]
        print(t[0],t[1],t[2])
    print('\n')

def get_state(node):
    used,sum = [0]*9,0
    for pos,i in enumerate(node):
        num = 0
        used[i] = 1
        for k in range(0,i):
            if(used[k]==0):num += 1
        sum += num*factory[8-pos]
    return sum

def find_next_nodes(curr_node):
    def swap(L,i,j):#return a list with swapped items
        temp = L[::]
        temp[i],temp[j] = temp[j],temp[i]
        return temp
    pos = curr_node.index(0)#find the position of 0
    i,j = pos//3,pos%3#convert pos to grid coordinates
    nextnodes = []
    if(i!=2):nextnodes.append(swap(curr_node,pos,pos+3))
    if(i!=0):nextnodes.append(swap(curr_node,pos,pos-3))
    if(j!=2):nextnodes.append(swap(curr_node,pos,pos+1))
    if(j!=0):nextnodes.append(swap(curr_node,pos,pos-1))
    return pos,nextnodes


def bfs():
    path = [[] for i in range(362880)]
    iscompleted = False
    openlist = []#the statelist
    visit = [0]*362880
    openlist.append(T)#add the first node
    visit[get_state(T)] = 1
    path[get_state(T)] = [-1,0]
    count = 100000;
    while(len(openlist)!=0 and count>0):#if the open list is empty,loop ends
        count -= 1
        curr_node = openlist[0]
        state = get_state(curr_node)
        openlist.remove(curr_node)
        if(curr_node == obj):#if the obj is found,break the loop
            iscompleted = True;
            return path,curr_node.index(0),state
            break;
        zero_pos,nextnodes = find_next_nodes(curr_node)#return all nodes near to 0 and pos of 0
        for node in nextnodes:
            next_state = get_state(node)
            if(visit[next_state]==0):#ensure the node is not visited
                openlist.append(node)
                visit[next_state] = 1
                path[next_state] =  [state,zero_pos]#record the state path and the solution
                #print(path[next_state])
    return False
def move(path,zero_pos,state):
    zero_list = [zero_pos]
    while(path[state][0]!=-1):
        zero_list = [path[state][1]]+zero_list
        state = path[state][0]
    for i in range(len(zero_list)-1):
        os.system('cls')
        formed_print(T)
        time.sleep(0.5)
        T[zero_list[i]],T[zero_list[i+1]] = T[zero_list[i+1]],T[zero_list[i]]
    os.system('cls')
    formed_print(T)

path,zero_pos,state = bfs()
move(path,zero_pos,state)

这里是求解16数码的代码,相比8数码,做出了一些改进.首先一行代码就可以写出一个0到N的阶乘的list,这样我们修改N的大小时候就只要修改N就可以了.然后我采用了一个方法加快速度,就是测量这个状态的价值,然后插入到队列里面.但是这个时候我还没有看过A*算法的实现细节,所以只是简单的进行插入队列,不过比起直接的BFS还是要快不少了.(当然不一定最优)

import os
import time
from functools import reduce
N = 16
T = [4,7,8,3,6,13,15,2,1,11,5,12,0,14,10,9] 

#obj = [4,7,8,3,1,6,15,2,13,0,5,12,14,11,10,9]#2 
#obj = [1,2,4,3,7,8,0,12,13,6,5,15,14,11,10,9] #25s
#obj = [1,2,0,3,7,8,4,12,13,6,5,15,14,11,10,9]#29
#obj = [1,2,3,12,7,8,15,0,13,6,4,5,14,11,10,9]  #34#
#obj = [1,2,15,3,7,8,4,12,13,6,0,5,14,11,10,9] #39
#obj = [1,2,3,4,7,8,15,12,13,6,5,0,14,11,10,9] #45
#obj = [1,2,3,4,7,8,15,0,13,6,5,12,14,11,10,9] #47
#obj = [1,2,3,4,7,8,0,15,13,6,5,12,14,11,10,9]#48 
#obj = [1,2,3,4,7,8,5,15,13,6,0,12,14,11,10,9]#49 
#obj =[1,2,3,4,7,8,5,15,13,6,12,0,14,11,10,9]  #50
obj = [1,2,3,4,5,13,6,15,14,7,11,10,0,9,8,12]#59
#obj = [1,2,3,4,5,13,6,15,14,11,0,10,9,7,8,12]#the final state

 factory = list(map(lambda x:reduce(lambda f,n:f*n,range(1,x+1),1),range(8)))##一行代码解决阶乘数组
def formed_print(L):
    n = 0;
    for i in range(0,13,4):
        t = L[i:i+4]
        print(t[0],t[1],t[2],t[3])
    print('\n')

def displacement(lhs,rhs):#compare the displacement
    count = 0
    for i in range(len(lhs)):
        if(lhs[i]!=rhs[i]): count += 1
    return count

def get_state(node):
    #used,sum = [0]*N,0
    #for i in node:
        #num = 0
        #used[i] = 1
        #for k in range(0,i):
            #if(used[k]==0):num += 1
        #sum += num*factory[N-i-1]
    sum = ''
    for i in node:
        sum += str(i)
    return sum

def find_next_nodes(curr_node):
    def swap(L,i,j):#return a list with swapped items
        temp = L[::]
        temp[i],temp[j] = temp[j],temp[i]
        return temp
    pos = curr_node.index(0)#find the position of 0
    i,j = pos//4,pos%4#convert pos to grid coordinates
    nextnodes = []
    if(i!=3):nextnodes.append(swap(curr_node,pos,pos+4))
    if(i!=0):nextnodes.append(swap(curr_node,pos,pos-4))
    if(j!=3):nextnodes.append(swap(curr_node,pos,pos+1))
    if(j!=0):nextnodes.append(swap(curr_node,pos,pos-1))
    return pos,nextnodes


def bfs():
    path = {}
    iscompleted = False
    openlist = []#the statelist
    openlist.append(T)#add the first node
    visit = {}#use visit to indicate being visited
    visit[get_state(T)] = 1;
    print(visit)
    path[get_state(T)] = [-1,0]
    while(len(openlist)!=0):#if the open list is empty,loop ends
        curr_node = openlist[0]
        state = get_state(curr_node)
        openlist.remove(curr_node)
        if(curr_node == obj):#if the obj is found,break the loop
            iscompleted = True;
            return path,curr_node,curr_node.index(0),state
            break;
        zero_pos,nextnodes = find_next_nodes(curr_node)#return all nodes near to 0 and pos of 0
        for node in nextnodes:
            if(node == obj):formed_print(node)
            next_state = get_state(node)
            if(not(next_state in visit)):#ensure the node is not visited
                visit[next_state] = 1
                path[next_state] =  [state,zero_pos]#record the state path and the solution
                if(node == obj):openlist.insert(0,node)
                elif(displacement(node,obj)<=2):openlist.insert(1,node)
                elif(displacement(node,obj)<=4):openlist.insert(2,node)
                elif(displacement(node,obj)<=6):openlist.insert(3,node)
                else:openlist.append(node)
    return False
def move(path,zero_pos,state):
    zero_list = [zero_pos]
    while(path[state][0]!=-1):
        zero_list = [path[state][1]]+zero_list
        state = path[state][0]
    print(len(zero_list))
    return
    for i in range(len(zero_list)-1):
        os.system('cls')
        formed_print(T)
        #time.sleep(0.00001)
        T[zero_list[i]],T[zero_list[i+1]] = T[zero_list[i+1]],T[zero_list[i]]
    os.system('cls')
    formed_print(T)

path,node,zero_pos,state = bfs()
move(path,zero_pos,state)

你可能感兴趣的:(ACM)