アルゴリズムとデータ構造
13.「グラフアルゴリズ
ム II 」
2011 年 4 月 27 日(水)
服部 健太
2011/4/27 アルゴリズムとデータ構造 13 2
概要
 重み付きグラフについて説明する
 最短経路問題とそれを解くアルゴリズムであ
る Dijkstra 法, Floyd 法について説明する
 最小全域木問題とそれを解くアルゴリズムで
ある Prim 法, Kruskal 法について説明する
2011/4/27 アルゴリズムとデータ構造 13 3
重み付きグラフ
 辺に重み,もしくはコストが割り当てられたグラフ
 例:
 鉄道路線図において,路線を辺としたときの運賃
 都市間の道路地図において,辺を各都市を結ぶ道路とした
とき,移動にかかる時間
 検討課題:他にはどのような例が考えられるか ?
A 市
B 市
F 市
D 市
E 市
G 市
60 分
10 分
40 分
25 分
45 分
8 分
2011/4/27 アルゴリズムとデータ構造 13 4
最短経路問題
 重み付きグラフ上で与えられた 2 頂点 u, v を結ぶパ
スの中で,それに沿った辺の重みの総和が最小とな
るものを求める問題
 以下のバリエーションを考える
 出発点を 1 つに固定して,そこから他のすべての頂点への
最短経路を求める問題
 与えられた2頂点間の最短経路を求める問題もこれを利用す
る
 すべての 2 頂点の組み合わせに対して,最短路を求める問
題
 ここでは,重み付き有向グラフを対象とする
2011/4/27 アルゴリズムとデータ構造 13 5
全ての重みが1の最短経路問題
 全ての辺の重みが 1 の場合,最短経路問題は
,頂点 u と v を最も少ない個数の辺で結ぶパ
スを見つけることになる.
 この場合,幅優先探索によって解ける
 確認問題:上記を確認せよ
2011/4/27 アルゴリズムとデータ構造 13 6
単一出発点の問題
 Dijkstra のアルゴリズム
 考え方:
 出発点から頂点 v への最短経路について, v の 1 つ手前の頂点までの経路は
,その頂点までの最短経路となっている.
 戦略:
 各頂点への最短経路を出発点に近いところから一つずつ確定していく
 最短経路が確定した頂点の中から,次に近い頂点を探して,確定する
 確定した頂点から出ている辺の次の頂点の距離が,より短かくなれば更新す
る.
出発点
u
v
t
最短経路
最短経路
距離 =100
距離 =102
10
5
距離 =107
2011/4/27 アルゴリズムとデータ構造 13 7
Dijkstra 法の動作
a
b
d
c
e
10
1
5
2 3 4 6
2
9
7
0
∞ ∞
∞ ∞
a
b
d
c
e
10
1
5
2 3 4 6
2
9
7
0
10 ∞
5 ∞
a
b
d
c
e
10
1
5
2 3 4 6
2
9
7
0
8 14
5 7
a
b
d
c
e
10
1
5
2 3 4 6
2
9
7
0
8 13
5 7
a
b
d
c
e
10
1
5
2 3 4 6
2
9
7
0
8 9
5 7
a
b
d
c
e
10
1
5
2 3 4 6
2
9
7
0
8 9
5 7
2011/4/27 アルゴリズムとデータ構造 13 8
Dijkstra 法の Python 風コード
def dijkstra(G, s):
V = set() # 空集合
U = list(range(len(G.vertices)))
initialize_single_source(G, s)
while len(U) > 0:
p = remove_mindist(U) # の中で最小の dist を持つ頂点を取
り出す
V.add(p)
for e in G.edges[p]: # p から出ているそれぞれの辺 :
q = e.dest
if q in U:
G.vertices[q].dist = min(G.vertices[q].dist,
G.vertices[p].dist + e.weight)
def initialize_single_source(G, s):
for i in range(len(G.vertices)):
G.vertices[i].dist = 999999 # ∞ の代用
G.vertices[s].dist = 0
最短経路を求める場合
,ここで,直前の頂点
を記録しておけば良い
2011/4/27 アルゴリズムとデータ構造 13 9
Dijkstra 法の正しさの証明
 U 内の各頂点には, V に属する頂点だけを経由してその頂点に
達する最短のパスの長さ distance が記録されている
 アルゴリズムの擬似コードから明らか
 U の中から最小の distance を持つ頂点 p を選んだときに, p ま
での最短経路が, V に属する頂点しか経由しないことを証明す
る
 背理法による:
 もし, V に属さない点を経由して p に至るパスがあり, V から直接
p に達するパスよりも短いとする.このとき,このパス上で V の外
にある最初の頂点を q とする
 頂点 q から p までのパスの長さは 0 以上なので,出発点から q に行
くパスの長さの方が,出発点から p に行くまでのパスの長さよりも
短くなくてはならない.これは,最小の distance を持つ頂点 p を選
んだ,とする仮定と矛盾する
 注意:
 Dijkstra 法が正しく動くためには,辺の重みが負ではないことが
条件.
2011/4/27 アルゴリズムとデータ構造 13 10
Dijkstra 法の正しさの証明(2)
出発点
p
qV
U
2011/4/27 アルゴリズムとデータ構造 13 11
全頂点間の問題
 方法1:
 Dijkstra アルゴリズムを各頂点に 1 回ずつ適用する
 方法2: Floyd のアルゴリズム
 入力:各辺の重みを表す 2 次元配列 W
def floyd(W):
A = copy(W) # 2 次元配列 W をコピー
for k in range(len(A)):
for i in range(len(A)):
for j in range(len(A)):
if A[i][k] + A[k][j] < A[i][j]:
A[i][j] = A[i][k] + A[k][j]





→∞
=
=
)(
)(
)(0
],[
の場合みが辺が存在して,その重
が存在しない場合辺
の場合
dd
ji
ji
jiW
2011/4/27 アルゴリズムとデータ構造 13 12
Floyd のアルゴリズムの説明
 次のような値を持つ行列 Ak ( k=0,1,2, ・・・ ,n )
を考える
 Ak[i,j] = k 番目以下の頂点だけ(両端を除く)を経由す
る頂点 i から j までの最短のパスの長さ
 Floyd のアルゴリズムでは, A0 , A1 , A2 ,・・・
, An の順に求める
 Ak[i,j] = MIN(Ak-1[i,j], Ak-1[i,k]+Ak-1[k,j])
i j
k
1 ~ k-1 の頂点を経由したパス
Ak-1[i,j]
Ak-1[i,k] Ak-1[k,j]
2011/4/27 アルゴリズムとデータ構造 13 13
Floyd 法で最短経路を求める
 if 文のところを以下のように書き換えて,最短経路が最初に経由
する頂点を 2 次元配列 P に記録しておくようにする
 配列 P の要素 P[i][j] は, i と j の間に辺があれば j ,なければ 0 で初
期化
if A[i][k] + A[k][j] < A[i][j]:
A[i][j] = A[i][k] + A[k][j]
P[i][j] = P[i][k]
 頂点 i から j への最短経路の出力:
def path(P, i, j):
k = i
while (P[k][j] != j) and (P[k][j] != 0):
print(k); k = P[k][j]
if P[k][j] = None: error(“no path”)
print k; print j
2011/4/27 アルゴリズムとデータ構造 13 14
グラフの木
 連結している無向グラフについて考える
 連結性を保ったまま,辺を取り除いていくと,辺の
数が |V|-1 になったところで,1本も取り除けなく
なる
 このグラフは木となる.
 これを元のグラフの全域木( spanning tree ,極大木)と
呼ぶ
 取り除き方によって全域木は何通りも作れる
 最小全域木( minimum spanning tree )とは,残った辺の
重みの総和が最小になるような全域木のこと
無向グラフ
2011/4/27 アルゴリズムとデータ構造 13 15
Prim 法
 最小全域木を求めるアルゴリズム
 基本的な戦略:
 1つの辺からはじめて,全頂点を覆うまで辺を 1 本ずつ付け加え,木を拡大
していく
def prim(G):
U = set()
V = set(G.vertices)
initialize_single_source(G, 0)
while len(U) != 0:
p = remove_mindist(U) # U の中で最小の dist を持つ頂点
を取り出す
V = V.add(p)
for e in p.edges: # p から出ているそれぞれの辺
q = e.dest
if q in U:
G.vertices[q].dist = min(G.vertices[q].dist,
e.weight)
ここだけ
Dijkstra 法と異
なる
2011/4/27 アルゴリズムとデータ構造 13 16
練習問題
 以下のグラフに対して, Prim 法を適用し,
最小全域木を求めよ
 別の頂点を出発点としたときでも,同じ最小全域
木が得られることを確認せよ
A
B
C
D E
F
G
7 8
5 5
79
15
6 8 9
11
2011/4/27 アルゴリズムとデータ構造 13 17
Prim 法の正しさの証明
 各ステップで集合 V と U を結ぶ辺のうち,重み最小のものを選んでい
る.
 この辺を e とし, e を含む最小木が必ず存在することを背理法で証明する
 e を含まない最小木があったとして,それを f とする
 f の重みは e の重み以上である
 この最小木には,辺 e の両端の頂点 p, q を結ぶ道があるので,これに e を
加えると閉路ができる.
 そこから f を除けば,再び全域木が得られるが,こちらの方が重みの総和が
小さい
e
V U
f
qp
2011/4/27 アルゴリズムとデータ構造 13 18
Kruskal 法
 各頂点をそれぞれ独立した集合とみなす
 重みの小さい辺から順に調べていき,異なる集合同士を結ぶ辺
があれば,この辺を最小木の辺の一つとして採用し 2 つの集合
を合併する
 最終的に集合が1つになるまで,上記を繰り返す
 練習問題:以下のグラフに対して, Kruskal 法を適用してみよ
A
B
C
D E
F
G
7 8
5 5
79
15
6 8 9
11

アルゴリズムとデータ構造13

  • 1.
  • 2.
    2011/4/27 アルゴリズムとデータ構造 132 概要  重み付きグラフについて説明する  最短経路問題とそれを解くアルゴリズムであ る Dijkstra 法, Floyd 法について説明する  最小全域木問題とそれを解くアルゴリズムで ある Prim 法, Kruskal 法について説明する
  • 3.
    2011/4/27 アルゴリズムとデータ構造 133 重み付きグラフ  辺に重み,もしくはコストが割り当てられたグラフ  例:  鉄道路線図において,路線を辺としたときの運賃  都市間の道路地図において,辺を各都市を結ぶ道路とした とき,移動にかかる時間  検討課題:他にはどのような例が考えられるか ? A 市 B 市 F 市 D 市 E 市 G 市 60 分 10 分 40 分 25 分 45 分 8 分
  • 4.
    2011/4/27 アルゴリズムとデータ構造 134 最短経路問題  重み付きグラフ上で与えられた 2 頂点 u, v を結ぶパ スの中で,それに沿った辺の重みの総和が最小とな るものを求める問題  以下のバリエーションを考える  出発点を 1 つに固定して,そこから他のすべての頂点への 最短経路を求める問題  与えられた2頂点間の最短経路を求める問題もこれを利用す る  すべての 2 頂点の組み合わせに対して,最短路を求める問 題  ここでは,重み付き有向グラフを対象とする
  • 5.
    2011/4/27 アルゴリズムとデータ構造 135 全ての重みが1の最短経路問題  全ての辺の重みが 1 の場合,最短経路問題は ,頂点 u と v を最も少ない個数の辺で結ぶパ スを見つけることになる.  この場合,幅優先探索によって解ける  確認問題:上記を確認せよ
  • 6.
    2011/4/27 アルゴリズムとデータ構造 136 単一出発点の問題  Dijkstra のアルゴリズム  考え方:  出発点から頂点 v への最短経路について, v の 1 つ手前の頂点までの経路は ,その頂点までの最短経路となっている.  戦略:  各頂点への最短経路を出発点に近いところから一つずつ確定していく  最短経路が確定した頂点の中から,次に近い頂点を探して,確定する  確定した頂点から出ている辺の次の頂点の距離が,より短かくなれば更新す る. 出発点 u v t 最短経路 最短経路 距離 =100 距離 =102 10 5 距離 =107
  • 7.
    2011/4/27 アルゴリズムとデータ構造 137 Dijkstra 法の動作 a b d c e 10 1 5 2 3 4 6 2 9 7 0 ∞ ∞ ∞ ∞ a b d c e 10 1 5 2 3 4 6 2 9 7 0 10 ∞ 5 ∞ a b d c e 10 1 5 2 3 4 6 2 9 7 0 8 14 5 7 a b d c e 10 1 5 2 3 4 6 2 9 7 0 8 13 5 7 a b d c e 10 1 5 2 3 4 6 2 9 7 0 8 9 5 7 a b d c e 10 1 5 2 3 4 6 2 9 7 0 8 9 5 7
  • 8.
    2011/4/27 アルゴリズムとデータ構造 138 Dijkstra 法の Python 風コード def dijkstra(G, s): V = set() # 空集合 U = list(range(len(G.vertices))) initialize_single_source(G, s) while len(U) > 0: p = remove_mindist(U) # の中で最小の dist を持つ頂点を取 り出す V.add(p) for e in G.edges[p]: # p から出ているそれぞれの辺 : q = e.dest if q in U: G.vertices[q].dist = min(G.vertices[q].dist, G.vertices[p].dist + e.weight) def initialize_single_source(G, s): for i in range(len(G.vertices)): G.vertices[i].dist = 999999 # ∞ の代用 G.vertices[s].dist = 0 最短経路を求める場合 ,ここで,直前の頂点 を記録しておけば良い
  • 9.
    2011/4/27 アルゴリズムとデータ構造 139 Dijkstra 法の正しさの証明  U 内の各頂点には, V に属する頂点だけを経由してその頂点に 達する最短のパスの長さ distance が記録されている  アルゴリズムの擬似コードから明らか  U の中から最小の distance を持つ頂点 p を選んだときに, p ま での最短経路が, V に属する頂点しか経由しないことを証明す る  背理法による:  もし, V に属さない点を経由して p に至るパスがあり, V から直接 p に達するパスよりも短いとする.このとき,このパス上で V の外 にある最初の頂点を q とする  頂点 q から p までのパスの長さは 0 以上なので,出発点から q に行 くパスの長さの方が,出発点から p に行くまでのパスの長さよりも 短くなくてはならない.これは,最小の distance を持つ頂点 p を選 んだ,とする仮定と矛盾する  注意:  Dijkstra 法が正しく動くためには,辺の重みが負ではないことが 条件.
  • 10.
    2011/4/27 アルゴリズムとデータ構造 1310 Dijkstra 法の正しさの証明(2) 出発点 p qV U
  • 11.
    2011/4/27 アルゴリズムとデータ構造 1311 全頂点間の問題  方法1:  Dijkstra アルゴリズムを各頂点に 1 回ずつ適用する  方法2: Floyd のアルゴリズム  入力:各辺の重みを表す 2 次元配列 W def floyd(W): A = copy(W) # 2 次元配列 W をコピー for k in range(len(A)): for i in range(len(A)): for j in range(len(A)): if A[i][k] + A[k][j] < A[i][j]: A[i][j] = A[i][k] + A[k][j]      →∞ = = )( )( )(0 ],[ の場合みが辺が存在して,その重 が存在しない場合辺 の場合 dd ji ji jiW
  • 12.
    2011/4/27 アルゴリズムとデータ構造 1312 Floyd のアルゴリズムの説明  次のような値を持つ行列 Ak ( k=0,1,2, ・・・ ,n ) を考える  Ak[i,j] = k 番目以下の頂点だけ(両端を除く)を経由す る頂点 i から j までの最短のパスの長さ  Floyd のアルゴリズムでは, A0 , A1 , A2 ,・・・ , An の順に求める  Ak[i,j] = MIN(Ak-1[i,j], Ak-1[i,k]+Ak-1[k,j]) i j k 1 ~ k-1 の頂点を経由したパス Ak-1[i,j] Ak-1[i,k] Ak-1[k,j]
  • 13.
    2011/4/27 アルゴリズムとデータ構造 1313 Floyd 法で最短経路を求める  if 文のところを以下のように書き換えて,最短経路が最初に経由 する頂点を 2 次元配列 P に記録しておくようにする  配列 P の要素 P[i][j] は, i と j の間に辺があれば j ,なければ 0 で初 期化 if A[i][k] + A[k][j] < A[i][j]: A[i][j] = A[i][k] + A[k][j] P[i][j] = P[i][k]  頂点 i から j への最短経路の出力: def path(P, i, j): k = i while (P[k][j] != j) and (P[k][j] != 0): print(k); k = P[k][j] if P[k][j] = None: error(“no path”) print k; print j
  • 14.
    2011/4/27 アルゴリズムとデータ構造 1314 グラフの木  連結している無向グラフについて考える  連結性を保ったまま,辺を取り除いていくと,辺の 数が |V|-1 になったところで,1本も取り除けなく なる  このグラフは木となる.  これを元のグラフの全域木( spanning tree ,極大木)と 呼ぶ  取り除き方によって全域木は何通りも作れる  最小全域木( minimum spanning tree )とは,残った辺の 重みの総和が最小になるような全域木のこと 無向グラフ
  • 15.
    2011/4/27 アルゴリズムとデータ構造 1315 Prim 法  最小全域木を求めるアルゴリズム  基本的な戦略:  1つの辺からはじめて,全頂点を覆うまで辺を 1 本ずつ付け加え,木を拡大 していく def prim(G): U = set() V = set(G.vertices) initialize_single_source(G, 0) while len(U) != 0: p = remove_mindist(U) # U の中で最小の dist を持つ頂点 を取り出す V = V.add(p) for e in p.edges: # p から出ているそれぞれの辺 q = e.dest if q in U: G.vertices[q].dist = min(G.vertices[q].dist, e.weight) ここだけ Dijkstra 法と異 なる
  • 16.
    2011/4/27 アルゴリズムとデータ構造 1316 練習問題  以下のグラフに対して, Prim 法を適用し, 最小全域木を求めよ  別の頂点を出発点としたときでも,同じ最小全域 木が得られることを確認せよ A B C D E F G 7 8 5 5 79 15 6 8 9 11
  • 17.
    2011/4/27 アルゴリズムとデータ構造 1317 Prim 法の正しさの証明  各ステップで集合 V と U を結ぶ辺のうち,重み最小のものを選んでい る.  この辺を e とし, e を含む最小木が必ず存在することを背理法で証明する  e を含まない最小木があったとして,それを f とする  f の重みは e の重み以上である  この最小木には,辺 e の両端の頂点 p, q を結ぶ道があるので,これに e を 加えると閉路ができる.  そこから f を除けば,再び全域木が得られるが,こちらの方が重みの総和が 小さい e V U f qp
  • 18.
    2011/4/27 アルゴリズムとデータ構造 1318 Kruskal 法  各頂点をそれぞれ独立した集合とみなす  重みの小さい辺から順に調べていき,異なる集合同士を結ぶ辺 があれば,この辺を最小木の辺の一つとして採用し 2 つの集合 を合併する  最終的に集合が1つになるまで,上記を繰り返す  練習問題:以下のグラフに対して, Kruskal 法を適用してみよ A B C D E F G 7 8 5 5 79 15 6 8 9 11