您的位置:首页 > 理论基础 > 数据结构算法

关于A 星算法的研究与简单分析及其实现-A star

2016-06-08 13:34 337 查看
关于A 星算法的研究与简单分析及其实现
Hanzack@163.com
(本文为自己所写例子为本人所创,欢迎批评指责,大家一起讨论,若转载请注明作者)
A星算法:从起始点开始,找它的所有子节点,加入到一个特殊队列priorityQueue(简称pQ)中(类似于一个数组)。 然后利用一个特殊函数对这些点进行打分, 这个特殊函数我们简单称为启发函数。   根据打分选出最好的点。 把这个节点从pQ队列中删除,并把这个节点的的所有子节点加入到pQ队列中。 循环这个步骤 知道找到到达终点的最短路径。

几个简单小问题:

1.     当一个节点没有子节点的时候怎么办?

不可能没有, B连接D, 那么互为子节点。

2.     当一个节点的子节点为目的点时候,怎么找到到达该目的点过程中的所有节点?

每一个节点都要封装为一个对象,存储着自己的父节点。由于每一个节点都可能有几个父节点,此时我们只要存储:在最短路径上该点的上一个节点为父节点即可。

(根据打分来看,该点是从哪一点过来的,那么就存储那一点为父节点)

二:

A星算法的简单图文描述:

先把A点加入队列pQ, 然后找到A点的子节点B, C, D 数字表示分数(这里数字越小表示越好)。然后把队列中的A点删除放入到一个封闭队列  close中(理解为一个叫做close的数组变量或者集合变量)。

然后从队列pQ,中找到分数最好的点, 这时候明显选B点,然后把E,F点加入到pQ队列中。并且把B移动到封闭队列中。 此时pQ中有如下几个点:  C, D, E, F。 显然分数最好的是C点, 然后。。。。。。重复上述行为,直到找到终点。

简单来说,找到一个点,把它的子节点加入队列,再从队列中所有点中找到分数最好的点,循环。

 

三:理解大致理论后的难点。
而在做代码实现时,涉及的难点为:
1.     每次找一个点的子节点的时候不能直接把子节点直接全部加入到队列中:
a.     由于无向图中相连节点互为子节点, 所以先判断该子节点是否已经在close队列中了(就是要先判断是否已经被访问过了)。
b.     还要判断该子节点是否已经在pQ队列中,因为它也有可能是别人的子节点,而已经被加入到了pQ中。
c.     那么如果都不是,就可以加入到pQ队列中了。
d.     否则,是不是不用考虑它直接看下一个子节点呢? 不是,而是要做条件判断做其他考虑。
2.     关于pQ队列, 写一个pQ的类用来按照分数的好坏来排序pQ队列中的对象(这里存储的是节点)。这个类的写法可以参考stack的原理。  把stack的先进后出FILO 原理改为,进来了按照分数决定先出后出。
3.     书写一个节点类,每一个节点都是一个打包好的对象,有属性:父节点, 权值()
 
四:  最需要看的案例-----详细告诉你以下原因
1.     为什么在把子节点加入到队列中前需要检查该节点是否已经在于队列中?
2.     为什么为什么在把子节点加入到队列中前需要检查该节点是否已经在于封闭列表里,如果在封闭列表里,为什么又会产生把它从封闭列表里面拉出来的情形。
 
 
 
 
 
 
 
 
 
 
 
 
逻辑如下:
此处使用的函数为  F=g + h,每一个点的分值函数都是F. 其中 g:从起点开始经由过路点到该点的距离, h是一个估值函数,此处使用的估值函数为欧几里得距离,即从该点到目的点X的直线距离(为什么使用这个函数,因为这个距离是两点可能的最小距离。请自行查看启发函数的目的和用处)
1.      A.由图和分值函数F可以看出,第一点为A点, 它的F值=g+h=0(从起始点A到A点的距离为0)+ A到X的距离。
Fa=1000
B.  Fb=AB距离+BX距离   为600
C. Fc=AC+CX       为670
2. 当由A点开始,先把A点的子节点找出来暂时先不放到队列中,并把A点放入到封闭队列close中去。现在开始检查子节点是否可以并且怎样的方式进入队列
a.  先判断子节点是否在close队列中。发现所有子节点(B,C)不在
b.  判断子节点是否已经出现在了pQ队列中。 不在
此时将两个节点加入到pQ队列中
      
 
 
 
 
 3 .  从队列中找出一个分数最高的, 由于队列中只有两个点:B, (f=100+500) 和C (f=70+600)
     故将B点找出来(600《670 距离越小越好), 并且去找它的子节点。
 
此时B的子节点一共有A, C, D  三点
a.      先判断子节点是否在close队列中。
发现A在封闭列表里:
此时要判断A点新的F值是否比原来的F值(1000)小?
      是---》将A点的F值该为新的并且把它移动进pQ中
      否---》去看下一个子节点,不在考虑A点。
那么这时候A点新的F值是多少呢?
F新= 从A点经由B点到A点的距离+AX距离=100+100+1000
  发现C 不在close队列中,
   发现D不在close队列中
   发现E不在close队列中
b.      判断子节点是否已经出现在了pQ队列中。
       此处不考虑A点,因为上一步已经发现它在close队列中,且不满足被拉出来的条件
       发现D不在pQ队列中
       发现E不在pQ队列中
            故把D, E 加入到pQ队列中。注意此时:Fd=(AB+BD)+DX=750
                                             Fe=(AB+BE)+EX=1100
        发现C在pQ队列中,原来的F值为670
                  经由B点过来的F值为:100+10+600=710.
                  710》670  所以C的F值不用改变,C的父节点仍然是A
所以经过以上步奏:(两个列表有以下值:)
     Close:  A(仍然保留),   B(新加入的)
     PQ:    C(无需更改), D,E
     
4 . 已经找了两个点了, 现在继续从pQ剩余点中找出距离最小的点。发现是C点,F
值只有670。
a.      先判断子节点是否在close队列中。
发现A在封闭列表里:
此时要判断A点新的F值是否比原来的F值(1000)小?
      是---》将A点的F值该为新的并且把它移动进pQ中
            还要修改父节点
      否---》去看下一个子节点,不在考虑A点。
那么这时候A点新的F值是多少呢?
F新= 从A点经由C点到A点的距离+ AX距离=70+70+1000=1140
发现B在close队列中,
    
此时要判断B点新的F值是否比原来的F值(600)小?
      是---》将B点的F值改为新的并且把它移动进pQ中
            还要把B点的父节点变为C,
原来的是A
      否---》去看下一个子节点,不在考虑B点。
那么这时候B点新的F值是多少呢?
F新= 从A点经由C点到B点的距离+ AX距离=70+10+500=580
发现比原来F值小。
   发现E不在close队列中
 
b . 判断子节点是否已经出现在了pQ队列中。
       此处不考虑B点,因为上一步已经发现它在close队列中,且满足被拉出来的条件
       发现E已经在pQ队列中
                 E原来是由B节点产生的子节点所以它原来的F=100+700+300=1100
                  现在经由C点产生的F值= 70+400+300=770
         此时修改  E的F值从1100改为770, 并且把父节点从B改为C
       
所以经过以上步奏:(两个列表有以下值:)
     Close:  A(仍然保留F=1000),  
B(移动进pQ)  C, (新加入的F=670)
     PQ:    C(移除), D (F=750),E(F值被修改成了770) 
B(又移动回来了F=580)
 
 
5 .  继续从剩下的点中找F值最小点。 显然又是找到B,
    
a.      先判断子节点是否在close队列中。
发现A在封闭列表里:
此时要判断A点新的F值是否比原来的F值(1000)小?
      是---》将A点的F值该为新的并且把它移动进pQ中
            还要修改父节点
      否---》去看下一个子节点,不在考虑A点。
那么这时候A点新的F值是多少呢?
F新= 从A点经由C点到B点再到A点的距离+ AX距离=1180
发现C在close队列中,
    
此时要判断C点新的F值是否比原来的F值(670)小?
      是---》将B点的F值改为新的并且把它移动进pQ中
            还要把B点的父节点变为C原来的是A
      否---》去看下一个子节点,不在考虑B点。
那么这时候C点新的F值是多少呢?
F新= 从A点经由C点到B点再回到C点+ CX距离=690
发现比原来F值大。
   发现E不在close队列中
   发现D不在close队列中
b . 判断子节点是否已经出现在了pQ队列中。
       此处不再考虑A点,C点
 
         E已经被上一步C作为父节点所修改了
                  现在经由B点产生的新F值= 70+10+700+300=1080
         D   原来F值为750   现在经由ACBD路线是730,父节点从B变为B
       
所以经过以上步奏:(两个列表有以下值:)
     Close:  A(仍然保留F=1000),   B(重新移回) 
C, (仍然保留)
     PQ:    D (F修改为730),E(F值被修改成了770) 
B(被移动出去了F为570)
 
6.  这时候分数最好的点是D, 找子节点,只有两个一个是X一个是B。
分析方法同上。
 区别是, x的分值函数F=g+h,   h=0
g= AC+CB+BD+DX=70+10+300+350=730,故X的F值若经由D则是730.
把X点加入到pQ队列中去
   所以经过以上步奏:(两个列表有以下值:)
     Close:  A(仍然保留F=1000),   B(保留) 
C, (仍然保留)  D(新加进来的)
     PQ:    D (F修改为730),E(F值被修改成了770) 
B(被移动出去了F为570)
             X(新加进来的)
7.  此时,发现X 和E比选   X
当找X子节点之前(while的条件)发现, X是终点就结束循环。
如果,E比X小, 我们就重新分解E。。。
8.  根据X点依次找父节点就可以找到路径。
     此处为  ACBDX
 
至此全部完成。(将2,3,4,5,6,7  步融合成一个while循环)
While的条件为  currNode 是否为X目标点         currNode就是那个要被分解的点(例如一开始的A点)。
问题:
1.      如果AX的直线距离只有100, 那么A的F值就是0+100=100.   那不是每次都找自己?
当然不是, 如果F值是100, 第一次找它,分解子节点以后(X不是子节点),就把它扔到了closed列表中去了。 下次找最小值的时候是从pQ队列中找的。由于没有X,所以还是会继续找到C,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
以下是代码逻辑:
 
While(! CurrNode is targetNode):
{
For  currNode  的所有子节点
      If  该子节点在close 列表中
         {  If   该子节点的刚算出来的F值小于原来的F值
               {  把该子节点的父节点更新
                 把该子节点移动到pQ中}}
      Else if  该子节点已经在pQ中
           {   If   该子节点的刚算出来的F值小于原来的F值
                      把该子节点的父节点更新 }
      Else: 
           { 把该子节点加入到pQ中去 }
 
    
 
 
 
 
 
 
 
 
 
 
 
        If pQ ==null :  return null.     //说明没有点到达
  令currNode等于pQ列表中的F最小值
       将该点移出pQ 
}
 
 
 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息