最短路径——启发式搜索算法A*
现实中有一些问题,它们不存在已知的求解算法或求解方法非常复杂,而人使用自身的智能却能较好地求解,经过分析,有的问题可以归结为试探性的搜索方法,为了模拟这些试探性问题的求解过程而发展的一种技术就称为搜索,比如,走迷宫问题、八皇后问题、传教士与野人过河问题等
要利用搜索来求解问题,首先需要把问题表述为一组“状态State+操作算子Operator”。状态State形如(s0,s1,s2…sn),由一组独立分量组成,分量的值不同,代表不同的状态;操作算子,作用在状态State上,使得从一种状态转换到另一种状态。所有的状态即构成了状态空间,问题的求解转化为状态空间的搜索,即“搜索一条从初始状态到目标状态且满足一定条件的路径”。
状 态空间搜索,有盲目状态空间搜索和启发式状态空间搜索。盲目状态空间搜索,最常见的是广度优先搜索、深度优先搜索,由于其蛮力方式,当状态空间节点很多乃 至近似无穷时,这种方式搜索的代价很高,甚至不可行,只适合于小规模的搜索空间,其改进形式有回溯法、分支限界法,通过限制条件来剪掉根本不可行的分支, 减少搜索的节点,但在最坏情况下并不能保证最优的效率;启发式状态空间搜索,就是利用启发式信息,有意识地指导搜索的方向,避免大量不必要的搜索,使得搜 索的路径迅速收敛到目标,启发信息通常采用启发函数的形式来表示,问题的关键是启发函数的设计。
启发式搜索算法A*,用在最短(佳)路径搜索上,效率优于经典的Dijkstra算法。下面简要介绍一下A*算法的基本思想。
定义:启发函数f(n) = g(n) + h(n),n代表状态空间中当前某一状态节点,g(n)代表从初始状态节点Ss到当前节点n的搜索费用的估计,因为n为当前节点,搜索已到达n节点,所以g(n)可计算实际费用值,h(n)表示从当前节点n到目标节点Sg搜索费用的估计,因为尚未找到解路径,所以h(n)仅仅是估机值。
若进一步规定h(n)≥0,并且定义:
ƒ*(n) = ɡ*(n) + h*(n)
其中,ƒ*(n)表示SS经节点n到Sg最优路径的实际最小搜索费用,ɡ*(n)为SS到n的实际最小费用,h*(n)为n到Sg的实际最小费用的估计。
特别要求,当h(n)≤h*(n)时,就称为A*算法。 且满足单调性限制,即h(ni)<=h(nj)+C(ni,nj),类似于三角不等式,其中nj是ni的后继节点。如果有解,A*算法一定能找到最优解,满足单调性限制,有较高的效率。具体证明就不给出了。
最短路径,拿地图上的实际交通道路举例。把道路网络抽象成一个带拐向限制和权重的有向图, 拐向限制是用于判断从一条道路弧段往相邻的道路弧段可否拐,有向图是表示道路是单行的还是双行的,权重分弧段权重和拐向权重,即弧段权重是节点到节点的费 用,拐向权重是弧段转弧段的权重,需要特别注意的是,两个权重的量纲不同带来权重衡量的标准不统一,需要设计一个客观衡量权重的函数,以统一两者的量纲, 比如弧段权重以节点到节点的距离D(单位是米),拐向权重以平均拐向时间T(单位是秒),在实际搜索路径的过程中,计算费用的时候,需要把距离和时间统一起来,就需要设计一个函数来统一量纲,给出一个相对客观的费用评价。
道路网图的数据结构,首先确定描述道路网络的数据结构,通常是用邻接表结构,来表达道路节点到节点之间的关系,同时用另一个邻接表,来表达弧段转弧段的关系,权重可以统一采用时间T,限制转向的,设置权重为∞,启发函数:
f(n) = g(n) + Distance(n, Sg)/avg(v);
满足A*算法的条件。
A*算法有两个重要的数据结构:一个是OpenTable,用于存储已扩展、未考查的节点,程序中需要从此结构中取出权重最小的节点进行扩展,所以,此结构可以用“最小堆”来实现;另一个是CloseTable,用于存储已扩展考查过的节点,节点信息本身需要记录其父节点,便于由目标节点返向追踪回初始节点,所以,以节点ID编号作为KEY,CloseTable采用Hash结构实现。
通用的A*算法如下:
- 待扩展的节点集合open表,初始化为初始节点SS,已扩展的节点集合close为空 {},节点 SS 的代价为 g(SS) = 0。
- 每次从 open 表中取出一个节点 n,根据规则扩展产生一组后继节点 mi,然后把 n 放入 close 表中。节点 mi 可能属于下列三种情况之一:
- 新的节点,则把 mi 的源标记为 n,代价 f(mi) = g(mi) +h(mi),并放入open表中。
- 已在open表中存在的节点,并且代价 f(mi) < f(x) ,说明从 s 到 mi 并且经由 n 的路径要比先前搜索得到的路径要短。因此,用mi替换掉x。
- 已在close表中存在的节点,并且代价 f(mi)<f(x)。同理,把x结点从close 表中取出,并将mi结点放入到open。
- 不断重复上一步操作,直到满足下列条件之一:
- n == Sg,搜索成功。
- open表为空,搜索失败。