0x27
A*
在探讨A*算法之前,先回顾一下优先队列BFS
算法。该算法维护了一个优先队列(二叉堆),不断从队中取出“当前价值最小”的状态(堆顶)进行扩展。每个状态第一次从堆中被取出时,就得到了从初态到该状态的最小代价。
如果给定一个“目标状态”,需要求出从初态到目标状态的最小代价,那么优先队列BFS
的这个“优先策略”显然是不完善的。一个状态的当前代价最小,只能说明从起始状态到该状态的代价很小,而在未来的搜索中,从该状态到目标状态可能花费很大的代价。另外一些状态虽然当前代价略大,但是未来的搜索中,从该状态到目标状态的代价可能会小,于是从起始状态到目标状态的总代价反而更优。优先队列BFS
会先选择前者的分支,导致求出最有解的搜索量增大。比如在上一节优先队列BFS
的示意图中,产生最优解的搜索路径(5+2+1)的后半部分就很晚才得以扩展。
为了提高搜索效率,我们很自然地想到,可以对未来可能产生的代价进行预估。详细地讲,我们设计一个“估价函数”,以任意“状态”为输入,计算出从该状态到目标状态所需代价的估计值。在搜索中,仍然维护一个堆,不断从堆中取出“当前价值+未来估计”最小的状态进行拓展。
为了保证第一次从堆中取出目标状态时得到的就是最优解,我们设计的估价函数需要满足一个基本准则:
设当前状态 s t a t e state state到目标状态所需代价的估计值为 f ( s t a t e ) f(state) f(state)。
设在未来的搜索中,实际求出的从当前状态 s t a t e state state到目标状态的最小代价为 g ( s t a t e ) g(state) g(state)。
对于任意的 s t a t e state state,应该有 f ( s t a t e ) ≤ g ( s t a t e ) f(state)\leq g(state) f(state)≤g(state)。
与就是说,估价函数的估值不能大于未来实际代价,估价比实际价值更优。
为什么要遵守这个准则呢?我们不妨看看,如果某些估值大于未来实际代价,那么将会发生什么情况。
假设我们的估价函数 f f f在每个状态上的值如下页面所示:
我们看到本来在最优解搜索路径上的状态被错误地估计了较大的代价,被压在堆中无法取出,从而导致非最优解搜索路径上的状态不断扩展,直至在目标状态上产生错误的答案。
如果我们设计估价函数时满足上述准则,保证估值不大于未来实际代价,那么即使估价不太准确,导致非最优解搜索路径上的状态 s s s先被扩展,但是随着“当前代价”的不断累加,在目标状态被取出之前的某个时刻:
1.根据 s s s并非最优, s s s的“当前代价”就会大于从起始状态到目标状态的最小代价。
2.对于最优解搜索路径状态 t t t,因为 f ( t ) ≤ g ( t ) f(t)\leq g(t) f(t)≤g(t),所以 t t t的“当前代价”加上 f ( t ) f(t) f(t)必定小于等于 s s s的当前代价加上 g ( t ) g(t) g(t),而后者的含义就是从起始状态到目标状态的最小代价。
结合以上两点,可知“ t t t的当前代价加上 f ( t ) f(t) f(t)”小于 s s s的当前代价。因此, t t t就会被从堆中取出进行扩展,最终更新到目标状态上,产生最优解。
这种带有估价函数的优先队列BFS
算法就称为A*算法。只要保证对于任意状态
s
t
a
t
e
state
state,都有
f
(
s
t
a
t
e
)
≤
g
(
s
t
a
t
e
)
f(state)\leq g(state)
f(state)≤g(state),A*算法就一定能在目标状态第一次从堆中被取出时得到最优解,并且在搜索过程中每个状态只需要被扩展一次(之后再被取出就可以直接忽略)。估价
f
(
s
t
a
t
e
)
f(state)
f(state)越准确、越接近
g
(
s
t
a
t
e
)
g(state)
g(state),A*算法的效率就越高。如果估价始终为0,就等价于普通的优先队列BFS
。