启发式搜索中的全局择优搜索解决8数码问题
求解算法设计
全局优先搜索算法
(1) 把初始节点S0放入OPEN表,f(S0);
(2) 如果OPEN表为空,则问题无解,退出;
(3) 把OPEN表的第一个节点(记为节点n)取出放入CLOSED表;
(4) 考察节点n是否为目标节点:若是,则求得问题的解,退出;
(5) 若节点n不可扩展,则转第(2)步;
(6) 扩展节点n,用估计函数f(x)计算每个子节点的估价值,并为每个子节点配置指向父节点的指针;把这些子节点都送入OPEN表中,然后对OPEN表中的全部节点按估价值从小到大的顺序进行排序,然后转第(2)步。
节点扩展算法
(1)如图1,定义8数码的索引号。
(2)定义0代表8数码中的空格。0的移动即为空格的移动。
(3)定义:-3(0向上移),3(0向下移动),-1(0向左移动),1(向右移动)
(4)如果0号节点的索引除以3不为0,且该节点不是由父节点向右得到的,则该节点可以向左移动。
(5)如果0号节点的索引+1除以3不为0,且该节点不是由父节点向左得到的,则该节点可以向右移动。
(6)如果0号节点的索引大于2,且该节点不是由父节点向下得到的,则该节点可以向上移动。
(7)如果0号节点的索引小于6,且该节点不是由父节点向上得到的,则该节点可以向下移动。
两个点的最短移动距离
将indexA作为较小值
如果indexB-indexA≥3,result=1+(indexA+3与indexB的曼哈顿距离);
如果indexB-indexA=2,result=2;
如果indexB-indexA=0,result=0;
如果以上均不符合,那么计算(indexA+1)模3的值,如果为0,result取3,否则取1。
求解算法实现
8数码结构
将8数码保存在列表中,并将列表置于当前节点中。
self.S0=[] #初始状态
self.Sg=[1,2,3,8,0,4,7,6,5] #目标状态
node.list=[] 当前节点的8数码
节点数据结构
将Node类作为节点的数据,其包含的数据如下:
class Node():
def init(self):
self.index=0 #节点的序号,作为节点的唯一标识
self.parentIndex=0 #节点父亲的序号
self.zeroIndex=0 #0号的位置
self.list=[] #该节点的8数码(type=list)
self.fund=0 #该节点的估计值
self.depth=0 #该节点的深度
self.direction=0 #该节点有父亲节点如何移动得到 -3(0向上移),3(0向下移动),-1(0向左移动),1(向右移动)
扩展实验结果比对
用例解路径<6
用例解路径>10
用例解路径>20
启发函数3在不同权重下的结果
实验代码
'''
Created on 2020年10月24日
启发式搜素——全局优先搜索解决8数码问题
@author: xxxxxx
'''
import GlobalOptimal as GO
if __name__=='__main__':
glo=GO.globalOptimal()
#list=[1,3,2,4,0,5,6,7,8 ]
print("输入起始状态")
for i in range(9):
a=input("第"+str(i)+"个数")
list.append(int(a))
glo.start(S0=list)
'''
Created on 2020年10月24日
保存待扩展的节点
@author: xxxxxxxx
'''
import operator
import NODE as Node
class Open():
def __init__(self):
self.nodelist=[] #保存节点的列表
self.cmpfun = operator.attrgetter('fund','depth') #open表排序条件 估计值优先,其次是深度
def sortfun(self):
'''
对open表进行排序
:return:
'''
#根据条件对open表进行排序
self.nodelist.sort(key=self.cmpfun)
def append(self,node):
'''
向节点列表中添加节点
:param node:
:return:
'''
#节点列表中添加一个新的节点
self.nodelist.append(node)
#对节点列表(open表)进行排序
self.sortfun()
def pop(self):
'''
从open表中取一个节点
:return:node
'''
#从open表中取出一个节点
return self.nodelist.pop(0)
def isEmpty(self):
'''
判断open表是否为空
:return: true 为空,false 不为空
'''
if self.nodelist:
return False
else:
return True
'''
Created on 2020年10月24日
保存节点的相关数据
@author: xxxxxxxx
'''
class Node():
def __init__(self):
self.index=0 #节点的序号,作为节点的唯一标识
self.parentIndex=0 #节点父亲的序号
self.zeroIndex=0 #0号的位置
self.list=[] #该节点的8数码(type=list)
self.fund=0 #该节点的估计值
self.depth=0 #该节点的深度
self.direction=0 #该节点有父亲节点如何移动得到 -3(0向上移),3(0向下移动),-1(0向左移动),1(向右移动)
def construction(self,index,parentIndex,list,fund,depth,direction):
'''
对节点进行构造
'''
self.index=index
self.parentIndex=parentIndex
self.list=list
self.fund=fund
self.depth=depth
self.direction=direction
self.zeroIndex=self.list.index(0)
'''
Created on 2020年10月24日
全局优先搜锁
@author: xxxxxx
'''
import time
import OPEN as Open
import NODE as Node
import HFunction as hf
class globalOptimal():
'''
全局优先搜索
'''
def __init__(self):
self.open=Open.Open()
self.S0=[] #初始状态
self.Sg=[1,2,3,8,0,4,7,6,5] #目标状态
self.Sindex=-1 #记录最后一个节点序号 -1尚没有节点,0初始节点
self.closed={}
self.targetPath=[] #解路径
def constructionNode(self,Sn,depth,parentIndex,direction):
'''
构造节点
:param Sn: 该节点的8数码列表
:param depth: 该节点的深度
:param parentIndex: 该节点的父亲节点序号
:param direction: 该节点如何得到
:return:
'''
#新创建一个节点
newnode=Node.Node()
#为该节点分配一个序号
self.Sindex=self.Sindex+1
#计算机该节点的估计值
hun=hf.hfun_3(Sn)
fund=hun+depth
#构造该节点
newnode.construction(index=self.Sindex,parentIndex=parentIndex,list=Sn,fund=fund,depth=depth,direction=direction)
#将该节点加入open表
self.open.append(node=newnode)
def closedPush(self,node):
'''
将从open表中取出的节点放到closed表中
:param node: 要放置的节点
:return:
'''
index=node.index
self.closed[index]=node
def isTarget(self,node):
'''
判断是否为目标解
:param node: 当前节点
:return: true,是目标节点,false 不是目标节点
'''
for i in range(9):
if node.list[i]!=self.Sg[i]:
return False
return True
def getTarget(self,node):
'''
求取解路径
:param node:最终解的节点
:return:
'''
self.targetPath.append(node) #将解节点放入路径表中
while node.parentIndex!=-1: #当路径上的节点还有父节点时
index=node.parentIndex #获取父节点序号
node=self.closed[index] #获取父节点
self.targetPath.append(node) #将节点加入路径表
def moveNoed(self,parNode,direction):
'''
8数码中空白格的移动
:param parNode: 待移动的节点(父节点
:param direction: 需要移动的方向
:return:
'''
#构造移动后的8数码
zeroIndex=parNode.zeroIndex
ss=[i for i in parNode.list]
tem=ss[zeroIndex+direction]
ss[zeroIndex]=tem
ss[zeroIndex+direction]=0
#构造新节点
self.constructionNode(Sn=ss,depth=parNode.depth+1,parentIndex=parNode.index,direction=direction)
def extendNode(self,node):
'''
向下扩展的节点
:param node: 待扩展的节点
:return:
'''
zeroindex=node.zeroIndex
#向左移动
if zeroindex%3!=0 and node.direction!=1:
self.moveNoed(node,direction=-1)
#向右移动
if (zeroindex+1)%3!=0 and node.direction!=-1:
self.moveNoed(node,direction=1)
#向上移动
if zeroindex>2 and node.direction!=3:
self.moveNoed(node,direction=-3)
#向下移动
if zeroindex<6 and node.direction!=-3:
self.moveNoed(node,direction=3)
def solve(self):
'''
对问题进行求解
:return:
'''
# 构造初始节点,并将其加入open表
self.constructionNode(Sn=self.S0,depth=0,parentIndex=-1,direction=0)
#open表不为空,继续执行
while(self.open.isEmpty()==False):
#取open表中的第一个元素
node=self.open.pop()
#将该节点放入closed表中
self.closedPush(node=node)
if self.isTarget(node=node)==True: #如果是解节点
self.getTarget(node=node) #求取解路径
return True
else: #否则向下扩展
self.extendNode(node=node)
return False
def start(self,S0):
'''
求解的起始函数,调用函数可以进行
:param S0: 初始状态列表
:return: 该函数无返回值,会控制台打印结果。
'''
self.S0=S0
if hf.inverseNum(S0)%2!=1:
print("无解")
else:
start = time.perf_counter()
bool=self.solve()
end = time.perf_counter()
if bool==True:
print("总扩展节点",self.Sindex," 解路径长度",len(self.targetPath)," 用时",(end - start))
print("路径如下")
for i in self.targetPath:
print(i.list)
else:
print("error")
'''
Created on 2020年10月24日
启发式搜索中的启发函数的定义,还有对逆序数的求解,对最短移动距离的求解。
其中启发函数1,2,3,4为本实验的扩展内容
启发函数5为根据启发函数2改变的
@author: xxxxxxxxx
'''
Sg=[1,2,3,8,0,4,7,6,5] #目标状态
def inverseNum(list):
'''
求取逆序数
:param list:8数码列表
:return:返回逆序数
'''
count = 0
temlist = [i for i in list]
temlist.remove(0)
for i in range(7):
for j in range(i + 1, 8):
if temlist[i] > temlist[j]:
count = count + 1
return count
def minMove(indexA,indexB):
'''
两个点的最短移动距离
:param indexA: 点A
:param indexB: 点B
:return: 移动的最短距离
'''
#将A作为较小
if indexA>indexB:
indexA,indexB=indexB,indexA
differenceValue=indexB-indexA
if(differenceValue>=3): #如果差值大于3,则肯定不在同一行
result=1+minMove(indexA+3,indexB)
elif differenceValue==2: #如果差值为2,则最短距离肯定为2
result=2
elif differenceValue==0: #差值为0,则不用移动
result=0
else: #差值为1,可能移动1次也可能移动3次
result=3 if (indexA+1)%3==0 else 1
return result
def hfun_1(sn):
'''
启发函数_1,)启发函数 h(n)定义为当前节点与目标节点差异的度量:即当前节点与目标节点格局相比,位置不符的数字个数。
:param sn:当前状态列表
:return:启发函数值
'''
hun=0
for i in range(9):
if sn[i] != Sg[i] and sn[i] != 0:
hun = hun + 1
return hun
def hfun_2(sn):
'''
启发函数_2,)启发函数 h(n)定义为当前节点与目标节点距离的度量:当前节点与目标节点格局相比,位置不符的数字移动到目标节点中对应位置的最短距离之和。
:param sn:当前状态列表
:return:启发函数值
'''
count=0
for i in range(9):
if sn[i]!=Sg[i] and sn[i]!=0:
count=count+minMove(i,Sg.index(sn[i]))
return count
def hfun_3(sn):
'''
启发函数_3,启发函数 h(n)定义为每一对逆序数字乘以一个倍数。
:param sn: 当前状态列表
:return: 启发函数值
'''
inum=inverseNum(list=sn)
return abs(inum-7)*3
def hfun_4(sn):
'''
启发函数_4,启发函数 h(n)定义为位置不符数字个数的总和与 3 倍数字逆序数目相加。
:param sn:当前状态列表
:return:启发函数值
'''
num1=inverseNum(list=sn)
num2=hfun_1(sn)
return num2+3*abs(num1-7)
def hfun_5(sn):
'''
启发函数_5,当前状态与所有目标状态位置不符的数字,在列表中的索引值的差值和
:param sn:当前状态列表
:return:启发函数值
'''
count = 0
for i in range(9):
if sn[i] != Sg[i] and sn[i] != 0:
count=count+abs(i-Sg.index(sn[i]))
return count
本文作者:发呆鱼
本文链接:https://www.cnblogs.com/dyiblog/articles/15929771.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步