A*算法证明与详解

背景

AA^*算法出现之前,人们都在用DFS和BFS进行搜索,然而,这两种算法在展开子节点时都属于盲目型搜索,也就是说,它不会选择在下一次搜索中更优的那个节点,继而借此跳转到该节点进行下一步的搜索,而是盲目全局搜索。如果运气不好,在此情形中,均需要试探完整个解集空间,显然,DFS和BFS只适用于问题规模不大的搜索问题中。 然而,1968年的一篇论文“P. E. Hart, N. J. Nilsson, and B. Raphael. A formal basis for the heuristic determination of minimum cost paths in graphs. IEEE Trans. Syst. Sci. and Cybernetics, SSC-4(2):100-107, 1968”,打破了这个僵局,从此,一种精巧、高效的算法------A*算法横空出世了,而且还在好多领域取得了不错的应用。

算法介绍

AA^*算法属于启发式搜索算法,在寻路方面,它可以是在图形平面上,有多条路径,求出最低成本的算法,是非常流行的该类搜索算法中的一个,它一般被用于路径优化领域,例如导航、游戏里面的人物移动等。它的特别之处是在检查最短路径中时,检查每个可能符合需要的节点时都引入了全局的信息,然后对当前节点距终点的距离做出估计,并作为评价该节点处于最短路线上的可能性的量度。

算法搜索过程

核心

两个集合:Open,Closed和一个公式:f(n)=g(n)+h(n)f(n)=g(n)+h(n)。其中Open,Closed都是存储节点的集合,Open存储可到达的节点,Closed存储已经到达的节点;而公式f(n)=g(n)+h(n)f(n)=g(n)+h(n)则是对节点价值的评估,g(n)g(n)代表从起点走到当前节点的成本,也就是走了多少步,h(n)h(n)代表从当前节点走到目标节点的距离,即不考虑障碍的情况下,离目标还有多远。至于f(n)f(n),则是对g(n)g(n)h(n)h(n)的综合评估,我们应该尽量选择步数更少,离目标更近的节点,那么f(n)f(n)的值越小越好。

搜索思路

开始时,Closed表为空,Open表仅包括起始节点,每次迭代中,AA^*算法将Open表中具有最小代价之的节点去除进行检查,检查后的节点放入Closed表,如果这个节点不是目标节点,那么考虑该节点的所有相邻节点。对于每个相邻节点按下列规则处理;

  1. 如果相邻节点既不在Open表中,又不在Closed表中,则将它加入Open表中;
  2. 如果相邻节点已经在Open表中,并且新的路径具有更低的代价值,则更新它的信息;
  3. 如果相邻节点已经在Closed表中,那么需要检查新的路径是否具有更低的代价值,如果是,那么将它从Closed表中移出,加入到Open表中,否则忽略。(这里需要检查Closed表是因为要防止由于h(n)计算不准确而导致的误差)

重复上述步骤,直到到达目标节点。如果在到达目标之前,Open表就已经变空,则意味着在起始位置和目标位置之间没有可达的路径。

算法流程图

A*算法证明与详解

算法正确性证明

前置知识

符号说明:

  1. g(n)g(n)表示从点stst到达节点nn的当前最小代价。
  2. g(n)g^*(n)表示从节点stst到节点nn的实际最小代价。
  3. h(n)h(n)表示从节点nn到目标节点endend的预估代价。
  4. h(n)h^*(n)表示从节点nn到目标节点endend的实际最小代价。
  5. f(n)=g(n)+h(n)f(n)=g(n)+h(n) 。这里f(n)f(n)的含义为从初始节点stst出发经过节点nn再到达目标节点endend的最小代价的预估。
  6. f(n)f^*(n)。实际的最短路径长度。
  7. OPEN LISTOPEN\ LIST(开放列表),CLOSE LISTCLOSE\ LIST(封闭列表)。

算法本身性质一: 每次选择的节点是f(n)=g(n)+h(n)f(n)=g(n)+h(n)最小的节点。而且,OPEN LISTOPEN \ LIST上任一具有f(n)<f(st)f(n)<f^*(st)的节点nn,最终都将被AA^*选作扩展节点。(下面定理推论2.1)

算法本身性质二: h(n)h(n)h(n)\le h^*(n),预估代价小于实际最小代价。

开始证明

先证明,如果有解,那么算法一定可以找到:

定理1: 对有限图,如果从初始节点stst到目标节点endend有路径存在,则A*算法一定成功结束。

  1. 首先证明算法必定结束。由于搜索图为有限图,如果算法能找到解,则会成功结束;如果算法找不到解,那么必然会因为OPEN LISTOPEN \ LIST 为空而结束。因此A算法必然会结束。
  2. 然后证明算法一定会成功结束。由于至少存在一条由初始点到目标点的路径,设此路径为Pst=st(v0)v1v2end(vm)P_{st}=st(v_0)\to v_1 \to v_2 \cdots \to end(v_m) 那么算法开始时,节点v0v_0OPEN LISTOPEN \ LIST 中,而且路径中任一节点vkv_k 离开OPEN LISTOPEN \ LIST 后,其后继节点vk+1v_{k+1}一定会在这之前进入OPEN LISTOPEN \ LIST 。这样在OPEN LISTOPEN \ LIST 变空之前,目标节点必然会出现在OPEN LISTOPEN \ LIST 中。因此,算法必定会结束。

引理1: 对无限图,若有从初始节点stst到目标节点endend的路径,则AA^*不结束时,在OPEN LISTOPEN \ LIST 中即使最小的一个f(vn)f(v_n)值也将增到任意大,或有f(vn)>f(st)f(v_n)>f^*(st)

  1. d(vn)d^*(v_n)AA^*生成的从初始节点v0v_0到节点vnv_n的最短路径长度(这里的长度指的是要经过多条边),由于搜索图中每条边的代价都是一个正数,令这些正数中最小一个数是ee,则有g(vn)d(vn)×eg^*(v_n)\ge d^*(v_n)\times e。(g(vn)g^*(v_n)是源点到vnv_n的最小代价,d(vn)d^*(v_n)是源点到vnv_n的最小长度,长度乘以所有边中的最小代价,得到的肯定比从源点到nn的最小代价要小)
  2. 又因为是最佳路径的代价,所以g(vn)g(vn)d(vn)×eg(v_n)\ge g^*(v_n)\ge d^*(v_n)\times e
  3. 又因为h(vn)0h(v_n)\ge 0,所以f(vn)=g(vn)+h(vn)g(vn)d(vn)×ef(v_n)=g(v_n)+h(v_n)\ge g^*(v_n)\ge d^*(v_n)\times e
  4. 所以如果AA^*算法不终止,从OPEN LISTOPEN \ LIST 中选出的节点必将拥有任意大的d(vn)d^*(v_n)值,因此,也将具有任意大的f值。

引理2: AA^*结束前的任意时刻,OPEN LISTOPEN \ LIST 中总是存在结点vkv_k ,它是从初始结点stst到目标节点endend 的一个节点,且满足f(vk)<f(vk)f(v_k)<f^*(v_k)

设初始点stst到目标endend 的最佳路径序列Pst=st(v0)v1v2end(vm)P_{st}=st(v_0)\to v_1 \to v_2 \cdots \to end(v_m)

  1. 算法开始的时候,节点ststOPEN LISTOPEN \ LIST中,当节点stst离开OPEN LISTOPEN \ LIST 进入CLOSE LISTCLOSE\ LIST中时,节点v1v_1进入OPEN LISTOPEN \ LIST 。因此,AA^*没有结束之前,在OPEN LISTOPEN \ LIST 中,必然是最佳路径上的节点。设这些节点排在最前面的节点为vkv_k,则有:f(vk)=g(vk)+h(vk)f(v_k)=g(v_k)+h(v_k)
  2. 由于vkv_k在最佳路径上,故有g(vk)=g(vk)g(v_k)=g^*(v_k),从而f(vk)=g(vk)+h(vk)f(v_k)=g^*(v_k)+h(v_k)
  3. 又由于AA^*算法性质二h(vk)h(vk)h(v_k)\le h^*(v_k),故有,f(vk)g(vk)+h(vk)=f(vk)f(v_k)\le g^*(v_k)+h^*(v_k)=f^*(v_k)
  4. 因为在最佳路径上的所有节点的ff^*值都相同,因此有f(vk)f(vk)f(v_k)\le f^*(v_k)

定理2: 对无限图,若从初始节点stst到目标节点endend有路径存在,则AA^*一定成功结束。
反证法:

  1. 使用引理1:假设AA^*不结束,在OPEN LISTOPEN \ LIST中即使最小的一个f(n)f(n)值也将增到任意大,或有f(n)>f(s)f(n)>f^*(s)
  2. 根据引理2:在AA^*结束前,必存在节点nn,使得f(n)<=f(s)f(n)<=f^*(s),所以,如果AA^*不结束,将导致矛盾,AA^*算法只能成功结束。

推论2.1:OPEN LISTOPEN \ LIST上任一具有f(n)<f(st)f(n)<f^*(st)的节点nn,最终都将被AA^*选作扩展节点。

  1. 由定理2,知AA^*一定结束,由AA^*的结束条件,OPEN LISTOPEN \ LISTf(end)f(end)最小时才结束。
  2. f(end)f(end)=f(st)f(end)\ge f^*(end)=f^*(st),所以f(n)<f(st)f(n)<f^*(st) ,均被扩展,得证。

根据定理一和定理二可知,不论是在无向图还是在有向图中,如果有解一定可以被AA^*算法找到

再证明,算法找的的解一定是最优解:

引理一: 已知从初始节点stst到目标节点endend的一条最短路径PstP_{st},路径上任意一点nng(n)=Pst(stn)g^*(n)= |P_{st}(st\to n)|

已知一条ststnn的路径长度为Pst(stn)|P_{st}(st\to n)|,根据g(n)g^*(n)的最小性g(n)Pst(stn)g^*(n)\le |P_{st}(st\to n)|

利用反证法证明如下:

假设:g(n)<Pst(stn)g^*(n)< |P_{st}(st\to n)|

那么,意味着存在一条路径PP',该路径从ststnn的长度P(stn)<Pst(stn)|P'(st\to n)|<|P_{st}(st\to n)|

P(stn)+Pst(nend)<Pst(stend)|P'(st\to n)| + |P_{st}(n\to end)| < |P_{st}(st\to end)|。这与PstP_{st}为从ststendend的最短路径矛盾。

所以原假设g(n)<Pst(stn)g^*(n) < |P_{st}(st\to n)|不成立,因此g(n)=Pst(stn)g^*(n)=|P_{st}(st\to n)|得证。

引理二: 已知从初始节点stst到目标节点endend的一条最短路径PstP_{st},路径上任意一点nnh(n)=Pst(nend)h^*(n)= |P_{st}(n\to end)|。(证明思路和引理1相同)

已知一条nnendend的路径长度为Pst(nend)|P_{st}(n\to end)|,根据h(n)h^*(n)的最小性h(n)Pst(nend)h^*(n)\le |P_{st}(n\to end)|

利用反证法证明如下:

假设:h(n)<Pst(nend)h^*(n)< |P_{st}(n\to end)|

那么,意味着存在一条路径PP',该路径从ststnn的长度P(nend)<Pst(nend)|P'(n\to end)|<|P_{st}(n\to end)|

Pst(stn)+P(nend)<Pst(stend)|P_{st}(st\to n)|+|P'(n\to end)| < |P_{st}(st\to end)|。这与PstP_{st}为从ststendend的最短路径矛盾。

所以原假设h(n)<Pst(nend)h^*(n) < |P_{st}(n\to end)|不成立,因此h(n)=Pst(nend)h^*(n)=|P_{st}(n\to end)|得证。

引理三: 对于任意节点nng(n)=g(n)g(n)=g^*(n)f(n)f(n)最小。

证明:已知f(n)=g(n)+h(n)f(n)=g(n)+h(n)。对于h(n)h(n),在寻路前就已经确定,是一个定值。因此,argmin(f(n))=argmin(g(n)+h(n))=argmin(g(n))+h(n)argmin(f(n))=argmin(g(n)+h(n))=argmin(g(n))+h(n) 。也就是说f(n)f(n)的值只有在更新g(n)g(n)时才会变。而g(n)g(n)最小值是g(n)g^*(n),所以对节点n而言,g(n)=g(n)g(n)=g^*(n)f(n)f(n)最小。

我们利用反证法进行证明:

假设:AA^*算法求出的解不是最优,那么我们通过AA^*算法寻到了一条从ststendend的路径PAP_{A^*},而这条路径并不是最短路径。

那么则存在最短路径PstP_{st},有Pst<PA|P_{st}|<|P_{A^*}|。设最短路径Pst=st(v0)v1v2end(vm)P_{st}=st(v_0)\to v_1 \to v_2 \cdots \to end(v_m)

以下利用数学归纳法

归纳奠基:

当到节点vn,n=0v_n,n=0时: 把节点stst放入CLOSE LISTCLOSE\ LIST中,把stst相邻节点放入OPEN LISTOPEN\ LIST中并更新gg值。这一步后v1v_1显然应该在OPEN LISTOPEN\ LIST中,并且因为节点v1v_1为最短路径PstP_{st}上的节点,根据引理一,g(v1)=Pst(stv1)g^*(v_1)=|P_{st}(st\to v_1)|。这里发现更新g(v1)g(v_1)时也是根据节点stst到节点v1v_1的边的长度,即g(v1)g(v_1)更新为Pst(stv1)|P_{st}(st\to v_1)|,这时有g(v1)=g(v1)g(v_1)=g^*(v_1)。根据引理三,这时的f(v1)f(v_1)就是最小值了,以后不会再发生变化。

利用算法本身性质一、引理一、引理二,有f(v1)=g(v1)+h(v1)=g(v1)+h(v1)g(v1)+h(v1)=Pst<PAf(v_1)=g(v_1)+h(v_1)=g^*(v_1)+h(v_1)\le g^*(v_1)+h^*(v_1)=|P_{st}|<|P_{A^*}|。也就是f(v1)<f(end)f(v_1)<f(end)也成立。由AA^*算法的性质一可以知道,v1v_1节点一定可以在结束之前被选中,用来更新数据。

当到节点vn,n=1v_n,n=1时:v2v_2不在OPEN LISTOPEN \ LISTCLOSE LISTCLOSE \ LIST中,直接更新g(v2)g(v_2);若在OPEN LISTOPEN \ LISTCLOSE LISTCLOSE \ LIST中,接下来由g(v2)g(v_2)g(v1)+Pst(v1v2)g(v_1)+|P_{st}(v_1\to v_2)|的大小关系,判断是否会通过通过v1v_1g(v2)g(v_2)进行更新。

  • 若更新,则有:

g(v2)=g(v1)+Pst(v1v2)=g(v1)+Pst(v1v2)=Pststv1+Pst(v1v2)=Pststv2=g(v2) \begin{aligned}g(v_2)&=g(v_1)+ |P_{st}(v_1\to v_2)|\\&=g^*(v_1)+ |P_{st}(v_1\to v_2)|\\&=|P_{st}(st\to v_1)| + |P_{st}(v_1\to v_2)|\\&=|P_{st}(st\to v_2)|\\&=g^*(v_2)\end{aligned}

根据引理一,此时的g(v2)g(v_2) 是最小的,所以若g(v2)g(v_2)更新,则g(v2)=g(v2)g(v_2)=g^*(v_2)

  • 若不更新,则说明g(v2)g(v1)+Pst(v1v2)=g(v2)g(v_2)\le g(v_1)+|P_{st}(v_1\to v_2)| = g^*(v_2),又因为g(v2)g(v2)g^*(v_2)\le g(v_2),则g(v2)=g(v2)g(v_2)=g^*(v_2)

这意味着通过v1v_1对节点v2v_2计算g(v2)g(v_2)后,无论是否更新,g(v2)g(v_2)都会取g(v2)g^*(v_2)。那么把v1v_1放入CLOSE LISTCLOSE\ LIST后,无论v2v_2CLOSE LISTCLOSE\ LISTOPEN LISTOPEN \ LIST中,还是不在,最终的g(v2)g(v_2) 都等于g(v2)g^*(v_2)。根据引理三,这时的f(v2)f(v_2)就是最小值了,以后不会再发生变化。

归纳假设:对于节点vn,n=kv_n,n=k时,满足g(vk)=g(vk)g(v_{k})=g^*(v_{k}),且以后不会发生变化。

归纳递推:对于节点vn,n=k+1,km1v_n,n=k+1,k\le m-1时:

利用算法本身性质一、引理一、引理二,有f(vk)=g(vk)+h(vk)=g(vk)+h(vk)g(vk)+h(vk)=Pst<PAf(v_k)=g(v_k)+h(v_k)=g^*(v_k)+h(v_k)\le g^*(v_k)+h^*(v_k)=|P_{st}|<|P_{A^*}|。也就是f(vk)<f(end)f(v_k)<f(end)也成立。由AA^*算法的性质一可以知道,vkv_k 节点一定可以在结束之前被选中,用来更新数据。

vk+1v_{k+1}不在OPEN LISTOPEN \ LISTCLOSE LISTCLOSE \ LIST中,直接更新g(vk+1)g(v_{k+1});若在OPEN LISTOPEN \ LISTCLOSE LISTCLOSE \ LIST中,接下来由g(vk+1)g(v_{k+1})g(vk)+Pst(vkvk+1)g(v_{k})+|P_{st}(v_k\to v_{k+1})|的大小关系,判断是否会通过通过vkv_kg(vk+1)g(v_{k+1})进行更新。

  • 若更新,则有:

g(vk+1)=g(vk)+Pst(vkvk+1)=g(vk)+Pst(vkvk+1)=Pststvk+Pst(vkvk+1)=Pststvk+1=g(vk+1) \begin{aligned}g(v_{k+1})&=g(v_k)+ |P_{st}(v_k\to v_{k+1})|\\&=g^*(v_k)+ |P_{st}(v_k\to v_{k+1})|\\&=|P_{st}(st\to v_k)| + |P_{st}(v_k\to v_{k+1})|\\&=|P_{st}(st\to v_{k+1})|\\&=g^*(v_{k+1})\end{aligned}

根据引理一,此时的g(vk+1)g(v_{k+1} ) 是最小的,所以若g(vk+1)g(v_{k+1})更新,则g(vk+1)=g(vK+1)g(v_{k+1})=g^*(v_{K+1})

  • 若不更新,则说明g(vk+1)g(vk)+Pst(vkvk+1)=g(vk+1)g(v_{k+1})\le g(v_k)+|P_{st}(v_k\to v_{k+1})| = g^*(v_{k+1}),又因为g(vk+1)g(vk+1)g^*(v_{k+1})\le g(v_{k+1}),则g(vk+1)=g(vk+1)g(v_{k+1})=g^*(v_{k+1})

这意味着通过vkv_k 对节点vk+1v_{k+1}计算g(vk+1)g(v_{k+1})后,无论是否更新,g(vk+1)g(v_{k+1})都会取g(vk+1)g^*(v_{k+1})。那么把vkv_k 放入CLOSE LISTCLOSE\ LIST后,无论vk+1v_{k+1}CLOSE LISTCLOSE\ LISTOPEN LISTOPEN \ LIST中,还是不在,最终的g(vk+1)g(v_{k+1}) 都等于g(vk+1)g^*(v_{k+1})。根据引理三,这时的f(vk+1)f(v_{k+1})就是最小值了,以后不会再发生变化。

综上所述:kZ,0km,g(k)=g(k)\forall k \in Z, 0\le k \le m, 满足g(k)=g^*(k),且以后不会发生变化。

因此,最后从OPEN LISTOPEN\ LIST 中取出end(vm)end(v_m)节点的时候,PA=f(vm)=g(vm)+h(vm)=g(vm)+h(vm)=g(vm)=Pst|P_{A^*}|=f(v_m)=g(v_m)+h(v_m)=g^*(v_m)+h(v_m)=g^*(v_m)=|P_{st}|。这是一条根据AA^*算法找到的路径PAP_{A^*}。这显然与我们最初的假设“通过AA^*算法找到的路径不是最小值,也就是Pst<PA|P_{st}|<|P_{A^*}|”矛盾。所以假设不成立,通过AA^*算法找到的就是最短路径。