A*求15码

解释一下A*算法,一种启发式搜索算法,用了一个估价函数f(n) = h(n)+g(n),其中g(n)是节点深度,h(n)是当前节点到目标节点的估计代价.特别要注意h(n),如果h(n)大于当前节点到目标节点的实际代价,那么可能就不能找到实际的最佳解决。这是因为该算法可能会将那些最优方案上的节点放在后面这样就会导致搜索到一些不那么优秀的方案.但是速度也越快.如果该h(n)小雨实际代价,那么就一定可以找到最优解,但是速度会慢.在N数码中,我们采取两个状态之间不相同数码的距离之和为h(n)(即某个数码要移动到目标位置的代价,abs(dx)+abs(dy))。g(n)是已知的,记录起来就可以了.

具体算法思路网上都有,就不说了,顺便测试了一下,确实估计代价越大速度越快,但是找出来的方案不是很优秀.

[0]除此之外,我们还需要建立路径的记录.这样可以让我们反向输出最佳反感.路径的记录用什么记录呢?我选择用python的dict(字典),因为这是动态数据结构,可以节省很多不必要的内存.假设你开一个数组(C语言),需要的大小是16!……就算是new出来的也很变态了。。。没有试过,不知道可以分配这么多不。。。

[1]另外这里采用hash查重,在python里面用dict记录访问状态就好了,把状态转换成一个字符串就可以作为key值了.为什么直接存储字符串呢?因为你用dict内存就不是我们要考虑的因素了,肯定够,所以直接转换成字符串简单点….如果你选择用康托展开来压缩状态成为一个整数的话,康托展开需要的时间更久,要一个双重循环。。。所以在内存够的情况下我们可以不压缩状态.

[2]另外我们的path只记录零点的位置,这样倒序输出就得到零点移动路径,我们就知道这个数字是怎么移动的了.

[3]写了一个formed_print()函数用来将list按照矩阵的方式输出。

[4]开头一大串是测试用例.

import os
import time
from functools import reduce
N = 16
#test case
T =   [4,7,8,3,6,13,15,2,1,11,5,12,0,14,10,9,0,-1]
#obj = [4,7,8,3,6,13,15,2,1,11,5,12,14,10,0,9,0,-1]
#T = [1,2,3,4,5,0,7,8,9,6,10,12,13,14,11,15,0,-1]
#obj = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,-1]
#obj = [4,7,8,3,6,0,15,2,1,13,5,12,14,11,10,9,0,-1]#1
#obj = [4,7,8,3,1,6,15,2,13,0,5,12,14,11,10,9] #2
#obj = [1,0,8,3,7,4,15,2,13,6,5,12,14,11,10,9]#3
#obj = [1,4,8,3,7,6,15,2,13,5,0,12,14,11,10,9]#10
#obj = [1,4,0,3,7,2,8,12,13,6,5,15,14,11,10,9]#20
#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,4,0,13,6,5,15,14,11,10,9] #31
#obj = [1,2,3,12,7,8,4,15,13,6,0,5,14,11,10,9]#33
#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

def displacement(lhs,rhs):#compare the displacement
    erro_dis = 0
    for i in range(len(lhs)-2):
        if(lhs[i]!=rhs[i]and rhs[i]!=0): 
            pos1,pos2 = lhs.index(rhs[i]),rhs.index(rhs[i])
            x1,x2,y1,y2 =  pos1//4,pos2//4,pos1%4,pos2%4
            erro_dis = erro_dis + abs(x1-x2)+ abs(y1-y2)
    return erro_dis #*3 the larger the erro_dis is, the faster the search go on, with declines in the precision/accuracy
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 get_state(node):
    sum = ''
    for i in node[0:-2]:
        sum += str(i)
    return sum

def sortedinsert(path,openlist,newnode):
    newnode_state = get_state(newnode)
    for i,opennode in   enumerate(openlist):
        if(newnode[-2]+newnode[-1]<opennode[-2]+opennode[-1]):
            openlist.insert(i,newnode)
            return
    openlist.append(newnode)

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 find_same_node(openlist,it):
    for node in openlist:
        if(node[:-2]==it[:-2]):
            return node 

def bfs():
    path = {}
    iscompleted = False
    openlist = []#the statelist
    openlist.append(T)#add the first node
    visit = {}#use visit to indicate being visited
    wait_visited ={}
    visit[get_state(T)] = 1;
    wait_visited[get_state(T)] = True#indicate the node that wait to be visited
    path[get_state(T)] = [-1,0]#four arguments means the (1)parent state,(2)pos of zero,(3)depth of node,(4)the evaluation of node
    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)
        wait_visited[state] = False#meaning the node are visited
        if(curr_node[:-2] == obj):#if the obj is found,break the loop
            return path,curr_node,curr_node.index(0),state
        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(not(next_state in visit)):#the node is not visited and not waitting to be visited
                visit[next_state] = 1
                wait_visited[next_state] = True
                path[next_state] =  [state,zero_pos]#record the state path and the solution
                node[-2],node[-1] = curr_node[-2]+1,displacement(node,obj)
                openlist.append(node)
                sortedinsert(path,openlist,node)#insert the node in approriate position
            elif(wait_visited[next_state]== True):#meaning the node is waitting to be visited
                samenode = find_same_node(openlist,node)
                if(curr_node[-2]+1+displacement(node,obj)<samenode[-2]+samenode[-1]):#the new node is better
                    node[-2],node[-1] = curr_node[-2]+1,displacement(node,obj)
                    sortedinsert(path,openlist,node)
                #add the node which estimation is less
    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('steps:',len(zero_list))
    time.sleep(3)# display the steps
    for i in range(len(zero_list)-1):
        os.system('cls')
        formed_print(T)
        time.sleep(0.1)
        T[zero_list[i]],T[zero_list[i+1]] = T[zero_list[i+1]],T[zero_list[i]]
    os.system('cls')
    formed_print(T)

print(get_state(T))
path,node,zero_pos,state = bfs()
move(path,zero_pos,state)

你可能感兴趣的:(A*求15码)