- 物理機リージョン/AZ
- ハイパーバイザVM exit で捕まえる
- VMゲストOS
- コンテナnamespace / cgroup
- プロセスアプリ / DB今ここ
第11期では、取り出す規則(先頭・末尾・優先度)で探索の形が変わることを見ました。今回はその3つ目、「いちばん近いものから取り出す」規則が何を前提にしているかを読みます。
まず結論
Dijkstra 法は「まだ確定していない頂点のうち、始点にいちばん近いものは、もうこれ以上近くならない」と言い切って、その頂点を1つずつ確定していきます。そう言い切れる根拠は、辺の重みが負でないことだけです。
重みが全部1なら、取り出す順は幅優先探索の層の順と同じになります。重みに負の値が1本でも混ざると、その言い切りが崩れます。確定済みを書き換えない実装では、3頂点でも誤った距離が返ります。
辺が S→A(重み2)・S→B(重み5)・B→A(重み −4)の3本だけのグラフで、S から A への最短距離を Dijkstra 法で求めます。実装は「一度確定した頂点は二度と書き換えない」形です。A の距離として何が返るでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
いちばん近い未確定の頂点は、もう縮まない
始点から各頂点までの距離を求めたいとします。最初は始点だけが距離0で、ほかは「まだ分からない(無限大)」です。使う操作は1つです。
緩和(relaxation): 辺 u→v(重み w)について、dist[u] + w が dist[v] より小さければ、dist[v] をその値に書き換える。
緩和は暫定値を下げるだけで、最短かどうかは教えません。そこで Dijkstra 法は毎回、未確定の頂点のうち暫定値がいちばん小さいものを選び、「この値はもう変わらない」と確定します。その頂点から出る辺を緩和し、また次を選びます。
なぜ確定してよいのか。いま選んだ頂点を u とします。u へ別の道で来るには、どこかでほかの未確定の候補 x を通ります。x の暫定値は u 以上で、そこから足す辺はどれも0以上です。だから回り道をしても u の値より小さくなりません。
- ・前提は「辺の重みが0以上」の1つだけ
- ・負の辺があると回り道で短くなり、確定が早すぎたことになる
原論文の手順と、総当たりとの突き合わせ
Dijkstra の1959年の論文(Numerische Mathematik 1, 269–271)は、最短経路の問題(Problem 2)をこう書き出します。
Problem 2. Find the path of minimum total length between two given nodes P and Q. We use the fact that, if R is a node on the minimal path from P to Q, knowledge of the latter implies the knowledge of the minimal path from P to R. In the solution presented, the minimal paths from P to the other nodes are constructed in order of increasing length until Q is reached.
in order of increasing length(近い順)です。集合 A の定義も同じです。
A. the nodes for which the path of minimum length from P is known; nodes will be added to this set in order of increasing minimum path length from node P;
論文の全文に heap・priority・queue の語は1つもありません。書かれているのは集合の出し入れだけで、「いちばん近いもの」をどう探すかは決めていません。
手で確かめます。頂点6・有向辺9、重みが全部正のグラフ(A→B 7, A→C 9, A→F 14, B→C 10, B→D 15, C→D 11, C→F 2, D→E 6, F→E 9)で、A から始まる単純経路15本を全部たどった最小値と、heapq で書いた Dijkstra 法の結果を比べました(Python 3.14.6 / macOS arm64、2026-10-07 実行)。
graph1 V=6 E=9 simple paths enumerated: 15bruteforce: {'A': 0, 'B': 7, 'C': 9, 'D': 20, 'E': 20, 'F': 11}dijkstra(strict=True): {'A': 0, 'B': 7, 'C': 9, 'D': 20, 'E': 20, 'F': 11} settle order=['A', 'B', 'C', 'F', 'D', 'E'] heappops=8 agree=True
一致しました。確定順は A, B, C, F, D, E で、距離は 0, 7, 9, 11, 20, 20 と小さい順に並んでいます。取り出し8回のうち2回は、積み直したあとに残った古い項目で、読み飛ばしています。heapq 公式の「Priority Queue Implementation Notes」も、ヒープの中で値を直さない方法の1つとしてこう挙げています(公式は削除の印、本文は確定済みの集合で読み飛ばす)。
Removing the entry or changing its priority is more difficult because it would break the heap structure invariants. So, a possible solution is to mark the entry as removed and add a new entry with the revised priority:
Dijkstra 法が「未確定の頂点のうち、距離の暫定値がいちばん小さいもの」を確定してよいのはなぜですか。
前提が崩れると、手順は終わったうえで誤る
確定の根拠は「回り道は短くならない」でした。負の辺が1本あると、これがそのまま崩れます。予測の3頂点グラフ(S→A 2, S→B 5, B→A −4)で実測しました(同じ環境)。
bruteforce: {'A': 1, 'B': 5, 'S': 0}dijkstra(strict=True): {'A': 2, 'B': 5, 'S': 0} settle order=['S', 'A', 'B'] heappops=3 agree=Falsedijkstra(strict=False): {'A': 1, 'B': 5, 'S': 0} settle order=['S', 'A', 'B', 'A'] heappops=4 agree=True
strict=True は確定済みの頂点を二度と書き換えない形で、A=2 を返しました。S の次にいちばん近い A(距離2)をその場で確定してしまい、あとで B から来た 5 + (−4) = 1 を受け付けないからです。
落とし穴があります。strict=False は確定後でも短くなれば積み直す形で、heapq で短く書くとこうなりがちです。この形は A を2回取り出し、正しい値 1 に直しました。負の閉路が無い限り、処理し直すことで正しい値に追いつくのです。ただしその時点で「各頂点を1回だけ処理する」という Dijkstra 法の性質はもう失われています。負の辺で崩れることを確かめたいときに、この形で試して「一致したから負の辺も大丈夫」と読むのは誤りです。
重みが全部1の場合と、前提を仕様に書いたプロトコル
① 幅優先探索 — 重みが全部1の Dijkstra 法
第11期の『取り出す順序が、解ける問題を決める』は、幅優先が最短を返すのは辺の重みが等しい場合の性質で、あの1例は証明ではない、と断って止めていました。重みが全部1なら、暫定値は「何本の辺でたどり着いたか」です。いちばん近いものから確定すると、距離0、距離1、距離2……と層の順に確定します。先に入れたものから取り出すキューは、この順を比較なしで作ります。
上のグラフ1の重みを全部1にして、両方を走らせました(同じ環境、本教材で実行)。
BFS order ['A', 'B', 'C', 'F', 'D', 'E'] dist [0, 1, 1, 1, 2, 2]Dijkstra order ['A', 'B', 'C', 'F', 'D', 'E'] dist [0, 1, 1, 1, 2, 2]same dist: True
距離は一致し、確定は層の順(0 → 1 → 2)に進みました。同じ層の中の順番は、同点をどう扱うか次第です。
② OSPF の SPF 計算 — 前提が仕様に書いてある
RFC 2328(OSPF Version 2)は、ルータが持つ最短経路の木をこう定義します(§6)。
Shortest-path tree The shortest-path tree for the area, with this router itself as root. Derived from the collected router-LSAs and network-LSAs by the Dijkstra algorithm (see Section 16.1).
§16.1 は、確定してよい理由まで書いています。
However, the paths to the candidate vertex that is closest to the root are guaranteed to be shortest; this vertex is added to the shortest-path tree, removed from the candidate list, and its adjacent vertices are examined for possible addition to/modification of the candidate list.
そして §9 のインタフェースのコストの定義に、前提そのものがあります。
The cost of an interface must be greater than zero.
コストを正に限ることで、前提をプロトコルが守っています。
RFC 2328(OSPF)の中で、Dijkstra 法の前提が仕様として書かれている箇所はどれですか。
幅優先探索(重みが全部1)
重みが全部1なので、暫定値は「何本でたどり着いたか」になります。近い順はそのまま層の順で、先に入れたものから取り出すキューがその順を作ります。前提はグラフの形で満たされています。
OSPF の SPF 計算(RFC 2328)
コストがリンクごとに違うので、暫定値を比べる必要があります。§16.1 は「closest to the root」の候補が最短だと保証されると書き、§9 は「The cost of an interface must be greater than zero.」と定めます。前提は、プロトコルの仕様が守っています。
幅優先探索と OSPF の経路計算。片方は優先度付きキューを使わず、片方は使います。それでも同じ手法だと言えるのはなぜでしょうか。また、それぞれ「重みが負でない」をどこで保証しているでしょうか。
「次にどの頂点を確定するか」の選び方と、それが正しいと言える理由を、2つに分けて並べてみてください。
この章のまとめ
- 緩和は「辺を使うと短くなるなら暫定値を書き換える」操作。暫定値を下げるだけで、最短だとは言わない。
- Dijkstra 法は、未確定の中でいちばん近い頂点を確定する。辺の重みが0以上なら、回り道で値が下がらないので、確定してよい。
- 頂点6・辺9のグラフで、単純経路15本の総当たりと一致した。
- 負の辺が1本あると崩れる。3頂点で、確定済みを書き換えない型は A=2 を返した(正しくは1)。積み直す型は A を2回処理して直した。
- 重みが全部1なら近い順は層の順になり、幅優先探索と同じ手順になる。
- OSPF は RFC 2328 で「Dijkstra algorithm」を指定し、§9 でコストは0より大きいと前提を仕様に書いている。
- 原論文(1959)に heap・priority・queue の語は無い。もう1つの問題は最小全域木。
- 既存クイズでは、「アルゴリズムとデータ構造」の algorithm-8、「II(応用編)」の algorithm-2-4・algorithm-2-5、「III(発展編)」の algorithm-3-5 がこの章の結論の一行版です。
- CLRS(『アルゴリズムイントロダクション』)の目次や索引で Dijkstra's algorithm(単一始点最短路) を探すと、この回の内容に対応する章や節が見つかります。章の題と番号は版で変わるので、手元の目次で確かめてください。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
heapq で書いた Dijkstra 法で、確定後でも短い道が見つかれば積み直して処理し直す型があります(古い項目は読み飛ばします)。これを S→A 2・S→B 5・B→A −4 のグラフで走らせると、どうなりましたか。
用語集(3語)
- 緩和(relaxation)
- 辺 u→v(重み w)について、「u までの暫定距離 + w」が v の暫定距離より小さければ、v の暫定距離をその値に書き換える操作。最短経路の手順の多くは、これを行う順と回数の違いとして読める。
- 確定(settle)
- その頂点の距離がもう変わらないと決めて、以後は書き換えないこと。Dijkstra 法は暫定値がいちばん小さい未確定の頂点を1つずつ確定する。確定してよい根拠は、辺の重みが負でないこと。
- SPF 計算(OSPF)
- OSPF の各ルータが、集めたリンク状態から自分を根とする最短経路の木を作る計算。RFC 2328 は §16.1 でこれを Dijkstra algorithm として書き、§9 でインタフェースのコストは0より大きくなければならないと定めている。