ダイクストラ法よりも高速!66年ぶりの快挙を成し遂げたアルゴリズムについて(1/2)
はじめに
Polaris.AIでお手伝いをさせていただいているSuiginと申します。筆者はアルゴリズムに興味があり、最近最短経路問題について面白い論文が出たので、そちらの解説をさせていただければなと思います。
すこしAIからは離れた内容ですが、お付き合いいただければ幸いです。
この記事の目標
この記事で扱わせていただく論文は以下になります。
R. Duan, J. Mao, X. Mao, X. Shu and L. Yin. "Breaking the Sorting Barrier for Directed Single-Source Shortest Paths". Proceedings of the 57th Annual ACM Symposium on Theory of Computing (STOC), pages 36-44, 2025
リンク(Arxiv): https://arxiv.org/abs/2504.17033
著者による英語の解説はYoutubeにあります。https://youtu.be/LzvvcadKbd0?si=1w6oXRNH1tX4QSd-
また日本語による解説記事も見られるのですが、ここでは論文通りではなく、ダイクストラ法から出発して、どのように改良することができるのかを解説できればなと思います。
記事の流れとしては以下のようになります。
1(今回). 最短経路問題: ここで今までの結果や論文の問題設定を確認します。
2(今回). ダイクストラ法: 記号の導入をしながら、ダイクストラ法を解説します。
3(今回). 分割統治法の適用: 論文の核心のアイデア1つめです。
4(次回). 高速化: 論文の核心のアイデア2つめであるPivot Pruningを紹介します。
5(次回). ダイクストラ法との比較: 論文のタイトルにもなっている"Breaking the Sorting Barrier"がどういう意味かをダイクストラ法と比較しながら解説します。
おまけですが、Suiginによる論文のアルゴリズムの実装は以下にあります。一部ちゃんと実装していない部分もありますが参考にしていただければ光栄です。
[1]
1. 最短経路問題グラフ [2]と開始頂点sが与えられたとき、各頂点gについてsからgへの経路のうち重み最小のものを求める問題です。
例えば上のグラフの場合、sからgへ向かう最短経路(=経路のうち重み最小のもの)は重み4,2,4の辺をたどっていくもので、その重みは4+2+4=10になります。g以外の他の頂点についても最短経路の重みを求める必要があることに注意してください。
辺の重みは負である可能性もありますが、以降は全ての辺の重みは非負であるとします。
最短経路の重みが分かれば、実はそこから最短経路自体を求めることも簡単にできるので、例えばカーナビの経路探索や、物流最適化などに応用されることが多いです。
与えられたグラフの頂点の数を
- 辺の重みが非負整数の場合(Throup, 2004):
(O(m+n \log \log \min \{n, C\}) は辺の重みの最大値)C - グラフが双方向[4]の場合(Duan, Mao, Shu, and Yin, 2023):
O(m \sqrt{\log n \log \log n})
今回紹介するアルゴリズムは、そのような制約がない場合でも、最短経路問題を
2. ダイクストラ法
各頂点
さきほど出てきたグラフと同じものですが、
データ構造を
まず
これを繰り返します。次は
次にbを見ることになり、無事頂点dも最短距離に更新されました。
以降は以下のようになります。
これによって最短距離が求まりました。計算量
- 先頭の要素から辺を順番に見ていくパート: アルゴリズムを通して
O(m) -
の削除/挿入/更新: 平衡二分探索木にすれば1回につき\mathcal{H} 。アルゴリズムを通してO(\log n) O(n \log n)
となり合計
ここで平衡二分探索木とはデータを二分木状に管理するデータ構造です。要素が木の左から右へ大きくなるように配置されており、また木の高さ(深さ)が高々
3. 分割統治法の適用
ではどのようにすればここから高速化できるでしょうか。
ここでデータ構造から頂点を取り出す部分を1つずつではなく、ある決まった数を一気に取り出すことを考えます。ここでの決まった数を
- 1個の要素の挿入/更新
- M個の先頭の要素の取り出し
になります。これは以下のように
実際高さが先ほどの探索木だと3だったのが、いまだと2になっていることが分かると思います。
ブロックが
アルゴリズムも
そのために以下のような条件を満たす関数を考えて、それを分割統治法により実装します。
BMSSP (Bounded Multi-Source Shortest Path)
グラフの頂点数を
入力(
|S| \leq 2^{lt} - 全ての頂点
についてv\in S B > \hat{d}(v) -
を満たす頂点へのv \in S, \hat{d}(v) > d(v) からの最短経路[6]はs を満たす頂点を通る。u \in S, \hat{d}(u) = d(u)
出力(
- 全ての
についてu \in U \hat{d}(u)=d(u) -
は以下の2つを満たす頂点U を全て含む。v \hat{d}(v)<B' - sからvへの最短経路は
のいずれかの頂点を通る。S
- いずれかが成り立つ。
-
でありかつ|U|=\Theta (k2^{lt}) B'<B -
でありかつB'=B である頂点についてd(v)<B'
-
(BMSSP定義終わり)
BMSSPの実装する際に説明しますが、
出力について簡単に言えば
また最短経路問題を解くには
BMSSPの実装
先ほどの通り、データ構造
- 1個の要素の挿入/更新(
insert
):O(\log (N/M)) - M個の先頭の要素の取り出し(
pull
):O(M)
でできるものが存在します。BMSSPは以下のように
BMSSP(l, B, S):
M=2^{(l-1)t}
(Dをブロック1つあたりM個で初期化)
for(v in S): # Sに含まれる各頂点vについて
D.insert((v, d^(v)))
U={}
i=0; B0'=(Sに含まれる頂点vのうちd^(v)の最小値);
while(|U|<k2^{lt} && !D.empty()):
i++
Si=D.pull()
Bi=(Dの先頭の頂点の重み。もしDが空なら∞とする)
Bi', Ui = BMSSP(l-1,Bi,Si)
(UにUiの要素を追加)
for((u,v) s.t. u in Ui) { #始点をUiとする辺について
if(d^(v)>=d^(u)+w(u,v)):
d^(v)=d^(u)+w(u,v)
if(d^(v)>=Bi'):
D.insert(v)
return min(B,Bi), U
アルゴリズムの挙動をイメージしてみましょう。まず
ここから
ここから
次のループで
ここからまた
BMSSPの計算量
これは残念ながらダイクストラより速くなっておりません。
今回のまとめ
ここまでお付き合いしていただいたのにも関わらず、計算量が改善される所が見せられず申し訳ございません。
ダイクストラから出発して、分割統治法によるアルゴリズムで、データ構造の操作の計算量は削減できましたが、結局全体としてはダイクストラの計算量とまだ同程度です。
ただここから2つ工夫をすると計算量を落とすことができるので、それは機会があれば次回に解説をしたいと思います。そのうち1つが論文の核のアイデアであるPivot Pruningです。
-
厳密には単一始点最短経路問題と呼ばれます。 ↩︎
-
初出のグラフ/計算量理論に関する用語については、このように太字で表記します。詳しくない方には申し訳ございませんが、大まかに意味が通じるように書く努力をします。 ↩︎
-
どのくらい演算を必要かの目安です。
なら辺と頂点の数に比例する数の演算をする必要があります。 ↩︎O(n+m) -
ある辺があったとき、反対向きにも同じ重みの辺がある場合。無向グラフとも呼ばれます。対してそのような制約がない場合有向グラフと言います。今回扱うのは有向グラフです。 ↩︎
-
正しくはならし計算量です。 ↩︎
-
複数ある可能性がありますが、微小な重みを辺に加えることによって、すべての経路が異なる重みをもつと仮定してもよくなります。論文中のPreliminaryに解説があります。 ↩︎
-
論文中のAlgorithm 3の簡易バージョンです。 ↩︎
-
一般のグラフの場合、ここは正しくないですが、実は考えるべきグラフは出次数高々2以下として良いです。論文中のPreliminaryに解説があります。 ↩︎
Discussion