6.4 すべての頂点対に対する 最短経路問題

6.4 すべての頂点対に対する
最短経路問題
Dijkstra は出発点を1つ決めていたが、
全頂点間の最短路は?
ただし,n: 頂点の数,e: 辺の数
Dijkstra のアルゴリズムを全ての頂点に対し
て適用する:
隣接行列: O(n3)
隣接リスト: O(ne log n)
Floyd のアルゴリズム
初期化:
A[i,j] = C[i,j] for all i ≠ j
以下のように A を更新する:
Ak[i,j] = min { Ak-1[i,j], Ak-1[i,k]+Ak-1[k,j] }
Ak[i,j]: k 番目以下のノードのみを
通過する i から j への最短距離
k
i
j
Floyd のアルゴリズム
8
1
2
2
Ak[i,j] = min { Ak-1[i,j]
Ak-1[i,k]+Ak-1[k,j] }
3
2
3
5
k=1
A0
1
2
3
1
0
3
∞
2
8
0
2
3
5
∞
0
A1
1
2
3
1
0
3
∞
k=2
A2
1
2
3
1
0
3
5
2
8
0
2
3
5
8
0
2
7
0
2
3
5
8
0
k=3
2
8
0
2
3
5
8
0
A3
1
2
3
1
0
3
5
問題
次のグラフにおいて,Floydのアルゴリズム
の実行の様子を書きなさい
8
1
2
3
5
2
3
for i = 1 to n do {
for j = 1 to n do {
A[i,j] = C[i,j];
}
}
for i=1 to n do { A[i,i] = 0; }
for k = 1 to n do {
for i = 1 to n do {
for j = 1 to n do {
if(A[i,k] + A[k,j] < A[i,j]){
A[i,j] = A[i,k] + A[k,j];
}
}
}
※ Ak[i,k] = Ak-1[i,k] および Ak[k,j] = Ak-1[k,j]
}
なので、k ステップ目で A[i,k] および A[k,j] は
変化しない Î 同じ行列を書き換えても影響を与えない
問題
前スライドを言葉で説明しなさい
問題
Floydのアルゴリズムの計算量を書きなさい
FloydのアルゴリズムとDijkstraのアルゴリズム,
どちらの計算量が良いでしょうか?
Warshall のアルゴリズム
経路があるかどうかを知りたいだけの場合
Ak[i,j] = i から j まで k 番目以下のノードのみを
通過した経路が存在するかどうか
Ak[i,j] = Ak-1[i,j] ∨ (Ak-1[i,k] ∧ Ak-1[k,j])
※Floyd のアルゴリズムの距離が真偽値に変
えただけ (Floyd のアルゴリズム以前に発見
された)
応用例:有向グラフの中心
グラフの G=(V,E) の「なるべく真ん中」
頂点 v の離心率
= max { w から v への最短距離}
w∈V
G の中心
= 離心率が最小の頂点
有向グラフの中心の例
a
頂点 離心率
1
b
1
2
2
c
d
3
4
5
e
a
∞
b
6
c
8
d
5
e
7
問題
次の有向グラフの中心を書きなさい
a
1
b
1
2
2
c
d
3
1
1
e
計算量
1. Floyd のアルゴリズムを使用して、各頂点
間の最小距離を求める
O(n3)
2. 各列 i の最大コストを求める
= 頂点 i の離心率
O(n2)
3. 離心率最小の頂点を求める O(n)
問題
2個前の有向グラフの中心を求める計算過程です.
前スライドの1,2,3が該当する部分を示しなさい
a
b
c
d
e
a
0
1
3
5
7
b
∞
0
2
4
6
c
∞
3
0
2
4
d
∞
1
3
0
7
e
∞
6
8
5
0
max
∞
6
8
5
7
6.5 有向グラフのなぞり
木の行きがけ順のなぞりを一般化した深さ優先探索
1.
最初は有向グラフ G の全ての頂点に unvisited という
マークがついているとする
2.
G の中の1つの頂点 v を出発点として定める
A) 頂点v には visited というマークを付ける
B) 深さ優先探索を再帰的に使い、v に隣接する、かつ、
unvisited とマークされている頂点を1つずつ調べる
C) 頂点v から行ける頂点を全部訪問し終わった時点で v
に関する探索は終了する
3.
まだ unvisited とマークされている頂点が残っていれば、
そのうちの1つを新しい出発点として選び、2.A~2.C を繰
り返す
深さ優先探索の呼び出し
count = 0; //大域変数.頂点の訪問番号
for(v = 0; v < n; v++) //初期化
mark[v] = unvisited;
for(v = 0; v < n; v++) //呼び出し
if(mark[v] == unvisited)
dfs(v);
注:C言語なので頂点番号が1個ずれています
問題
前スライドを言葉で説明しなさい
深さ優先探索の関数
void dfs(int v){
mark[v] = visited;
dfnumber[v] = count; count++;
/* L[v] は v の隣接リスト */
for(L[v] 上の各頂点 w){
if(mark[w] == unvisited)
dfs(w);
}
}
※ 実際には dfs 内で v に対して行う処理がつけ
加わる
問題
前スライドを言葉で説明しなさい
深さ優先探索の計算量
e 本の辺,n (≦e) 個の頂点を持つグラフに対
して深さ優先探索を行ったとする
• dfs(v) が呼ばれると、mark[v] は直ちに
visited に変更される
• mark[v] が visited であるときは dfs(v) は呼
ばれない
よって,計算量はO(e)
深さ優先探索の例
頂点の訪問番号
2
6
F
B
1
A
5
E
4
7 G
3
D
C
問題
前スライドの例に深さ優先探索を行った.
実行の様子を完成しなさい
呼び出し側で,dfs(A)
関数内で,dfs(B)
関数内で,dfs(C)
関数内で,dfs(D)
呼び出し側で,dfs(?)
関数内で,?
深さ優先の極大森
dfs において unvisited な頂点につながっていた
弧(辺)からなる木の集合(森)
2
6
F
B
1
A
5
E
4
7 G
3
D
C
残りの弧:後退弧,先行弧,交差弧
w が v の子孫 ⇔ df#[v] ≦ df#[w] ≦ df#[v] + v の子孫数
ただし, df#[u] は頂点uの訪問番号
6
7 G
1
A
後
退
弧
B
4
3
D
交差弧
後退弧
E
交差弧
F
5
2
交差弧
先行弧: 番号が 小 Î 大
後退弧:番号が 大 Î 小
交差弧:番号が 大 Î 小
C
交差弧
※ 小 Î 大となる交差弧はあり得ない
(木の弧か先行弧になってしまう)
問題
次の有向グラフにおいて,深さ優先の極大森を
求めなさい.頂点には訪問番号を書きなさい.
さらに,後退弧,先行弧,交差弧も求めなさい.
F
B
A
G
D
C
E
v の子孫の数の求め方
void dfs(int v){
mark[v] = visited;
dfnumber[v] = count; count++;
children[v] = 0;
/* L[v] は v の隣接リスト */
for(L[v] 上の各頂点 w){
if(mark[w] == unvisited){
dfs(w);
children[v] += (1 + children[w]);
}
注:括弧がなくても正常に動きます.
}
ただし,実際には括弧をつけた方がいいです.
}