A[i,j]=a[i,k]+a[k,j];
易知该算法的复杂度为o(n3)。
执行该算法后矩阵A中aij即为点i与点j间的最短路径,若要求路径的具体行程,需在算法中以数组保存路径的改变信息,这里不再介绍。
2. Dijkstra算法
这种算法是Dijkstra于1959年提出的,主要用于计算图G中的某一点u0到其它点的最短距离。
算法:
Step1:令l(u0)=0;l(v)=∞;v≠u0
S0={u0};v=0;
Step2:"vÎ┑Si=V(G)-Si
l(v)=min{l(v),l(uI)+ω(ui,v)}
设uI+1是使l(v)取到最小值的┑Si中的点。
令Si+1=Si∪{ui+1}
Step3:If i=γ(G)-1 then Stop.
If i<γ(G)-1 then i=i+1,Goto Step2.
该算法的复杂度为o(n2)。它与Floyd算法相比,优点在于计算量小,缺点是每次只能计算出图G中的一个点到其它点的最短路径,但由于网络通信中对路由的选择只需计算当前点与它点的距离,故此法得以广泛应用。
3. 广度优先搜索算法(Broad-First-Search)
广度优先搜索算法是一种搜索策略,与之相对应的还有深度优先搜索算法。广度优先是指从图G中的某点为始点出发,标记出所有与之相邻的点,并再以所有与之相邻的点为始点,搜索所有与这些点相邻的点,从而逐层向下扩展,实现对图的遍历。同理,深度优先搜索是指从某点出发,逐层向下扩展,直到无路可扩展时向上回溯,它是优先考虑图的深度(指从某点的扩展深度),而广度优先则优先考虑图的广度(指从某点的可扩展量)。由于该算法仅提供出一种搜索的策略,故它不仅可用于最短路径的搜索,事实上,图论中有很多问题均可通过此类搜索策略而得到实现。本文提出它只是因为在A*算法中,其部分地用到了该算法的思想。这里仅给出它的思想,不再对它在最短路径中的应用介绍。
算法:
Step1: "vÎV(G),令l(v)=0,l=0
Step2:If所有标号为l的顶点u的相关联的边皆已标号时,Then Goto Step3。
Else 把与u相关联的边的未标号的顶点标以l+1,并记录这些边。
令l=l+1,Goto(2)。
Step3:Stop。
4. 动态规划算法
这种算法利用动态规划理论求解最短路径,即将始点到终点的路程分为若干状态,从而将之转化为多阶段决策问题,这样就可用动态规划理论来解决。但这也必然决定了它的局限性,即只有求解可进行状态划分的问题,而事实上有很多问题是不能进行如是转化的,但这种算法却是在这种特殊条件下的最佳算法,因而也得到一些应用,这里不再详述。
三.A*算法
1. 简介
A*算法是到目前为止最快的一种计算最短路径的算法,但它一种‘较优’算法,即它一般只能找到较优解,而非最优解,但由于其高效性,使其在实时系统、人工智能等方面应用极其广泛。
A*算法结合了启发式方法(这种方法通过充分利用图给出的信息来动态地作出决定而使搜索次数大大降低)和形式化方法(这种方法不利用图给出的信息,而仅通过数学的形式分析,如Dijkstra算法)。它通过一个估价函数(Heuristic Function)f(h)来估计图中的当前点p到终点的距离(带权值),并由此决定它的搜索方向,当这条路径失败时,它会尝试其它路径。
因而我们可以发现,A*算法成功与否的关键在于估价函数的正确选择,从理论上说,一个完全正确的估价函数是可以非常迅速地得到问题的正确解答,但一般完全正确的估价函数是得不到的,因而A*算法不能保证它每次都得到正确解答。一个不理想的估价函数可能会使它工作得很慢,甚至会给出错误的解答。
为了提高解答的正确性,我们可以适当地降低估价函数的值,从而使之进行更多的搜索,但这是以降低它的速度为代价的,因而我们可以根据实际对解答的速度和正确性的要求而设计出不同的方案,使之更具弹性。
2. 实现
限于A*算法实现上的复杂,这里不能给出具体的算法流程,下面分几个部分对A*算法实现中的几个关键性问题加以阐述。
3. 数据结构
众所周知,对图的表示可以采用数组或链表,而且这些表示法也各也优缺点,数组可以方便地实现对其中某个元素的存取,但插入和删除操作却很困难,而链表则利于插入和删除,但对某个特定元素的定位却需借助于搜索。而A*算法则需要快速插入和删除所求得的最优值以及可以对当前结点以下结点的操作,因而数组或链表都显得太通用了,用来实现A*算法会使速度有所降低。要实现这些,可以通过二分树、跳转表等数据结构来实现,我采用的是简单而高效的带优先权的堆栈,经实验表明,一个1000个结点的图,插入而且移动一个排序的链表平均需500次比较和2次移动;未排序的链表平均需1000次比较和2次移动;而堆仅需10次比较和10次移动。需要指出的是,当结点数n大于10,000时,堆将不再是正确的选择,但这足已满足我们一般的要求。
还有一种更好的方法是Hot Queues,而且这种方法还可应用于Dijkstra算法以降低其复杂度。当我们移动估价函数值为f的结点时,我们插入值为f+δ(δ<=C)(若δ>=0将意味着估价函数是有效的,反之亦然),常量C为从一个结点到相邻结点的权的最大改变。同时我们用一些“容器”来保存估价函数值的子集(这正如o(n)的排序算法的思想),例如,当有10个“容器”时,堆将平均只包含1/10的估价值。因而这将比用堆更为有效。
4. 估价函数(Heuristic Function)
估价函数的正确选取将直接关系到A*算法的成功与否,而函数的确定却与实际情形有着密切的关系。在本文中,仅对网格状地图的估价函数作部分讨论,而在其它情形中,需要作不同的分析,但至少估价函数应为连续函数。
a. Manhattan Distance
这是一种标准的估价函数,
h(A) = 10 * (abs(A.x-goal.x) + abs(A.y-goal.y))
b. Diagonal Distance
如果在地图上允许作斜线方向的运动,则Mahattan Distance修正为Diagonal Distance
h(A) = max(abs(A.x-goal.x), abs(A.y-goal.y))
5. 估价函数的判优
一般情形下,我们只需对估价函数的值进行比较而取其大者即可,但在几个结点的估价函数值相同的情形下,我们需要采取一定的策略来决定这几者谁更优,从而避免对多个点的搜索。从而如下代码可实现之:
double dx1 = currentX - goalX;