这里有一个描述算法并解释松弛概念的好文章。
“松弛”这个概念来源于将最短路径的估计值与弹簧的长度进行类比。弹簧一般不用于压缩,因此最短路径的代价最开始是一个高估值,就像拉开的弹簧一样。随着发现更短的路径,估价会逐渐降低,弹簧会逐渐松弛。当最短路径被找到时,如果存在的话,弹簧已经松弛到其原始长度。
Dijkstra算法中的松弛过程指的是如果通过顶点v的路径可以改进与v相连的所有顶点的代价,那么就更新这些顶点的代价。
放松一条边(这个概念也可以在其他最短路径算法中找到)是尝试通过使用另一个顶点来降低到达一个顶点的代价。
您正在计算从起始顶点(比如S)到所有其他顶点的距离。在某些时候,您会有中间结果——当前估计值。放松是一个过程,在这个过程中,您检查某些顶点u和v:
if directly_connected(v, u)
if est(S, v) > est(S, u) + dist(u,v)
est(S, v) = est(S, u) + dist(u, v)
其中est(S,a)
表示当前的距离估计值,dist(a,b)
表示在图中相邻的两个顶点a和b之间的距离。
在松弛过程中,您要检查的基本上是从a到b的当前估计是否可以通过“改变路径”通过c来改进(这种“改变路径”的长度将是从a到c,再从c到b的路径长度)。
在最短路径算法中,高成本告诉算法“不要走那条路”。因此,默认情况下,我们只知道起始节点是最短路径的一部分,而所有其他节点仍未确定。因此,我们将起始节点(例如S)的成本设为零,将每个其他节点(例如A,E)的成本设为无穷大。
S := new Node()
A := new Node()
E := new Node()
// low cost
S.edge_cost = 0
// high cost
A.edge_cost = 1000 // "infinity"
E.edge_cost = 1000 // "infinity"
高成本只是暂时的,现在我们需要“放松”降低成本。为此,我们使用边缘成本(例如a、b和c),并在节点较低时更新它们。
// Check if S node can be reduced
// SS := S.edge_cost + S.edge_cost // 0 + 0 = 0
// if (S.edge_cost > SS) { // 0 > 0 => false
// S.edge_cost = SS
// }
// Check if A node can be reduced
SA := S.edge_cost + a // 0 + 3 = 3
if (A.edge_cost > SA) { // 1000 > 3 => true
A.edge_cost = SA // 3
}
// Check if E node can be reduced
AE := A.edge_cost + b // 3 + 5 = 8
if (E.edge_cost > AE) { // 1000 > 8 => true
E.edge_cost = AE // 8
}
// Note: The nodes hold memory,
// so the `edge_cost` remembers the previous cost,
// and that's what the edge is compared against
SE := S.edge_cost + c // 0 + 1 = 1
if (E.edge_cost > SE) { // 8 > 1 => true
E.edge_cost = SE // 1
}
看一下https://www.youtube.com/watch?v=2E7MmKv0Y24&t=1336s 时间:28:30。有趣的是最短路径却处于紧张状态,这对我来说正好相反于“放松”。嗯,算了。
边缘松弛。
放松从 v -> w 的边缘意味着测试从 s 到 w 的最佳已知方式是否是从 s 到 v,然后取 v 到 w 的边缘,如果是,则更新我们的数据结构。
Java 代码:
private void relax(DirectedEdge e) {
int v = e.from(), w = e.to;
if (distTo[w] > distTo[v] + e.weight()) {
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
}
}
还有顶点松弛。这意味着放松从给定顶点指向的所有边。
private void relax(EdgeWeightedGraph G, int v) {
for (DirectedEdge e : G.adj(v)) {
relax(e);
}
}