一、A*算法概述
A*(A-Star)算法是一种静态路网中求解最短路最有效的方法。公式表示为:
f(n)=g(n)+h(n)
其中f(n) 是从初始点经由节点n到目标点的估价函数g(n) 是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。保证找到最短路径(最优解的)条件,关键在于估价函数h(n)的选取:
估价值h(n)<=n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。如果 估价值>实际值, 搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
二、核心实现介绍
1. h(x)估算函数:
#计算给定节点array的估价函数h(x)
def fore_hs(array, target) :
hs= 0
fori in range(0, 9) :
if array[i] != target[i] :
hs += 1
return hs
2. 产生新节点并更新估算权值:
#从father节点产生一个新的节点,要求计算出估价函数
def insert_s(t_open, t_close, a_new,a_target, s_father) :
del_none(t_open)
del_none(t_close)
cost = s_father.get_gs()+1 + fore_hs(a_new, a_target)
if not judge_repeat(t_open,t_close, a_new, cost) :
return None
s_new = state(a_new, s_father)
s_new.set_cost(s_father.get_gs()+1, fore_hs(a_new, a_target))
return s_new
三、结果截图
# filename :chess.py
# info: A*算法求解八数码问题
class state :
def __init__(self, array, father_state) :
self.array = array
self.gs = 0#从初始节点S0到节点n的实际代价
self.hs = 0#从节点n到目标节点Sg的最优路径代价
self.close_flag = False #是否放入close表
self.father_state = father_state#此节点的父节点
def get_array(self) :
return self.array
def get_father(self) :
return self.father_state
def get_gs(self) :
return self.gs
def get_hs(self) :
return self.hs
def set_cost(self, gs, hs) :
self.gs = gs
self.hs = hs
def print_a(self) :
print('array: ', self.array, '\n gs: ', self.gs, ' hs: ', self.hs)
#将start位置和end位置的数字交换,并返回新的array
def move(array, start, end) :
a_tmp = array[:]
tmp = array[start]
a_tmp[start] = a_tmp[end]
a_tmp[end] = tmp
return a_tmp
#计算给定节点array的估价函数h(x)
def fore_hs(array, target) :
hs = 0
for i in range(0, 9) :
if array[i] != target[i] :
hs += 1
return hs
#用于open表中节点按gs+hs排序
def sor_key(t_sor) :
return t_sor.get_gs() + t_sor.hs
#用于根据close表得到求解路径
def get_path(t_close) :
s_tmp = t_close[len(t_close)-1]
t_tmp1 = []
t_tmp2 = []
while(not s_tmp==None) :
t_tmp1.append(s_tmp)
s_tmp = s_tmp.get_father()
for i in range(0, len(t_tmp1)) :
t_tmp2.append(t_tmp1[len(t_tmp1)-i-1])
for i in t_tmp2 :
i.print_a()
return t_tmp2
#删除某节点的子节点(递归删除)
def del_child(s_father) :
for i in [k for k in t_open if k.get_father() == s_father] :
del_child(i)
del t_open[t_open.index(i)]
for j in [k for k in t_open if k.get_father() == s_father] :
del_child(j)
del t_open[t_open.index(j)]
#判断节点是否重新出现,并对重新出现节点进行处理
def judge_repeat(t_open, t_close, a_new, cost) :
#若在open表中出现,并且新插入位置的代价比原代价小,则可以删除原节点,在新位置插入
if (not t_open == None) and (not t_open == []) :
for i in t_open :
if a_new==i.get_array() :
if cost >= (i.get_gs() + i.get_hs()) :
return False
else :
del t_open[t_open.index(i)]
return True#需要重新插入
# 若重复节点出现在close表中,可以改变其父节点,并删除子节点(重新求取)
if (not t_close == None) and (not t_close == []) :
for i in t_close :
if a_new==i.get_array() :
if cost >= (i.get_gs() + i.get_hs()) :
return False
else :
del_child(i)
del t_open[t_open.index(i)]
return True#需要重新插入
return True
#删除table中的None节点
def del_none(table) :
if (not table==None) and (not table==[]) :
if table[len(table)-1]==None :
table = table[:table.index(None)]
#从father节点产生一个新的节点,要求计算出估价函数
def insert_s(t_open, t_close, a_new, a_target, s_father) :
del_none(t_open)
del_none(t_close)
cost = s_father.get_gs()+1 + fore_hs(a_new, a_target)
if not judge_repeat(t_open, t_close, a_new, cost) :
return None
s_new = state(a_new, s_father)
s_new.set_cost(s_father.get_gs()+1, fore_hs(a_new, a_target))
return s_new
#用于拓展open表中current节点
def expand(t_open, t_close, s_current, a_target) :
index = s_current.get_array().index(-1)#获得空位置
move_table = {0: [[0,1], [0,3]],
1: [[1, 0], [1, 2], [1, 4]],
2: [[2, 1], [2, 5]],
3: [[3, 0], [3, 4], [3, 6]],
4: [[4, 1], [4, 3], [4, 5], [4, 7]],
5: [[5, 2], [5, 4], [5, 8]],
6: [[6, 3], [6, 7]],
7: [[7, 4], [7, 6], [7, 8]],
8: [[8, 5], [8, 7]]}
for key, value in move_table.items() :
if index == key :
for i in value :
s_tmp = insert_s(t_open, t_close, move(s_current.get_array(), i[0], i[1]), a_target, s_current)
if not s_tmp == None :
t_open.append(s_tmp)
#a--array表示节点中数的排列, t--table表示表, s--state表示节点
def answer(start) :
a_start = start
a_target = [1, 2, 3,
8, -1, 4,
7, 6, 5]
#节点s0放入open表
s_tmp = state(a_start, None)
s_current = s_tmp
s_tmp.set_cost(0, fore_hs(a_start, a_target))
t_open = [s_tmp]
t_close = []
while(True) :
#若open表为空则无解退出
if t_open==None or t_open==[] :
print("can't find the path.")
return
#将open表元素排序
t_open = sorted(t_open, key = sor_key)
t_close.append(t_open[0])
s_current = t_open[0]
del t_open[0]
#print('insert close.')
#若节点n为目标节点则结束,返回路径
if a_target == s_current.get_array() :
print("Find it!")
return get_path(t_close)
#拓展s_current节点
expand(t_open, t_close, s_current, a_target)#拓展节点函数
#print('2: ', t_open)
if __name__=='__main__' :
print('test 1 :')
answer([2, 8, 3, 1, -1, 4, 7, 6, 5])
print('\n\ntest 2 :')
answer([2, 8, 3, 1, 6, 4, 7, -1, 5])
print('\n\ntest 3 :')
answer([2, 3, -1, 1, 8, 4, 7, 6, 5])
print('\n\ntest 4 :')
answer([2, 8, 3, 1, 4, 5, -1, 7, 6])
#print('\n\ntest 4 :')
#answer([5, 3, 7, 6, 1, -1, 2, 8, 4])