前面的文章已经讨论过,当H函数可变时,前面给出的AStar算法伪过程存在问题,并且通过实际的例子证明了问题的存在。现在,让我们具体分析一下问题究竟出现在什么地方。
我们回顾一下AStar算法的证明过程,其中一个关键的过程在于结论2的得出。结论2的得出依靠引理3。如果H函数可变,那么引理3是不成立的。对于某个节点node而言,F(node)=G(node)+H(node)。如果H值不变,则当G值最小时F值最小。如果H函数可变,则当函数G函数最小时,对于当前的H值来说是最小的F值。可是如果后面H值变了,则对于某个稍大的G值而言,所得的F值可能会小于之前那个F值。
我们来看看注意点7的过程和图2。
图2
开始的H值为:H(A)=-100,H(C)=40,H(end)=0。先扩展start节点然后是A节点。此时从start节点到A节点的路径值G(A)=100,F(A)=0,此时A的F值并不是最小值。如果H函数值不变,则扩展C节点时会再次计算A节点的F值,因为G(A)=30,则F(A)=-70,此时根据引理3,该G(A)为最小值,对应的F值是最小值。可是H函数值可变,当G(A)达到最小值时,H(A)变成了20远大于之前的H值-100,使得尽管G值达到最小,但是对应的F值并没有达到最小,根据AStar算法,不会进行更新,使得从start到A的路径没有更新成从start到C到A。
我们现在对算法作出修改,在原始的算法中,注意注释中的程序注意点1和程序注意点2,比较都是基于F值进行比较,现在我们对这两处作出修改,比较基于G值进行。
作出了修改以后,引理3就不存在了。因为比较基于G值进行比较,G(S1)= Pstp (start-S1)是最小值,设定此时的F值为F1,根据修改过的AStar算法,此时节点S1的F值不会改变。并且因为H(S1)<=β(S1),F1<= Pstp (start-S1)<=N<M。至此,结论1仍然成立。
现在我们假设节点Sm以最小的G值进行了扩展,1<=m<=k,G(Sm)= Pstp(start-Sm),此时对应的F值是Fm。下面看看节点Sm+1。如果m=k,那么Sm+1就是end节点。
扩展Sm节点时,因为Sm+1节点是其后继节点,则计算Sm+1节点的F值为F(Sm+1)=G(Sm+1)+H(Sm+1)=G(Sm)+w(Sm,Sm+1)+ H(Sm+1)。因为G(Sm)= Pstp(start-Sm)。因为Sm节点以及Sm+1节点都是最短路径stp上的节点,很显然,Pstp(start-Sm)+w(Sm,Sm+1)= Pstp(start-Sm+1)。因此,F(Sm+1)= Pstp(start-Sm+1)+ H(Sm+1)<=N<M。令此时的F值为Fm+1。至此结论2仍然成立。
结论1和结论2仍然成立。和上面的证明相同的过程,可以得出算法的正确性。
现在将修改过的算法伪过程列出来:
struct Node{
int g; //该节点的g值
int h; //该节点的h值
int f; //该节点的f值
Node* pre; //该节点的前驱节点
};
AStar_Search(){
struct Node start_node;
start_node.g = 0;
start_node.h = H(start);
start_node.f = start_node.h;
start_node.pre = NULL;
OPEN链表 = [start_node]; CLOSE链表 = [];
while ( OPEN链表非空 ) {
如果H函数发生了变化,更新OPEN链表中的Node结构的h和f属性;
从OPEN链表中取得F值最小的Node,称之为x_node,对应的节点称之为为x;
从OPEN链表中删除x_node;
if (x是end节点){
根据每个节点对应的node结构的pre指针,返回路径;
}
for (x的每一个后继节点y){
struct Node y_node;
y_node.g = x_node.g+w(x,y);
y_node.h = H(y);
y_node.f = y_node.g+y_node.h;
y.pre = x_node;
if( y不在OPEN表 and 不在CLOSE表中){
把y_node放到OPEN表中;
}else if( y在OPEN表中){
取出OPEN表中的y节点对应的Node结构,称之为y_open;
if( y_node.g小于y_open.g) { //程序注意点1
y_open.g=y_node.g;
y_open.h=y_node.h;
y_open.f =y_node.f;
y_open.pre = y_node.pre;
}
}else{ //y在CLOSE表中
取出CLOSE表中的y节点对应的Node结构,称之为y_close;
if(y_node.g小于y_close.g){ //程序注意点2
将y_close从CLOSE链表中删除
把y_node放到OPEN表中;
}
}
} //end for
将x_node放入到CLOSE表中;
} //end while
} // end AStar_Search
H函数毕竟是一个估计函数,随着算法的进行,有可能对节点的H值进行更新以更准确的反映出节点到终点的最短路径值。因此,这里对算法的修改会具有某些实际意义。