求助英文高手,翻译经典文章——A*算法!!!重金酬谢!!!(200分)

  • 主题发起人 算法不懂
  • 开始时间

算法不懂

Unregistered / Unconfirmed
GUEST, unregistred user!
The maze we will look at is small, but has enough twists and turns to make it a little challenge for the A*.
The black lines are impassable, the red square denotes the start (0,0),and the green denotes the end (2,2).
Note that in the bottom-right corner there are two grey areas, one darker than the other. These are different
cost areas, the darker grey being a higher cost. This will help demonstration back-propagation of lower g-values
a bit later on.
Now, during the first couple of iterations we find the only the square that is open is the one directly beneath
us. We therefore have no choice but to travel there. In fact, this happens rightdo
wn the line - despite the
'fitness' of the node increasing and increasing (as distance increases).
Nevertheless, let us look at the calculations are are taken. For the first loop, only (1,0) is open, so we create
a node there - and the values are initialized:
G: 1 + board value (0) = 1
H: (2-0)*(2-0) + (2-0)*(2-0) = 8
F: G + H = 9
This is inserted on to the Open list (since we haven't explored any of its child nodes yet), and the process is
continued. Thing is, none of the other nodes are valid, so when we pick the best node off the Open list it is
this one. The process then
continuesdo
wn the line. Now, imagine that the algorithm continuesdo
wn the initial
line, from (0,0) to (0,19). Now, backtrack a little. When the algorithm assesses (0,18) which nodedo
you think
it would pick to move to next? (1,19) is the best since it is closer to the goal point than (0,19). It also
involves less tiles in the long run. So, we are now assessing (1,19). During this time, we assess (0,19) again,
since it is still an adjacent square. (0,19) is still on the Open list from when (0,18) assessed it. So, when
the algorithm comes to check for (0,19) it finds the node on the Open list, and then
adds it to its list of
children. It then
checks to see whether it would benefit by moving through (0,19) - itdo
esn't, so things are
left alone.
After (1,19) has been fully assessed, (2,18) is chosen as the next best node to move to. This generates another
scenario the algorithm needs to deals with. (2,18) assesses the nodes around it and finds that (1,19) is not on
the Open list, but is on the closed list (all (1,19)'s child nodes have been put on the open list). It therefore
adds it to its list of children and continues on its merry way since it would not benefit by going back through
(1,19).
We will allow the algorithm to run for a little while. Another scenario arises soon though, at (3,5). When the
algorithm assesses (3,5) it finds (4,4) on the closed stack. What it also finds is that its g-value is higher
than its predicted g-value would be if the path went via (3,5). In short, this means that either a high-cost
path had been explored, or simply more nodes were transversed to get to (4,4) than was necessary. We therefore
update the node accordingly, and then
propagate this changedo
wn its children. Luckily, in this case, none of
the children required propagation.
A case arises later on when a huge propagation is required - at (21,15) it is found that by going through (22,
16) a saving of 16 can be made (G(21,15) = 94, G(22,16) = 78). The intuitive reader can propagate the changes
backward if they wish!
Finally, the algorithm zooms around the squiggly area, up and over and reaches its goal. Notice that the
algorithm would chose to take the longer, but less costly route by going through the light grey area.
 
我想这是一个迷宫的问题,应带有一个图的。我理解的大意如下:
我们将要看到的迷宫小,但是它有很多的曲径,这便使得对A*有一个挑战。黑线是不能通行的,
红色正方形表示起点(0,0),绿色表示终点(2,2),注意在右下角有两个灰色区域,其中
一个颜色较深,这表示不同的价值区,较深的颜色区表示值较高,这有助于后边较低估值的
演示。
 
现在,我们发现在第一个回复期间,在我们下面的开放的正方形只有唯一的一个,因此,我们别无选择,
只能往那儿走。事实上,这正好是正确的路线——尽管合适的节点越来越多(随着距离的增加)。
然而,让我们考虑一下要考虑的计算,在第一循环,只(1,0)是开放的,因此,我们在那儿产生一个
节点,值被初始化:
G: 1 + board value (0) = 1
H: (2-0)*(2-0) + (2-0)*(2-0) = 8
F: G + H = 9
 
把这些插进开放的列表里(既然我们还没有检测它的任何子节点),过程继续进行,事情是,
没有其它节点是有效的,因此当我们从列表里选择最好的一个节点时,就是这一个,过程沿着
线继续进行。现在,设想一下,运算法则继续沿着初始线下去,从(0,0)到(0。19),现在,
回溯一点,当运算法则经过(0,18)时,你认为它会选择哪一个节点作为下一个,(1,19)
是最好的,既然它比(0,19)离目标更近。
从长远来看,它包括了更少的碎片(?),因此,现在我们选择(1,19)。在这时,我们估计
一下(0,19),既然它仍是一个邻近的正方形区,当(0,18)经过它的时候,它仍在开放
列表里,因此当运算法则访问到(0,19),发现开放列表里的节点,于是把它加到子节点中,
然后继续检查是否从(0,19)受益,没有,结果就明显了。
 
(1,19)被充分考虑之后,(2,18)被选定为下一个最好的节点,这就产生了一个运算法则必须处理
的另一种情况,(2,18)评估它周围的所有节点,发现(1,19)不在开放列表里,但在邻近列表里,
((1,19)的所有子节点都在开放列表里),既然没有从(1,19)通过中受益,因此将它加在子列表里,继续它
的快乐之旅。

剩下一点你自己翻吧,不当之处还望谅解。
[:D][:D][:)][:)][8D][^][^]
 
to 算法不懂:
你的这篇文章应该说我以前曾经接触过一些,翻译出来就没有必要了;
只是让你看看2篇文章,对你理解A*算法绝对有好处!
一篇是介绍A*算法,另一篇就是你的这篇文章的姊妹篇了。^_^
>1 :
A*在游戏设计中有它很典型的用法,是人工智能在游戏中的代表。
A*算法在人工智能中是一种典型的启发式搜索算法
一、何谓启发式搜索算法:
在说它之前先提提状态空间搜索。状态空间搜索,如果按专业点的说
法就是将问题求解过程表现为从 初始状态到目标状态寻找这个路径的
过程。通俗点说,就是在解一个问题时,找到一条解题的过程可以从
求解的开始到问题的结果(好象并不通俗哦)。由于求解问题的过程
中分枝有很多,主要是求解过程中求 解条件的不确定性,不完备性造
成的,使得求解的路径很多这就构成了一个图,我们说这个图就是状
态空 间。问题的求解实际上就是在这个图中找到一条路径可以从开始
到结果。这个寻找的过程就是状态空间搜索。
常用的状态空间搜索有深度优先和广度优先。广度优先是从初始状态
一层一层向下找,直到找到目标 为止。深度优先是按照一定的顺序前
查找完一个分支,再查找另一个分支,以至找到目标为止。这两种算
法在数据结构书中都有描述,可以参看这些书得到更详细的解释。
前面说的广度和深度优先搜索有一个很大的缺陷就是他们都是在一个
给定的状态空间中穷举。这在状 态空间不大的情况下是很合适的算法
,可是当状态空间十分大,且不预测的情况下就不可取了。他的效率
实在太低,甚至不可完成。在这里就要用到启发式搜索了。
启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,
得到最好的位置,再从这个位置 进行搜索直到目标。这样可以省略
大量无畏的搜索路径,提到了效率。在启发式搜索中,对位置的估价
是十分重要的。采用了不同的估价可以有不同的效果。我们先看看
估价是如何表示的。
启发中的估价是用估价函数表示的,如:
f(n) = g(n) + h(n)
其中f(n) 是节点n的估价函数,g(n)实在状态空间中从初始节点到
n节点的实际代价,h(n)是从n到目 标节点最佳路径的估计代价。在
这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说
详细点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >> g(n)
时,可以省略g(n),而提高效率。这些就深了, 不懂也不影响啦!
我们继续看看何谓A*算法。

二、初识A*算法:
启发式搜索其实有很多的算法,比如:局部择优搜索法、最好优先搜索
法等等。当然A*也是。这些算法 都使用了启发函数,但在具体的选取
最佳搜索节点时的策略不同。象局部择优搜索法,就是在搜索的过程
中 选取“最佳节点”后舍弃其他的兄弟节点,父亲节点,而一直得搜
索下去。这种搜索的结果很明显,由于舍 弃了其他的节点,可能也把
最好的节点都舍弃了,因为求解的最佳节点只是在该阶段的最佳并不
一定是全局 的最佳。最好优先就聪明多了,他在搜索时,便没有舍弃
节点(除非该节点是死节点),在每一步的估价中 都把当前的节点和
以前的节点的估价值比较得到一个“最佳的节点”。这样可以有效的
防止“最佳节点”的 丢失。那么A*算法又是一种什么样的算法呢?
其实A*算法也是一种最好优先的算法。只不过要加上一些约束 条件
罢了。由于在一些问题求解时,我们希望能够求解出状态空间搜索的
最短路径,也就是用最快的方法求 解问题,A*就是干这种事情的!
我们先下个定义,如果一个估价函数可以找出最短的路径,我们称之
为可采 纳性。A*算法是一个可采纳的最好优先算法。A*算法的估价
函数可表示为:
f'(n) = g'(n) + h'(n)
这里,f'(n)是估价函数,g'(n)是起点到终点的最短路径值,h'(n)
n到目标的最断路经的启发值。由 于这个f'(n)其实是无法预先知道的
,所以我们用前面的估价函数f(n)做近似。g(n)代替g'(n),但
g(n)>=g'(n) 才可(大多数情况下都是满足的,可以不用考虑),
h(n)代替h'(n),但h(n)<=h'(n)才可(这一点特别的重 要)。可以
证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。
我们说应用这种估价函数的 最好优先算法就是A*算法。哈!你懂了吗?
肯定没懂!接着看!

举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点
所在的层数,h(n)=0,这种h(n)肯 定小于h'(n),所以由前述可知广
度优先算法是一种可采纳的。实际也是。当然它是一种最臭的A*算法。

再说一个问题,就是有关h(n)启发函数的信息性。h(n)的信息性通俗
点说其实就是在估计一个节点的值 时的约束条件,如果信息越多或约
束条件越多则排除的节点就越多,估价函数越好或说这个算法越好。
这就 是为什么广度优先算法的那么臭的原因了,谁叫它的h(n)=0,
一点启发信息都没有。但在游戏开发中由于实 时性的问题,h(n)
信息越多,它的计算量就越大,耗费的时间就越多。就应该适当的减
小h(n)的信息,即 减小约束条件。但算法的准确性就差了,这里就
有一个平衡的问题。
>2 :
一、在这里我将对A*算法的实际应用进行一定的探讨,并且举一个有关A*
算法在最短路径搜索的例子。
二、A*算法的程序编写原理
我在《初识A*算法》中说过,A*算法是最好优先算法的一种。只是有一
些约束条件而已。 我们先来看看最好优先算法是如何编写的吧。
如图有如下的状态空间:(起始位置是A,目标位置是P,字母后的数字表示节点的估价
值)
A - 5
/ │ \
                /  │  \
               ↓   ↓   \
               B-4  C-4     D-6
              /|  / \     /\  
             / |  /   \   /  \  
            /  |  |  ↓   |   \    
           E-5  F-5  G-4  H-3  I    J         
  
           //   /\ |  / \  /\   \        
 
          /  / /  ||  /  \ / \   \        
    
         /   v   || ↓   ↓/  \  \       
     
         K    L   M N  O-2  P-3   Q R       
     
         |  |                       
| |
| |
S T
搜索过程中设置两个表:OPEN和CLOSED。OPEN表保存了所有已生
成而未考察的节点,CLOSED 表中记录已访问过的节点。算法中有一步
是根据估价函数重排OPEN表。这样循环中的每一 步只考虑OPEN表中状
态最好的节点。具体搜索过程如下:

1)初始状态:
OPEN=[A5];CLOSED=[];
2)估算A5,取得搜有子节点,并放入OPEN表中;
OPEN=[B4,C4,D6];CLOSED=[A5]
3)估算B4,取得搜有子节点,并放入OPEN表中;
OPEN=[C4,E5,F5,D6];CLOSED=[B4,A5]
4)估算C4;取得搜有子节点,并放入OPEN表中;
OPEN=[H3,G4,E5,F5,D6];CLOSED=[C4,B4,A5]
5)估算H3,取得搜有子节点,并放入OPEN表中;
OPEN=[O2,P3,G4,E5,F5,D6];CLOSED=H3C4,B4,A5]
6)估算O2,取得搜有子节点,并放入OPEN表中;
OPEN=[P3,G4,E5,F5,D6];CLOSED=[O2,H3,C4,B4,A5]
7)估算P3,已得到解;
看了具体的过程,再看看伪程序吧。算法的伪程序如下:

Best_First_Search()
{
Open = [起始节点];
Closed = [];
while ( Open表非空 )
{
从Open中取得一个节点X,并从OPEN表中删除。
if (X是目标节点)
{
求得路径PATH;返回路径PATH;
}
for (每一个X的子节点Y)
{
if( Y不在OPEN表和CLOSE表中 )
{
求Y的估价值;并将Y插入OPEN表中;//还没有排序
}
else

if( Y在OPEN表中 )
{
if( Y的估价值小于OPEN表的估价值 )
更新OPEN表中的估价值;
}
else
//Y在CLOSE表中
{
if( Y的估价值小于CLOSE表的估价值 )
{
更新CLOSE表中的估价值;
从CLOSE表中移出节点,并放入OPEN表中;
}
}
将X节点插入CLOSE表中;
按照估价值将OPEN表中的节点排序;
}//end for
}//end while
}//end func
啊!伪程序出来了,写一个源程序应该不是问题了,依葫芦画瓢就可以。
A*算法的程序与此 是一样的,只要注意估价函数中的g(n)的h(n)约束
条件就可以了。不清楚的可以看看《初识A*算法》。好了,我们可以
进入另一个重要的话题,用A*算法实现最短路径的搜索。在此之 前你
最好认真的理解前面的算法。
三、用A*算法实现最短路径的搜索
在游戏设计中,经常要涉及到最短路径的搜索,现在一个比较好的方法
就是用A*算法进行设 计。他的好处我们就不用管了,反正就是好!^_*
注意下面所说的都是以 ClassAstar 这个程序为蓝本,你可以在这里
下载这个程序。这个程 序是一个完整的工程。里面带了一个EXE文件。
可以先看看。
先复习一下,A*算法的核心是估价函数f(n),它包括g(n)和h(n)两部分.
g(n) 是已经走过的 代价,h(n)是n到目标的估计代价。在这个例子
中g(n)表示在状态空间从起始节点到 n节点的 深度,h(n)表示n节点
所在地图的位置到目标位置的直线距离。啊!一个是状态空间,一个
是 实际的地图,不要搞错了。再详细点说,有一个物体A,在地图上
的坐标是(xa,ya),A所要到 达的目标b的坐标是(xb,yb)。则开始搜索
时,设置一个起始节点1,生成八个子节点2 - 9 因 为有八个方向。
如图: 节 点 1 g(1)=0
/| | \ h(1)=(Xb*Xb-Xa*Xa)+(Yb*Yb-Ya*Ya)
/ / | \
/ / | \
节点2 节点3 节点4...节点9 g(9)=1
/|\ h(9)=(Xb*Xb-X9*X9)+(Yb*Yb-Y9*Y9)
/ | \
           /   |   \    
节点10 节点11...节点17 g(17)=2
h(17)=(Xb*Xb-X17*X17)+(Yb*Yb-Y17*Y17)
仔细看看节点1、9、17的g(n)和h(n)是怎么计算的。现在应该知道了
下面程序中的f(n)是如何 计算的吧。开始讲解源程序了。其实这个
序是一个很典型的教科书似的程序,也就是说只要 你看懂了上面的
伪程序,这个程序是十分容易理解的。不过他和上面的伪程序有一些
的不同, 我在后面会提出来。
先看搜索主函数:
void AstarPathfinder::FindPath(int sx, int sy, int dx, int dy)
{
NODE *Node, *BestNode;
int TileNumDest;
//得到目标位置,作判断用
TileNumDest = TileNum(sx, sy);
//生成Open和Closed表
OPEN=( NODE* )calloc(1,sizeof( NODE ));
CLOSED=( NODE* )calloc(1,sizeof( NODE ));
//生成起始节点,并放入Open表中
Node=( NODE* )calloc(1,sizeof( NODE ));
Node->g = 0;
//这是计算h值
Node->h = (dx-sx)*(dx-sx) + (dy-sy)*(dy-sy);
//此处按道理应用开方
//这是计算f值,即估价值
Node->f = Node->g+Node->h;
Node->NodeNum = TileNum(dx, dy);
Node->x = dx;
Node->y = dy;

OPEN->NextNode=Node;
// make Open List point to first node
for (;;)
{ //从Open表中取得一个估价值最好的节点
BestNode=ReturnBestNode();
//如果该节点是目标节点就退出
if (BestNode->NodeNum == TileNumDest) // if we've found the
//end, break and finish
break;
//否则生成子节点
GenerateSuccessors(BestNode,sx,sy);
}
PATH = BestNode;
}
再看看生成子节点函数 GenerateSuccessors:
void AstarPathfinder::GenerateSuccessors(NODE *BestNode,int dx,int dy)
{
int x, y;
//哦!依次生成八个方向的子节点,简单!
// Upper-Left
if ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y-TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Upper
if ( FreeTile(x=BestNode->x, y=BestNode->y-TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Upper-Right
if ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y-TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Right
if ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y) )
GenerateSucc(BestNode,x,y,dx,dy);
// Lower-Right
if ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y+TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Lower
if ( FreeTile(x=BestNode->x, y=BestNode->y+TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Lower-Left
if ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y+TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Left
if ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y) )
GenerateSucc(BestNode,x,y,dx,dy);
}
看看最重要的函数GenerateSucc:
void AstarPathfinder::GenerateSucc(NODE *BestNode,int x,int y,int dx,int dy)
{
int g, TileNumS, c = 0;
NODE *Old, *Successor;
//计算子节点的 g 值
g = BestNode->g+1;
//g(Successor)=g(BestNode)+cost of getting
//from BestNode to Successor
TileNumS = TileNum(x,y);
// identification purposes
//子节点再Open表中吗?
if ( (Old=CheckOPEN(TileNumS)) != NULL ) // if equal to NULL then

//not in OPEN list, else
it returns the Node in Old
{
//若在
for( c = 0;
c < 8;
c++)
if( BestNode->Child[c] == NULL ) // Add Old to the list of
// BestNode's Children (or Successors).
break;
BestNode->Child[c] = Old;
//比较Open表中的估价值和当前的估价值(只要比较g值就可以了)
if ( g < Old->g ) // if our new g value is < Old's then

//reset Old's parent to point to BestNode
{
//当前的估价值小就更新Open表中的估价值
Old->Parent = BestNode;
Old->g = g;
Old->f = g + Old->h;
}
}
else
//在Closed表中吗?
if ( (Old=CheckCLOSED(TileNumS)) != NULL ) // if equal to NULL then

// not in OPEN list, else
it returns the Node in Old
{
//若在
for( c = 0;
c< 8;
c++)
if ( BestNode->Child[c] == NULL ) // Add Old to the list of
//BestNode's Children (or Successors).
break;
BestNode->Child[c] = Old;
//比较Closed表中的估价值和当前的估价值(只要比较g值就可以了)
if ( g < Old->g ) // if our new g value is < Old's then

// reset Old's parent to point to BestNode
{
//当前的估价值小就更新Closed表中的估价值
Old->Parent = BestNode;
Old->g = g;
Old->f = g + Old->h;
//再依次更新Old的所有子节点的估价值
PropagateDown(Old);
// Since we changed the g value of
//Old,we need to propagate this new
//valuedo
wnwards, i.e.
//do
a Depth-First traversal of the tree!
}
}
else
//不在Open表中也不在Close表中
{
//生成新的节点
Successor = ( NODE* )calloc(1,sizeof( NODE ));
Successor->Parent = BestNode;
Successor->g = g;
Successor->h = (x-dx)*(x-dx) + (y-dy)*(y-dy);
// shoulddo

// sqrt(), but since wedo
n't really
Successor->f = g+Successor->h;
// care about the distance but
//just which branch looks
Successor->x = x;
// better this should suffice.
// Anyayz it's faster.
Successor->y = y;
Successor->NodeNum = TileNumS;
//再插入Open表中,同时排序。
Insert(Successor);
// Insert Successor on OPEN list wrt f
for( c =0;
c < 8;
c++)
if ( BestNode->Child[c] == NULL ) // Add Old to the
// list of BestNode's Children (or Successors).
break;
BestNode->Child[c] = Successor;
}
}
哈哈!A*算法我懂了!当然,我希望你有这样的感觉!不过我还要
再说几句。仔细看看这个程序,你会发现,这个程序和我前面说的伪
程序有一些不同,在GenerateSucc函数中,当子节点 在Closed表中时,
没有将子节点从Closed表中删除并放入Open表中。而是直接的重新
计算该 节点的所有子节点的估价值(用PropagateDown函数)。这
样可以快一些!另当子节点在 Open 表和Closed表中时,重新的计算
估价值后,没有重新的对Open表中的节点排序,我有些想不通, 为什
么不排呢?:-(,会不会是一个小小的BUG。
 
多人接受答案了。
 

Similar threads

I
回复
0
查看
2K
import
I
I
回复
0
查看
2K
import
I
I
回复
0
查看
1K
import
I
I
回复
0
查看
2K
import
I
I
回复
0
查看
3K
import
I
顶部