欢迎访问TK Xiong Blog. Code & Algorithm.

启发式策略的搜索算法

摘要:

在数据结构的学习中,常用的搜索算法有深度优先(DFS)和广度优先(BFS)两种,但是它们都有一定的缺陷,当目标状态空间特别大的时候,因为要穷举到所有的情况,所以执行效率太低,当数据量较大时,建议采用一种特殊的策略进行搜索,这种策略成为启发式策略。

关键词:

启发式策略,启发式搜素算法,A*算法,搜索算法

 

1.     引言

状态空间搜索在介绍启发式策略之前,我们先来看看什么叫做状态空间搜索。

百度百科的定义是这样的: 状态空间搜索就是将问题求解过程表现为从初始状态开始到目标状态的这样一个寻找路径的过程。

即:我们将我们在搜索的过程中所碰到的每一种情况定义为一种状态,从一种状态可以到达另一种状态,从初始状态到我们需求的目标状态有一个转化的过程,将这个过程定义为一条路径,我们寻找这个路径的过程,称之为状态空间搜索。

 

 

2.     启发式策略介绍

  • 启发信息是什么,在智能搜索中,常把搜索中出现的类似于问题的状态条件性质发展动态,解的过程特性结构特性,问题求解的技巧性规则等,称之为搜索的启发信息。
  • 启发式策略是什么,启发式策略是指人们在推理任务中,往往采用的一些推理规则;这些规则不一定遵循标准逻辑规范,但在生活情景中也能帮助人们作出快速的、基本有效的推断。

算法中的启发式策略则是:利用与问题解有关的启发信息来作引导的搜索策略。

例如:方向的限定,优先向目标状态的方向进行搜索而不是往与其相反的方向进行搜索。状态的限定,优先选择开销较小,获利较大的等等…

 

  • 估价函数 F(x) = G(x) + H(x)

F(x) : 从Ss经过结点x到达目标节点Se 的最优路径的代价估计值,作用是估价Open表中各节点的重要性,决定Open表的次序。

G(x) : 从Ss到节点x已经实际付出的代价。指出了搜索的横向趋势、完备性好,但效率差。

H(x) : 从节点x到达目标节点Se的最优路径的估计代价,称为启发函数。

在人工智能问题的求解中,我们往往就是需要去找到一个好的启发函数,使我们的代码显得更加地智能化。

 

估价值 H(n) <= n到目标节点的距离的实际值 时,搜索的点数较多,搜索范围较大,效率较低。但是这样可以得到最优解。

如果 H(n) = n到目标节点的距离的实际值 时,搜索将恰好沿着最短路进行,这样搜索的效率是最高的。

估价值 H(n) > n到目标节点的距离的实际值 时,搜素的点数较少,搜索的范围较小,效率较高,但是不能保证得到最优解。

 

 

2.     启发式搜索的应用实例

3.1八数码

题目链接:

杭电:http://acm.hdu.edu.cn/showproblem.php?pid=1043

北大:http://poj.org/problem?id=1077

在Goodness大牛的博客里写出了八数码的这个问题的八种解决思路。

我这里考虑学习A*+哈希+简单估价函数A*+哈希+曼哈顿距离 这两种方法来解决这个问题。

 

A*+哈希+简单估价函数

A*:用搜索的深度作为G(n)(实际已经付出的代价),启发函数H(n)可以用两种状态 它们不相同数字的数目,同时需要注意以下两点:

1.H(n)>H’(n) , H’(n)为从当前节点到目标状态结点的实际的最优代价值。

2.每次扩展的结点的f值 大于等于 父结点的f值。

哈希:这里的哈希需要使用到排列的方法,叫做康托展开。可以在本人的Blog里找到这个问题的思想以及方法:http://blog.tk-xiong.com/archives/62

我们可以把八数码的九个数分别理解为 1 2 3 4 5 6 7 8 9,其中9代表着空。

那么1-9这样的状态可以写成  1 2 3 9 4 6 7 5 8 …使用康托展开计算的哈希值为:

0*0! + 0*1! + 0*2! + 5*3! + 0*4! + 1*5! + 1*6! + 0*7! + 0*8! < 9!

我们知道,计算出来的哈希值,是在 0(012345678) 到 9!-1= 362879(876543210)这两个数

据之间的。

截图:hash

 

简单估价函数:即用 两种状态 它们不相同数字的数目,比如:在问题描述里面给出的(123946758),它和我们需要得到的最终状态(123456789)的不相同的数字有四组,分别是第四个,第五个,第八个和第九个.这样它的估价函数的值就应该是 4.

截图:h

逆序对判断:

count-re

代码如下:

 

运行截图:

hdu1043-1

pku1077-1

分别是HDU(上面的) 和 PKU(下面的)运行结果的截图。

可以看到,HDU使用了多组测试数据,所以计算时间较长,在G++运行模式下,3800MS(3.8秒的时间通过),在C++运行模式下超过了规定时间。

在PKU中,应该是采用了单组数据,G++运行模式下,大概110MS(0.11秒)可以通过,在C++运行模式下,266MS(0.26秒)可以通过。

可以知道,这样的代码还是需要改进的。

 

对于A*算法,如果要优化,最好的就是启发函数的设计。

A*+哈希+曼哈顿距离

A* 和 Hash 在前面已经介绍就不再讲解。

曼哈顿距离:两个点在标准坐标系上的绝对轴距总和。

在问题描述里面给出的1-9(123946758),它和我们需要得到的最终状态(123456789)的不相同的数字有四组,分别是 4 5 8这四个数字。它们与它们本应该在的位置的曼哈顿距离分别是1 1 1,所以它计算出的H(A) = 3;

所以可以修改H()函数,使用曼哈顿距离作为我们的估价函数.

H-manhattan

 

运行截图:

hdu1043-2PKU1077-2

可以看到,上面的截图HDU的运行时间,相比之前的简单估价函数快了7倍.

而PKU的可以看到,单次运行时间,最快能达到16MS,这已经是非常快的速度了.

可以得到这样的结论:A*算法的运行速度与估价函数H()有关,估价函数越好,运行速度就越快.

 

3.2 四边形简单地图寻路算法

首先为什么叫四边形简单地图呢,因为这个实例选择了简单的二位数组当做地图,且通过各个点是没有地形附加因素的,即可以理解成简单的单位代价,在这样的情况下使用A*算法寻找最短路径.

single

我们根据上图对路径做出一定的规范:

  1. 采用了八连通的方法,但是不能穿过墙角.
  2. 相邻点代价取为10,斜线代价为
  3. 所以对于H函数的值最后也要*10

对于1的解释:比如在起点下方的斜线位置,必须先向下再向右,不能穿过墙角.

对于2的解释:10*sqrt(2)大致就是14(14.14…)

近似处理…

 

H()估价函数采用前面已学习的曼哈顿算法.

地图使用随机数生成.

 

代码如下:

 

但是,在某些情况下,结果会不太准确,比如:

single-ans

这种情况很明显的可以看出,在某些情况下,它宁愿选择开销更大的路径而不是开销更小的,只因为H()函数计算出的曼哈顿距离*10后的差值相比其他位置的4的差值更大一些.故可以适当调低倍数(HB),再进行测试。

 

3.3复杂地图寻路算法

这次的复杂地图是根据前面的简单地图做出的改进,

  1. 增加了地图的复杂性,通过不同的地形和长度所耗费的代价不一样
  2. 模拟真实的游戏地形,草地,澡泽,平原等…

需要解决的问题:

  1. H()函数相对以前需要更加的精细化.
  2. 因为地图的复杂性,所以H()函数需要更加的精细化.
  3. 如何模拟真实的游戏地形

 

代码如下:

 

注:以上代码还有部分需要修改的地方,不过实在是没时间了…

预计后期修改为:

  1. 修改地图的节点只允许一次访问,多次计算保证是最小的开销。
  2. 实在应该找一个漂亮点的界面
  3. 修改地图为六边形而不是四边形…
  4. 优化H()函数,得到最优值
  5. 使用小顶堆维护Open表而不是优先队列(这样会更快)
  6. 欢迎访问http://blog.tk-xiong.com/archives/232

 

 

参考文献

[1]  状态空间搜索——百度百科

[2]  启发式搜索算法——百度百科

[3]  A*算法——百度百科

[4]  三种重要的启发式策略——新浪博客

[5]  人工智能——状态空间的搜索策略

[6]  人工智能——状态空间搜索及状态空间表示法

[7]  八数码的八境界

[8]  康托展开和逆康展开——TK-Xiong’s Blog

[9]  曼哈顿距离——百度百科

来自TK Xiong

【Search】启发式策略的搜索算法
Tagged on:     
0 0 投票数
Article Rating
订阅评论
提醒

1 评论
最新
最旧 最多投票
内联反馈
查看所有评论

真牛X….,简直膜拜