Bellman–Ford algorithm 是由 Richard Bellman 和 Lester Ford 創立的,求解單源最短路徑問題的一種演算法. 常見的最短路徑問題演算法還有 Dijkstra's algorithm, 且 Dijkstra 演算法不允許路徑的 cost 是負值, 但此演算法不受此限制. 但是如果圖形中有包含 cycle, 且 cycle 上面的 cost 的合為負值, 則此演算法不適合用於此種圖形.
Description:
在此演算法中, 最外面的 loop 是 path 的長度, 也就是理想下最多走 |V|-1 步應該可以到達終點 (不考慮有 loop 的情況下). 接著在外層 loop 裡再 loop 整個 Graph 的 edge(u,v), 並使用distance[destination vertex] 矩陣紀錄從 source vertex 到 destination vertex 走 n-1 步能到的最短的路徑 (目前要計算第 n 步能走到的最短距離). 並用下面的式子決定要不要更新矩陣distance[destination vertex]:
- for i from 1 to size(vertices)-1:
- for each edge (u, v) with weight w in edges:
- if distance[u] + w < distance[v]:
- distance[v] := distance[u] + w
- predecessor[v] := u
Bellman–Ford algorithm:
整個演算法的 pseudo code 如下:
- procedure BellmanFord(list vertices, list edges, vertex source)
- // This implementation takes in a graph, represented as lists of vertices and edges,
- // and fills two arrays (distance and predecessor) with shortest-path information
- // Step 1: initialize graph
- for each vertex v in vertices:
- if v is source then distance[v] := 0
- else distance[v] := infinity
- predecessor[v] := null
- // Step 2: relax edges repeatedly
- for i from 1 to size(vertices)-1:
- for each edge (u, v) with weight w in edges:
- if distance[u] + w < distance[v]:
- distance[v] := distance[u] + w
- predecessor[v] := u
- // Step 3: check for negative-weight cycles
- for each edge (u, v) with weight w in edges:
- if distance[u] + w < distance[v]:
- error "Graph contains a negative-weight cycle"
Example:
下面圖形共有 7 個 vertex, 說明最外面的 loop 只需要跑 7-1=6 次, 接下來我們要示範從 vertex 0 使用 Bellman-Ford 演算法找出到各個其他 vertex 的最短距離:
Loop1: 從 vertex 0 走一步能到達的只有 vertex 1,2,3:
Loop2: 從上一次結果發現只有 vertex 1,2,3 的 distance 矩陣的值不為 infinity, 故檢查這些 vertex 的 out edge:
Loop3: 從上一次結果發現, 有 vertex 1,2,3,4,5 的 distance 矩陣的值不為 infinity, 故檢查這些 vertex 的 out edge:
Loop4: 從上一次結果發現, 所有 vertex 的 distance 矩陣的值都不為 infinity:
Loop5-6: 在 loop5~6 沒有找到從 vertex 0 到其他 vertex 更短的路徑. 6=|V|-1, 故結束此演算法:
Implement:
- map.alg.SPP.java
- public static HashMap
> Bellman_Ford(Graph g, Vertex s) - {
- return Bellman_Ford(g, s, g.vtxMap.size()-1);
- }
- public static HashMap
> Bellman_Ford(Graph g, Vertex s, int iter) - {
- HashMap
> edgeMap = new HashMap >(); - // Step 1: initialize graph
- for(Vertex v:g.vtxMap.values())
- {
- if(v.equals(s)) {
- edgeMap.put(v.name, null); // Source vertex doesn't have to calculate SP.
- }
- else edgeMap.put(v.name, new ArrayList
()); - }
- // Step 2: relax edges repeatedly
- Set
availVtxSet = new HashSet (); - List
addEdgeList = new ArrayList (); - availVtxSet.add(s);
- for(int i=1; i<=iter; i++) // Path count 1~(n-1)
- {
- System.out.printf("\t[Info] Iter%d:\n", i);
- for(Vertex t:g.vtxMap.values()) // Pickup destination vertex
- {
- Edge te = null;
- Vertex mv = null;
- List
edgeList = edgeMap.get(t.name); - if(edgeList==null) {
- //Skip destination vertex = source vertex.
- continue;
- }
- int c = BellmanLC(edgeList);
- for(Vertex v:availVtxSet)
- {
- int vc = BellmanLC(edgeMap.get(v.name));
- if(vc==INFINITE) continue;
- for(Edge e:v.edgeSet)
- {
- if(e.to.equals(t))
- {
- if(vc+e.cost
- {
- te = e;
- c = vc + e.cost;
- mv = v;
- }
- break;
- }
- }
- }
- if(te!=null) {
- edgeList.clear();
- List
middleList = edgeMap.get(mv.name); - if(middleList!=null) edgeList.addAll(middleList);
- edgeList.add(te);
- addEdgeList.add(te.to);
- System.out.printf("\tAdd edge=%s: %d\n", te, BellmanLC(edgeList));
- }
- }
- availVtxSet.addAll(addEdgeList);
- addEdgeList.clear();
- }
- // Step 3: check for negative-weight cycles
- for(Vertex v:g.vtxMap.values())
- {
- for(Edge e:v.edgeSet)
- {
- int su = BellmanLC(edgeMap.get(e.from.name));
- int tu = BellmanLC(edgeMap.get(e.to.name));
- if(su+e.cost
- System.err.printf("\t[Warn] Not proper graph for Bellman-Ford!\n");
- System.out.printf("\t[Warn] Edge=%s (%d-%d->%d)\n", e, su, e.cost, tu);
- return null;
- }
- }
- }
- return edgeMap;
- }
完整代碼可以在 這裡 下載!
Spplement:
* Single Source Shortest Paths: Label Correcting Algorithm(Bellman-Ford Algorithm)
* Graph Theory Applet
* [CSIE 1212] Data Structure and Algorithm - 4/30 Graph3
沒有留言:
張貼留言