A* Algorithm

作者:曾巧(numenzq)

摘要

A*算法是人工智能中的启发式搜索算法中的典型实践,此文主要介绍了A*算法的思想,并以一个简单的演示程序加以说明,适合于初学者阅读和学习。

 

正文

   A*算法是人工智能运用在游戏中的一个重要实践,它主要是解决路径搜索问题。A*算法通过适当的估价函数(在下文中会提到),寻找到两点间的最短路径,它在一定程度上替代了像深度优先搜索(Deep First Search)和广度优先搜索(Breadth First Search)这样的盲目型搜索算法。

    说到两点间的最短路径搜索算法,可能大家都会想到Dijkstra算法,但你也应该知道Dijkstra的时间复杂度是O(n2),当你需要快速的找出两点间的最短路径时,Dijkstra算法就不太适合了,所以就诞生了A*算法,你可以把A*算法当着是Dijkstra算法的优化,因为当A*算法不实现估计函数时,它就是一个Dijkstra算法。A*算法与Dijkstra算法的最大区别就是它通过估价函数指定了要试探的下个节点。

    接下来,我将介绍A*算法的思想和实现。

 

内容

l        Dijkstra算法

l        启发式搜索算法

l        A*算法简介

l        A*算法流程

l        A*算法示例

l        概要

 

Dijkstra算法

    与深度优先搜索算法不同,Dijkstra算法是思想是广度优先搜索,它是通过起点上四周发散的遍历搜索,最后找到目的点的一个过程;就像之前说的那样,这样的搜索方式是盲目的,它并不关心目的点的位置,只是一味的在以起始点为中心的向外扩散搜索,这样会导致大量无效点的搜索,大大的降低搜索的效率。

    我之前说过,你可以把A*算法看着是Dijkstra算法的优化,那A*是如何做的呢?其实它就是引入了启发式搜索算法的思想,那什么是启发式搜索算法呢?让我们一起来了解吧。

 

启发式搜索算法

    所谓启发式搜索,就是利用一个估价函数评估每次的决策的价值,决定先尝试哪一种方案,这样可以极大的优化普通的广度优先搜索。一般来说,从出发点(A)到目的地(B)的最短距离是固定的,我们可以写一个函数judge()估计A B 的最短距离,如果程序已经尝试着从出发点A沿着某条路线移动到了C, 那么我们认为这个方案AB间的估计距离为AC实际已经行走了的距离H加上用judge()估计出的CB的距离。

如此,无论我们的程序搜索展开到哪一步,都会算出一个评估值,每一次决策后,将评估值和等待处理的方案一起排序,然后挑出待处理的各个方案中最有可能是最短路线的一部分的方案展开到下一步,一直循环到对象移动到目的地,或所有方案都尝试过却没有找到一条通向目的地的路径则结束。(通常在游戏里还要设置超时控制的代码,当内存消耗过大或用时过久就退出搜索)

怎么写这个算法中的估价函数非常的重要,如何保证一定能找到最短路径呢?充要条件是,你的估价函数算出的两点间的距离必须小于等于实际距离。这个可以从数学上严格证明,有兴趣可以自己去查阅相关资料。如果你的估价函数不满足这点,就只能叫做 A 算法,并不能保证最后的结果是最优的,但它可能速度非常的快。而游戏中我们也不一定非要得到最优解的,但无疑,满足那个条件的 A* 算法中,估计值越接近真实值的估价函数就做的越好。

 

A*算法简介

启发式搜索其实有很多的算法,比如:局部择优搜索法、最好优先搜索法等等。当然A*算法也在其中。这些算法都使用了启发函数,但在具体的选取最佳搜索节点时的策略不同。像局部择优搜索法,就是在搜索的过程中选取最佳节点后舍弃其他的兄弟节点,父亲节点,而一直得搜索下去。这种搜索的结果很明显,由于舍弃了其他的节点,可能也把最好的节点都舍弃了,因为求解的最佳节点只是在该阶段的最佳并不一定是全局的最佳。最好优先就聪明多了,他在搜索时,便没有舍弃节点(除非该节点是死节点),在每一步的估价中都把当前的节点和以前的节点的估价值比较得到一个最佳的节点。这样可以有效的防止最佳节点的丢失。

那么A*算法又是一种什么样的算法呢?其实A*算法也是一种最好优先的算法。只不过要加上一些约束条件罢了。由于在一些问题求解时,我们希望能够求解出状态空间搜索的最短路径,也就是用最快的方法求解问题,A*就是干这种事情的!我们先下个定义,如果一个估价函数可以找出最短的路径,我们称之为可采纳性。A*算法是一个可采纳的最好优先算法。A*算法的估价函数可表示为:

f'(n) = g'(n) + h'(n)

这里,f'(n)是估价函数,g'(n)是起点到终点的最短路径值,h'(n)n到目标的最短路经的启发值。由于这个f'(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。g(n)代替g'(n),但g(n)>=g'(n)才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h'(n),但h(n)<=h'(n)才可(这一点特别的重要)。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的最好优先算法就是A*算法。哈!你懂了吗?肯定没懂!接着看!

举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点所在的层数,h(n)=0,这种h(n)肯定小于h'(n),所以由前述可知广度优先算法是一种可采纳的。实际也是。当然它是一种最臭的A*算法。

再说一个问题,就是有关h(n)启发函数的信息性。h(n)的信息性通俗点说其实就是在估计一个节点的值时的约束条件,如果信息越多或约束条件越多则排除的节点就越多,估价函数越好或说这个算法越好。这就是为什么广度优先算法的那么臭的原因了,谁叫它的h(n)=0,一点启发信息都没有。但在游戏开发中由于实时性的问题,h(n)的信息越多,它的计算量就越大,耗费的时间就越多。就应该适当的减小h(n)的信息,即减小约束条件。但算法的准确性就差了,这里就有一个平衡的问题。可难了,这就看你的了!

 

A*算法流程

A*是启发试搜索加动态规划。具体实现依靠两个队列Open队列和Close队列。从一点访问四周相邻的格子如果可以移动且当前移动为起点到哪个格子的历史最佳方法则把那个格子按照估价值从小到大插入Open队列里面,几个方向试探结素后取出估价值最小的节点放入Close再从这里开始试探几个相邻的方向同样放入Open队列里面,放入Open的条件是1.这步在地图上面是可以移动的,2.这步所在节点在Open里面并不存在,3.从起点到这步的实际距离比这点的历史最小距离还短满足这三个条件就把节点放入Open队列。具体的算法网友们已经描述的再清楚不过了大致算法如下:

WHILE TRUE BEGIN

1.      S点加入OPEN队列(按该点到E点的距离排序+走过的步数从小到大排序)

2.      排序队列OPEN队列中距离最小的第一个点出列,并保存入CLOSE队列中

3.      从出列的点出发,分别向4个(或8个)方向中的一个各走出一步

4.      估算第3步所走到位置到目标点的距离,并把该位置按估价距离从小到大排序后并放入OPEN

5.      如果该点从四个方向上都不能移动,则把该点从CLOSE队列中删除

6.      从目标点回溯树,直到树根则可以找到最佳路径,并保存在PATH

END

 

A*算法示例

    现在,你应该比较清楚A*算法的思想和流程了,何不写程序试一下呢,接下来我们就来看一下用A*算法寻找两点间的最短路径,这个例子的估价函数比较简单,直接用两点间的最短路径就行了,其他的就没得什么难度了,下面的截图是我以前写的A*算法实现的效果图。

a star example

概要

A*算法在人工智能中是一种典型的启发式搜索算法,此文初步介绍了A*算法。对于初学者应该比较容易懂。

 

更多信息

·Amit’s A* Pages

   http://theory.stanford.edu/~amitp/GameProgramming/

·Two-Tiered A* Pathfinding

http://www.policyalmanac.org/games/twoTiered.htm

·Shortest Path Algorithms: Some References

http://i11www.iti.uni-karlsruhe.de/~fschulz/shortest-paths/

·A* search algorithm

http://en.wikipedia.org/wiki/A-star_search_algorithm

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值