Menu
Coddy logo textTech

ベルマン・フォード法

最終更新

ベルマン・フォード法は始点ノードから他のすべてのノードへの最短経路を求め、Dijkstra とは異なり一部の辺の重みが負であっても機能します。総当たりの緩和によって動作します。すべての辺を繰り返し走査し、その辺を通ると距離が短くなる場合、対象ノードの距離を更新します。上の再生を押すと、暫定距離がパスごとに下がっていく様子が見られます。

すべての辺に対する V - 1 回のパスの後、(辺は最大でも V - 1 本しか持てない)各最短経路が求まっています。これにより計算量は O(V · E) となり、Dijkstra より遅いものの、負の重みを扱え、負の重みの閉路を検出できます(V 回目のパスでまだ辺が緩和されるなら、最短経路は存在しません)。

時間計算量と空間計算量

指標計算量備考
時間O(V · E)すべての E 辺に対する V − 1 回のパス
空間O(V)ノードごとに 1 つの距離
負の重み対応Dijkstra に対する主な利点
負の閉路検出可能緩和が起きる V 回目のパスが 1 つを示す

ステップごとの流れ

ステップ何が起きるか
1始点の距離を 0 に、他はすべて無限大に設定する。
2次のステップを V − 1 回繰り返す。
3各辺 (u → v, w) について、dist[u] + w < dist[v] かどうか確認する。
4その場合は緩和する: dist[v] = dist[u] + w とする。
51 回の完全なパスで何も変わらなければ早期に停止する。

具体例

4 ノード S, A, B, C 上で始点 S、辺 S→A (4)S→B (5)A→C (3)B→A (-3) をこの順に緩和します。距離は S=0、それ以外はすべて から始まります:

パス距離 [S, A, B, C]動作
Init[0, ∞, ∞, ∞]始点の距離は 0、他はすべて無限大。
1[0, 2, 5, 7]S→A を 4、S→B を 5、A→C を 7 に緩和し、次に B→A が A を 5 + (-3) = 2 に下げる。
2[0, 2, 5, 5]A→C が今度は C を 2 + 3 = 5 に改善する。他の辺は緩和されない。
3[0, 2, 5, 5]どの辺も緩和されないので距離は確定: A は S→B→A 経由でコスト 2。

ベルマン・フォード法を使うべき場面

使うべきとき避けるべきとき
辺の重みが負になりうる。すべての重みが非負である(Dijkstra の方が速い)。
負の重みの閉路を検出する必要がある。グラフが巨大で密である - O(V · E) は遅すぎる。
グラフが小さいか疎である。大きなグラフで始点-終点の単一クエリだけが必要。
シンプルで実装しやすいベースラインが必要。重みが非負で、レイテンシが重要。

Bellman-Ford Algorithmのコード

Python, JavaScript, Java, C++, Cによるクリーンで実行可能なBellman-Ford Algorithmの実装です。言語を選んでコードをコピーするか、Coddyプレイグラウンドに読み込んだ状態で開けます。

PythonでのBellman-Ford Algorithmのコード

Python
1def bellman_ford(vertices, edges, start):2    dist = {v: float("inf") for v in vertices}3    dist[start] = 04    # Relax every edge V-1 times5    for _ in range(len(vertices) - 1):6        for u, v, w in edges:7            if dist[u] + w < dist[v]:8                dist[v] = dist[u] + w9    # One more pass: any improvement means a negative cycle10    for u, v, w in edges:11        if dist[u] + w < dist[v]:12            raise ValueError("Graph contains a negative-weight cycle")13    return dist14
15
16vertices = ["A", "B", "C", "D", "E"]17edges = [18    ("A", "B", 6), ("A", "C", 7), ("B", "D", 5), ("B", "E", -4),19    ("C", "D", -3), ("D", "B", -2), ("E", "D", 7),20]21
22for node, d in bellman_ford(vertices, edges, "A").items():23    print(f"A -> {node}: {d}")
このコードをPythonプレイグラウンドで実行

ベルマン・フォード法のFAQ

ベルマン・フォード法の時間計算量は?
ベルマン・フォード法は O(V · E) 時間で動作します。V - 1 回のパスを行い、各パスですべての E 辺を緩和します。これは Dijkstra の O((V + E) log V) より遅く、負の辺の重みに対応するための代償です。
Dijkstra ではなくベルマン・フォード法を使うべきなのはいつ?
グラフが負の辺の重みを持ちうる場合、または負の重みの閉路を検出する必要がある場合にベルマン・フォード法を使います。Dijkstra は速いですが、非負の重みでのみ正しく動作します。よくある実用例は、負の閉路が重要となる為替裁定の検出です。
ベルマン・フォード法はどのように負の閉路を検出するのか?
V - 1 回の緩和パスの後、負の閉路が存在しなければすべての最短経路が確定します。さらにもう 1 回のパスでまだ辺を緩和できる場合、到達可能な負の重みの閉路が存在し、最短経路は未定義になります(コストを下げるために無限にループできてしまいます)。
ベルマン・フォード法とワーシャル・フロイド法の違いは?
ベルマン・フォード法は単一の始点からの最短経路を O(V · E) で計算し、ワーシャル・フロイド法はすべての対の最短経路を O(V³) で計算します。密なグラフでは、各始点からベルマン・フォード法を実行するとコストは O(V² · E) になるため、すべての対が必要なときは通常ワーシャル・フロイド法の方が良い選択です。どちらも負の辺を扱え、負の閉路を通知できます。
ベルマン・フォード法はなぜちょうど V - 1 回のパスが必要なのか?
負の閉路のないグラフでのどの最短経路も最大 V - 1 本の辺しか使いません。V を超えるノードを訪れる経路はいずれかを繰り返すからです。各パスは各最短経路の少なくとも 1 本の辺が正しく緩和されることを保証するので、V - 1 回のパスですべてを確定できます。よくある誤解は、パスを増やすと結果が改善するというものですが、負の閉路が存在しない限り決して改善しません。
ベルマン・フォード法は早期に停止できるか?
はい。すべての辺に対する完全なパスで何も緩和されなければ、すべての距離はすでに最適であり、V - 1 回のパスに達する前に停止できます。この早期終了の最適化は、早く収束するグラフでしばしば大幅に高速化しますが、最悪計算量は O(V · E) のままです。
Coddy programming languages illustration

Coddy でアルゴリズムをマスターしよう

始める