Transcript Document

コンピュータソフトウェア
9. 最短路(Shortest Path)
田浦
http://www.logos.ic.i.u-tokyo.ac.jp
/~tau/lecture/software/
最短路の問題

2点間を結ぶ最短の道を求める
 道の長さとは:
 単純な場合(重みなしグラフ): 含む辺の数
 複雑な場合(重みつきグラフ): 含む辺についた「重み」
の和
 前者ならば幅優先探索が答え.主な興味は後者
1
3
4
3
2
3
1
3 4
7
わかりやすい応用例(1)



渋滞考慮無しのカーナビ
 頂点: 交差点
 辺 : 2交差点を結ぶ一本道
 辺の重み: その一本道を抜けるのにかかる時間(渋滞無し
 これが時刻によらず一定)
2点間の最短路 = 2つの交差点間を最も早く移動するドライ
ブルート
クイズ: 一方通行,Uターン禁止,右折禁止などをどうモデル
化する?
わかりやすい応用例(2)


インターネットのルーティング
 頂点: ネットワークルータ
 辺: ケーブル(インターネットより下のレイヤ(2)で直接通信
できるルータの対)
 辺の重み: パケットがそこを通ることに対するコスト
 例: バンド幅の関数(バンド幅が大きければ小さい)
2点間の最短路: パケットを配送する適切な道
少し自明でない応用例(1)

時刻表を考慮した路線検索
 頂点 (t, A) : 時刻tに駅Aにいるという「状態」
 辺:
 (t, A)  (t + 1, A) : 1単位時間同じ場所にとどまる
 (t, A)  (u, B) : 時刻tにA駅を発車して時刻uにB駅につ
く列車がある
 辺の重み: かかる時間(前者は1, 後者は u – t)
 (t, A)から(*, B)への最短路: 時刻tにA駅にいる人が最早
でB駅にたどり着く手段を与える
視覚的に「見るからにグラフ」ではないものが,実はし
ばしばグラフとして表現できる
少し自明でない応用例(2)



(1)と同じ流れで「渋滞考慮ありカーナビ」
いろいろな問題のモデル化が考えられるが最も単純には
 「渋滞考慮」=
 時刻によって各道を通るのにかかる時間が異なる
 各道をある時刻に通る際の通過時間は与えられている
(渋滞の発生は先まで予測できている)
 時刻は分刻み,通過時間も分刻みとする
 としてモデル化する
(t, A)  (u, B) : 交差点AとBが一本道でつながっており,時
刻tにAにいた車は時刻uにBにつける
アルゴリズム


Dijkstra (ダイクストラ)法 (必修)
 幅優先探索とほぼ同じ考え方
 指定した一頂点から他の全頂点までの最短路(距離)を求
める (一対全)
Floydのアルゴリズム
 全ての頂点対間の最短路・距離を求める (全体全)
Dijkstra法: 概要




最前線: 訪問済みから1 hop
優先度つきグラフ探索の一種
 頂点uの優先度: これまでに見つ
かっているs * uの最短距離
あとはほぼ「一般的な探索方法」に従う
だけ
入力: 辺重みつき有向グラフG = V, E
とその一頂点s ( V)
 辺の重みは非負
 アルゴリズムは無向でも同様
出力: D[i] (i  V)が,sからiへの最短距
離を与えるような配列D
 最短路を与えるように変更するのは
容易
訪問済み
訪問済みから>1 hop
Dijkstra法: 擬似コードdijkstra(s, G) {
4
8
3
1
6
start
4
2
1
5
goal
Wu,v : 辺u  vの重み(距離)
S = {};
D = new int[n];
for (i = 0; i < n; i++) {
if (i == s) D[i] = 0;
else D[i] = ;
}
while (S  V) {
pick u  V – S that minimizes D;
S = S + { u };
for v  adjacents(u) {
d = D[u] + Wu,v ;
if (d < D[v]) D[v] = d;
}
}
}
Dijkstra法の動き


赤: S (意図: 最短距離確定)
緑:暫定最短距離 (startから赤だけを使う道の中での最短距
離)
1
456
4
8
3
1
6
0
start
4
2
1
34
5
goal
9
8
7
dijkstra(s, G) {
S = {};
D = new int[n];
for (i = 0; i < n; i++) {
if (i == s) D[i] = 0;
else D[i] = ;
 正しさのポイント
}
 ループ不変条件:
while (S  V) {
pick u  V – S that minimizes D;
 頂点i  Sに対し,D[i]が「すべて
S = S + { u };
の道の中での」 sからiへの最短
for v  adjacents(u) {
距離
d = D[u] + Wu,v ;
if (d < D[v]) D[v] = d;
 頂点i  Vに対し,D[i]は,「Sの
}
点のみを経由する道の中での」
}
sからiへの最短距離
}
Dijkstra法の正しさ
S
証明

黒板にて
実現の詳細と計算量




基本
 whileループは常にn (頂点数)回繰
り返す
ポイント
 集合Sの表現とかかわる操作(2行)
単純アプローチ
 S : 配列 S[i] = 1 iff i  S;

: 線形探索 O(n)
 全体の計算量 O(n2)
 密なグラフならこれ以上は難しそう
疎なグラフでより高速な方法は?
 集合から最小の要素を取り除く!
dijkstra(s, G) {
S = {};
D = new int[n];
for (i = 0; i < n; i++) {
if (i == s) D[i] = 0;
else D[i] = ;
}
while (S  V) {
pick u  V – S that minimizes D;
S = S + { u };
for v  adjacents(u) {
d = D[u] + Wu,v ;
if (d < D[v]) D[v] = d;
}
}
}
疎なグラフ用の
Dijkstra法


集合 T = V – S をヒープを用い
た優先度キューで表現
 最小値の除去O(log n)
 new 優先度の変更 O(log n)
合計計算量
 O((n + m) log n)
 たとえば m  O(n)のグラフ
に限れば O(n log n)
 現実の道路・鉄道・ネット
ワークetc.は?
ヒープを用いた優先度キューの簡単な拡張と
して考えてみよ
dijkstra(s, G) {
T = priority_queue(V);
dijkstra(s, G) {
D = new int[n];
S = {};
for (i = 0; i < n; i++) {
D = new int[n];
if (i == s) D[i] = 0;
for (i = 0; i < n; i++) {
else D[i] = ;
if (i == s) D[i] = 0;
}
else D[i] = ;
while (T  {}) {
}
delete u  T that minimizes D;
while (S  V) {
pick u  V – S that minimizes D;
for v  adjacents(u) {
S = S + { u };
d = D[u] + Wu,v ;
for v  adjacents(u) {
if (d < D[v]) {
d = D[u] + Wu,v ;
D[v] = d;
if (d < D[v]) D[v] = d;
change priority of v in T to d;
}
}
}
}
}
}
}
最短路を実際に求
めるには?


配列Dと並行してPを維持
P[i] : D[i]に書かれている(暫
定)最短距離を与える経路上
でのiの先行頂点
 s    P[i]  i
dijkstra(s, G) {
S = {};
D = new int[n];
for (i = 0; i < n; i++) {
if (i == s) D[i] = 0;
else D[i] = ;
}
while (S  V) {
pick u  V – S that minimizes D;
S = S + { u };
for v  adjacents(u) {
d = D[u] + Wu,v ;
if (d < D[v]) { D[v] = d; P[v] = u; }
}
}
}
全対全最短距離




方法1: Dijkstraを各頂点を開始点として行う
 O(n3) または O(n (n + m) log n)
方法2: Floydのアルゴリズム
 O(n3)
入力:辺重みつき有向グラフG = V, E
出力: D[i,j] が iからjへの最短距離を与えるような2
次元配列D
Floydのアルゴリズム



仮定:
 重みの和が負となる閉路 floyd(G) {
D = new int[n][n]; /* 略記 */
がない
for (i = 0; i < n; i++)
 Dijkstraと異なり,負の重
for (j = 0; j < n; j++)
みの辺があってもよい
D[i,j] = Wi,j;
(*)のループの不変条件:
for (k = 0; k < n; k++)
(*)
 Sk = { 0, 1, ..., k – 1 }とし
for (i = 0; i < n; i++)
て,D[i,j]はiからjへの道
for (j = 0; j < n; j++)
で,i,jを除いてはSの頂点
D[i,j] = min(D[i,j], D[i,k] + D[k,j]);
であるようなものの中で
}
の最短距離
証明は練習問題
1-1最短距離?


Dijkstra法を使って全頂点までの距離をまず求め,目的地ま
での最短距離を取り出す
1-1だけを(1-全よりも)高速に計算する方法は知られていない