N数码问题的启发式搜索算法--A*算法python实现

  • 一、启发式搜索:A算法

1)评价函数的一般形式 : f(n) = g(n) + h(n)

g(n):从S0到Sn的实际代价(搜索的横向因子)

h(n):从N到目标节点的估计代价,称为启发函数(搜索的纵向因子);

特点: 效率高, 无回溯,

搜索算法

OPEN表 : 存放待扩展的节点.

CLOSED表 : 存放已被扩展过的节点.

2)评价函数  f(x) = g(x) + h(x)

当f(x) = g(x)   时,为宽度优先搜索

当f(x) = 1/g(x)时,为深度优先搜索

当f(x) = h(x)   时,为全局优先搜索

比较f(x)大小,决定节点搜索顺序,即在OPEN表中的顺序

3)Step1:   把初始节点S0放入OPEN表中;

Step2:   若OPEN表为空,则搜索失败,退出.

Step3:   移出OPEN中第一个节点N放入CLOSED表中, 并标以顺序号n;

Step4:   若目标节点Sg=N, 则搜索成功,结束.

Step5:   若N不可扩展, 则转Step2;

Step6:   扩展N, 生成一组子节点, 对这组子节点作如下处理后, 放入  OPEN表, 按f值重新排序OPEN表, 转 Step2;

删除重复节点和修改返回指针处理.

  • 二、启发式搜索:A*算法

1)评价函数的一般形式:

f(n) = g(n) + h(n)  且  h(n) <= h*(n)

g(n),h(n):定义同A算法;

h*(n):从N到目标节点的最短路径; 称此时的A算法为A*算法.

2)程序关键点

节点的扩展:close表存放已经扩展后的状态,open表存放未扩展的状态。首先获取节点能扩展的方向,扩展后将父节点放入close表中,如果转移之后的节点,既不在close表也不再open表,表明该节点还未被扩展,则插入open表,如果在close表中表明之前已经扩展过该状态,为了避免无限扩展应将该状态从open表舍弃,如果在open表则比较这两个矩阵的f值(选取最优解),留小的在open表,之后对open表中的节点根据f值进行排序,pop出f值最小的节点进行扩展,依次进行该过程,直至该节点为目标状态。
解的路径的输出:通过目标状态节点向上回溯找其父节点,直至开始状态。

  • 三、python代码实现
  1 # -*- coding: utf-8 -*-
  2 """
  3 Created on Sun Sep 16 14:31:40 2018
  4 A*算法解决N数码问题
  5 运行程序后如下是输入格式:
  6         请输入矩阵的行数
  7
  8         3                            输入对应的N
  9         请输入初始矩阵A
 10
 11         1 0 2                        一行行输入,每行数字空格隔开,每行最后一个数字输入完成后直接回车开始输入第二行
 12
 13         4 5 6
 14
 15         3 7 8
 16         请输入目标矩阵B
 17
 18         1 2 3
 19
 20         8 0 4
 21
 22         7 6 5
 23
 24 """
 25 import numpy as np
 26 import copy
 27 import time
 28 from operator import itemgetter
 29
 30 goal = {}
 31
 32 def get_location(vec, num):    #根据num元素获取num在矩阵中的位置
 33     row_num = vec.shape[0]     #numpy-shape函数获得矩阵的维数
 34     line_num = vec.shape[1]
 35
 36     for i in range(row_num):
 37         for j in range(line_num):
 38             if num == vec[i][j]:
 39                 return i, j
 40
 41 def get_actions(vec):    #获取当前位置可以移动的下一个位置,返回移动列表
 42     row_num = vec.shape[0]
 43     line_num = vec.shape[1]
 44
 45     (x, y) = get_location(vec, 0)    #获取0元素的位置
 46     action = [(0, 1), (0, -1), (1, 0), (-1, 0)]
 47
 48     if x == 0:    #如果0在边缘则依据位置情况,减少0的可移动位置
 49         action.remove((-1, 0))
 50     if y == 0:
 51         action.remove((0, -1))
 52     if x == row_num - 1:
 53         action.remove((1, 0))
 54     if y == line_num - 1:
 55         action.remove((0, 1))
 56
 57     return list(action)
 58
 59 def result(vec, action):    #移动元素,进行矩阵转化
 60      (x, y) = get_location(vec, 0)    #获取0元素的位置
 61      (a, b) = action    #获取可移动位置
 62
 63      n = vec[x+a][y+b]    #位置移动,交换元素
 64      s = copy.deepcopy(vec)
 65      s[x+a][y+b] = 0
 66      s[x][y] = n
 67
 68      return s
 69
 70 def get_ManhattanDis(vec1, vec2):    #计算两个矩阵的曼哈顿距离,vec1为目标矩阵,vec2为当前矩阵
 71     row_num = vec1.shape[0]
 72     line_num = vec1.shape[1]
 73     dis  = 0
 74
 75     for i in range(row_num):
 76         for j in range(line_num):
 77             if vec1[i][j] != vec2[i][j] and vec2[i][j] != 0:
 78                 k, m = get_location(vec1, vec2[i][j])
 79                 d = abs(i - k) + abs(j - m)
 80                 dis += d
 81
 82     return dis
 83
 84 def expand(p, actions, step):                          #actions为当前矩阵的可扩展状态列表,p为当前矩阵,step为已走的步数
 85     children = []                                      #children用来保存当前状态的扩展节点
 86     for action in actions:
 87         child = {}
 88         child[‘parent‘] = p
 89         child[‘vec‘] = (result(p[‘vec‘], action))
 90         child[‘dis‘] = get_ManhattanDis(goal[‘vec‘], child[‘vec‘])
 91         child[‘step‘] = step + 1                       #每扩展一次当前已走距离加1
 92         child[‘dis‘] = child[‘dis‘] + child[‘step‘]    #更新该节点的f值  f=g+h(step+child[dis])
 93         child[‘action‘] = get_actions(child[‘vec‘])
 94         children.append(child)
 95
 96     return children
 97
 98 def node_sort(nodelist):    #按照节点中字典的距离字段对列表进行排序,从大到小
 99     return sorted(nodelist, key = itemgetter(‘dis‘), reverse=True)
100
101 def get_input(num):
102     A = []
103     for i in range(num):
104         temp = []
105         p = []
106         s = input()
107         temp = s.split(‘ ‘)
108         for t in temp:
109             t = int(t)
110             p.append(t)
111         A.append(p)
112
113     return A
114
115 def get_parent(node):
116     q = {}
117     q = node[‘parent‘]
118     return q
119
120 def test():
121     openlist = []    #open表
122     close = []       #存储扩展的父节点
123
124     print(‘请输入矩阵的行数‘)
125     num = int(input())
126
127     print("请输入初始矩阵A")
128     A = get_input(num)
129
130     print("请输入目标矩阵B")
131     B = get_input(num)
132
133     print("请输入结果文件名")
134     resultfile = input()
135
136     goal[‘vec‘] = np.array(B)   #建立矩阵
137
138     p = {}
139     p[‘vec‘] = np.array(A)
140     p[‘dis‘] = get_ManhattanDis(goal[‘vec‘], p[‘vec‘])
141     p[‘step‘] = 0
142     p[‘action‘] = get_actions(p[‘vec‘])
143     p[‘parent‘] = {}
144
145     if (p[‘vec‘] == goal[‘vec‘]).all():
146         return
147
148     openlist.append(p)
149
150     start_CPU = time.clock()    #开始扩展时CPU开始计算
151
152     while openlist:
153
154         children = []
155
156         node = openlist.pop()    #node为字典类型,pop出open表的最后一个元素
157         close.append(node)  #将该元素放入close表
158
159         if (node[‘vec‘] == goal[‘vec‘]).all():    #比较当前矩阵和目标矩阵是否相同
160             end_CPU = time.clock()    #CPU结束计算
161
162             h = open(resultfile,‘w‘,encoding=‘utf-8‘,)  #将结果写入文件  并在控制台输出
163             h.write(‘搜索树规模:‘ + str(len(openlist)+len(close)) + ‘\n‘)
164             h.write(‘close:‘ + str(len(close)) + ‘\n‘)
165             h.write(‘openlist:‘ + str(len(openlist)) + ‘\n‘)
166             h.write(‘cpu运行时间:‘ + str(end_CPU - start_CPU) + ‘\n‘)
167             h.write(‘路径长:‘ + str(node[‘dis‘]) + ‘\n‘)
168
169             h.write(‘解的路径:‘ + ‘\n‘)
170             i = 0
171             way = []
172             while close:
173                 way.append(node[‘vec‘])  #从最终状态开始依次向上回溯将其父节点存入way列表中
174                 node = get_parent(node)
175                 if(node[‘vec‘] == p[‘vec‘]).all():
176                     way.append(node[‘vec‘])
177                     break
178             while way:
179                 i += 1
180                 h.write(str(i) + ‘\n‘)
181                 h.write(str(way.pop()) + ‘\n‘)
182             h.close()
183             f = open(resultfile,‘r‘,encoding=‘utf-8‘,)
184             print(f.read())
185
186             return
187
188         children = expand(node, node[‘action‘], node[‘step‘])    #如果不是目标矩阵,对当前节点进行扩展,取矩阵的可能转移情况
189
190         for child in children:     #如果转移之后的节点,既不在close表也不再open表则插入open表,如果在close表中则舍弃,如果在open表则比较这两个矩阵的f值,留小的在open表
191             f = False
192             flag = False
193             j = 0
194             for i in range(len(openlist)):
195                 if (child[‘vec‘] == openlist[i][‘vec‘]).all():
196                     j = i
197                     flag = True
198                     break
199             for i in range(len(close)):
200                 if(child[‘vec‘] == close[i]).all():
201                     f = True
202                     break
203             if  f == False and flag == False :
204                 openlist.append(child)
205
206             elif flag == True:
207                 if child[‘dis‘] < openlist[j][‘dis‘]:
208                     del openlist[j]
209                     openlist.append(child)
210
211
212         openlist = node_sort(openlist)   #对open表进行从大到小排序
213
214 test()
  • 四、程序运行结果如下图所示

图 1

 

图 2

图 3

  • 五、总结

通过这次编程了解到了搜索具有探索性,要提高搜索效率(尽快地找到目标节点),或要找最佳路径(最佳解)就必须注意搜索策略。对于状态图搜索,已经提出了许多策略,它们大体可分为盲目搜索(bland search)和启发式搜索(heuristic search)两大类。其中盲目搜索是无向导搜索。启发式搜索是有向导搜索,即利用启发信息(函数)引导去寻找问题解。通过A*算法解决N数码问题实验过程中也遇到很多问题,比如节点扩展的方向问题等,通过这次实验不仅锻炼了自己python编程能力,也让自己对N数码求解最优路径问题有了更清晰的认识,希望自己能在老师和同学的帮助下,能不断进步,当然最重要的是自己得付出,只会幻想而不行动的人,永远也体会不到收获果实时的喜悦。加油!!

原文地址:https://www.cnblogs.com/Jm-15/p/9692687.html

时间: 2024-10-31 16:13:51

N数码问题的启发式搜索算法--A*算法python实现的相关文章

【机器学习算法-python实现】采样算法的简单实现

1.背景 采样算法是机器学习中比较常用,也比较容易实现的(出去分层采样).常用的采样算法有以下几种(来自百度知道): 一.单纯随机抽样(simple random sampling) 将调查总体全部观察单位编号,再用抽签法或随机数字表随机抽取部分观察单位组成样本. 优点:操作简单,均数.率及相应的标准误计算简单. 缺点:总体较大时,难以一一编号. 二.系统抽样(systematic sampling) 又称机械抽样.等距抽样,即先将总体的观察单位按某一顺序号分成n个部分,再从第一部分随机抽取第k

【机器学习算法-python实现】矩阵去噪以及归一化

1.背景 项目需要,打算用python实现矩阵的去噪和归一化.用numpy这些数学库没有找到很理想的函数,所以一怒之下自己用标准库写了一个去噪和归一化的算法,效率有点低,不过还能用,大家如果有需要可以拿去. (1)去噪算法:根据概率论的知识,如果一组数据服从正态分布,我们设均值是n,方差是v,那么对于每个离散数值有百分之九十二以上的概率会在(n-3*v,n+3*v)的区间内.所以这里的去噪功能主要是实现如果超出了区间就将这个值标记为区间所能容忍最大值. (2)归一化:找到输入队列最大值max,最

【机器学习算法-python实现】协同过滤(cf)的三种方法实现

(转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景 协同过滤(collaborative filtering)是推荐系统常用的一种方法.cf的主要思想就是找出物品相似度高的归为一类进行推荐.cf又分为icf和ucf.icf指的是item collaborative filtering,是将商品进行分析推荐.同理ucf的u指的是user,他是找出知趣相似的人,进行推荐.通常来讲icf的准确率可能会高一些,通过这次参加天猫大数据比赛,我觉得只有在数据量非

【机器学习算法-python实现】svm支持向量机(2)—简化版SMO算法

(转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景知识 通过上一节我们通过引入拉格朗日乗子得到支持向量机变形公式.详细变法可以参考这位大神的博客--地址 参照拉格朗日公式F(x1,x2,...λ)=f(x1,x2,...)-λg(x1,x2...).我们把上面的式子变型为: 约束条件就变成了: 下面就根据最小优化算法SMO(Sequential Minimal Optimization).找出距离分隔面最近的点,也就是支持向量集.如下图的蓝色点所示.

【机器学习算法-python实现】K-means无监督学习实现分类

1.背景 无监督学习的定义就不多说了,不懂得可以google.因为项目需要,需要进行无监督的分类学习. K-means里面的K指的是将数据分成的份数,基本上用的就是算距离的方法. 大致的思路就是给定一个矩阵,假设K的值是2,也就是分成两个部分,那么我们首先确定两个质心.一开始是找矩阵每一列的最大值max,最小值min,算出range=max-min,然后设质心就是min+range*random.之后在逐渐递归跟进,其实要想明白还是要跟一遍代码,自己每一步都输出一下看看跟自己想象的是否一样. (

FP_Growth算法python实现

FP_Growth算法是关联分析中比较优秀的一种方法,它通过构造FP_Tree,将整个事务数据库映射到树结构上,从而大大减少了频繁扫描数据库的时间. FP_Growth算法主要分成两个步骤,第一步是构造FP-Tree,第二步是从FP-Tree中提取频繁项集. 更多关联分析和FP_Growth的介绍以及伪代码请见:什么是关联分析.FP-Growth算法的介绍. 本文主要目的是介绍python实现FP_Growth算法的代码. FP_Growth项目目录有四个文件: >FP_Growth ? __i

FP-Growth算法python实现之 FP-tree的构造

本文主要介绍FP-tree的构造算法.上接:FP-Growth算法python实现. tree_builder.py代码: #coding=utf-8 import tree_building class Tree_builder(object): """tree_builder类. 作用:根据事务数据集进行数据准备及构造树.""" def __init__(self, routines, min_sup=-1, counts=[], heade

FP-Growth算法python实现之频繁项集的挖掘

本文主要介绍从FP-tree中提取频繁项集的算法. 更多请见:FP-Growth算法的介绍.FP_Growth算法python实现.FP-Growth算法python实现之 FP-tree的构造. tree_miner.py代码: #coding=utf-8 import tree_builder import copy class Tree_miner(object): """tree_miner类. 作用:对Tree进行频繁项集的挖掘"""

【机器学习算法-python实现】PCA 主成分分析、降维

1.背景 PCA(Principal Component Analysis),PAC的作用主要是降低数据集的维度,然后挑选出主要的特征. PCA的主要思想是移动坐标轴,找到方差最大的方向上的特征值,什么叫方差最大的方向的特征值呢.就像下图中的曲线B,一样,它的覆盖范围最广. 基本步骤:(1)首先计算数据集的协方差矩阵 (2)计算协方差矩阵的特征值和特征向量 (3)保留最重要的n个特征 what is 协方差矩阵: 定义是变量向量减去均值向量,然后乘以变量向量减去均值向量的转置再求均值.例如x是变