記事のディレクトリ
ベルマンフォードアルゴリズムは、重み付きグラフで1つの頂点から他のすべての頂点への最短経路を見つけるのに役立ちます。
ダイクストラのアルゴリズムに似ていますが、エッジに負の重みがあるグラフに使用できます。
実生活で負の重みを持つエッジがあるのはなぜですか?
負のウェイトエッジは、最初は役に立たないように見えるかもしれませんが、キャッシュフロー、化学反応で放出/吸収される熱など、多くの現象を説明できます。
たとえば、ある化学物質Aから別の化学物質Bへの方法が異なる場合、それぞれの方法には熱放散と熱吸収の副作用があります。
最小限のエネルギーを必要とする一連の反応を見つけたい場合は、吸熱を負の重みとして使用し、熱放散を正の重みとして使用する必要があります。
なぜ負の重みに注意を払う必要があるのですか?
負の重みエッジは、負の重みサイクル、つまり、同じポイントに戻ることによって合計パス距離を短縮するサイクルを作成できます。
最短パスを見つけようとすると、負の重みのループが間違った結果をもたらす可能性があります。
ダイクストラのアルゴリズムなどの最短パスのアルゴリズムは、負の重みを使用できるため、そのようなループを検出できず、誤った結果をもたらす可能性があります。ループしてパスの長さを短くします。
ベルマンフォードアルゴリズムのしくみ
ベルマンフォードアルゴリズムの動作原理は、開始頂点から他のすべての頂点までのパス長を過大評価することです。次に、新しい短いパスを見つけることにより、以前の見積もりを徐々に調整します。
すべての頂点に対してこの操作を繰り返すことにより、結果が最適化されることを保証できます。
最初のステップは、ウェイトマップから始めることです。
2番目のステップは、頂点を選択し、他の頂点のパス値を無限大に割り当てることです。
3番目のステップは、各ノードにアクセスし、パス距離が正しくない場合は修正します。
最悪の場合、頂点の光路長を5回再調整する必要があるため、4番目のステップでは5回実行する必要があります。
5番目のステップでは、右上隅の固定点がパスの長さをどのように調整するかに注意してください。
6番目のステップでは、すべての頂点にパス長がある場合、負のサイクルがあるかどうかを確認します。
ベルマンフォード擬似コード
各頂点のパス距離を維持する必要があります。サイズvの配列に格納できます。ここで、vは頂点の数です。
また、最短経路の長さだけでなく、最短経路を取得できるようにしたいのです。これを行うには、各頂点を、パスの長さが最後に更新された頂点にマップします。
アルゴリズムが終了したら、ターゲット頂点からソース頂点までさかのぼってパスを見つけることができます。
function bellmanFord(G, S)
for each vertex V in G
distance[V] <- infinite
previous[V] <- NULL
distance[S] <- 0
for each vertex V in G
for each edge (U,V) in G
tempDistance <- distance[U] + edge_weight(U, V)
if tempDistance < distance[V]
distance[V] <- tempDistance
previous[V] <- U
for each edge (U,V) in G
If distance[U] + edge_weight(U, V) < distance[V}
Error: Negative Cycle Exists
return distance[], previous[]
ベルマンフォードvsダイクストラ
ベルマンフォードアルゴリズムとダイクストラアルゴリズムは、構造が非常に似ています。ダイクストラは頂点の直接の隣接点のみを調べますが、ベルマンはすべての反復ですべてのエッジをトラバースします。
Cの例
// Bellman Ford Algorithm in C
#include <stdio.h>
#include <stdlib.h>
#define INFINITY 99999
//struct for the edges of the graph
struct Edge {
int u; //start vertex of the edge
int v; //end vertex of the edge
int w; //weight of the edge (u,v)
};
//Graph - it consists of edges
struct Graph {
int V; //total number of vertices in the graph
int E; //total number of edges in the graph
struct Edge *edge; //array of edges
};
void bellmanford(struct Graph *g, int source);
void display(int arr[], int size);
int main(void) {
//create graph
struct Graph *g = (struct Graph *)malloc(sizeof(struct Graph));
g->V = 4; //total vertices
g->E = 5; //total edges
//array of edges for graph
g->edge = (struct Edge *)malloc(g->E * sizeof(struct Edge));
//------- adding the edges of the graph
/*
edge(u, v)
where u = start vertex of the edge (u,v)
v = end vertex of the edge (u,v)
w is the weight of the edge (u,v)
*/
//edge 0 --> 1
g->edge[0].u = 0;
g->edge[0].v = 1;
g->edge[0].w = 5;
//edge 0 --> 2
g->edge[1].u = 0;
g->edge[1].v = 2;
g->edge[1].w = 4;
//edge 1 --> 3
g->edge[2].u = 1;
g->edge[2].v = 3;
g->edge[2].w = 3;
//edge 2 --> 1
g->edge[3].u = 2;
g->edge[3].v = 1;
g->edge[3].w = 6;
//edge 3 --> 2
g->edge[4].u = 3;
g->edge[4].v = 2;
g->edge[4].w = 2;
bellmanford(g, 0); //0 is the source vertex
return 0;
}
void bellmanford(struct Graph *g, int source) {
//variables
int i, j, u, v, w;
//total vertex in the graph g
int tV = g->V;
//total edge in the graph g
int tE = g->E;
//distance array
//size equal to the number of vertices of the graph g
int d[tV];
//predecessor array
//size equal to the number of vertices of the graph g
int p[tV];
//step 1: fill the distance array and predecessor array
for (i = 0; i < tV; i++) {
d[i] = INFINITY;
p[i] = 0;
}
//mark the source vertex
d[source] = 0;
//step 2: relax edges |V| - 1 times
for (i = 1; i <= tV - 1; i++) {
for (j = 0; j < tE; j++) {
//get the edge data
u = g->edge[j].u;
v = g->edge[j].v;
w = g->edge[j].w;
if (d[u] != INFINITY && d[v] > d[u] + w) {
d[v] = d[u] + w;
p[v] = u;
}
}
}
//step 3: detect negative cycle
//if value changes then we have a negative cycle in the graph
//and we cannot find the shortest distances
for (i = 0; i < tE; i++) {
u = g->edge[i].u;
v = g->edge[i].v;
w = g->edge[i].w;
if (d[u] != INFINITY && d[v] > d[u] + w) {
printf("Negative weight cycle detected!\n");
return;
}
}
//No negative weight cycle found!
//print the distance and predecessor array
printf("Distance array: ");
display(d, tV);
printf("Predecessor array: ");
display(p, tV);
}
void display(int arr[], int size) {
int i;
for (i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
ベルマンフォードのアルゴリズムの複雑さ
時間の複雑さ
ベストケースの複雑さ | O(E) |
---|---|
平均的なケースの複雑さ | O(AND) |
最悪の場合の複雑さ | O(AND) |
スペースの複雑さ
スペースの複雑さはO(V)です。
ベルマンフォードアルゴリズムアプリケーション
- ルーティングアルゴリズムの最短パスを計算するために使用されます
- 最短経路を見つける
参照文書
[1]パレワラボPvt。Ltd.Bellman Ford's Algorithm [EB / OL] .https://www.programiz.com/dsa/bellman-ford-algorithm,2020-01-01。