Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【2019-06-19】アルゴリズム勉強会 - 最小全域木

【2019-06-19】アルゴリズム勉強会 - 最小全域木

2c68dc672293cc3f8a7a57d3af86f15b?s=128

Hidehisa Arai

June 19, 2021
Tweet

Transcript

  1. 
 
 2021/06/19
 アルゴリズム勉強会
 Hidehisa Arai
 1

  2. はじめに
 水源 a b d f c e h i

    k l j g 10 15 18 33 11 21 20 6 85 9 56 2 22 77 5 39 30 81 14 12 38 41 67 水源から村の各戸に水道を引きたい。コスト最小になる引き方は? 
 => 最小全域木
 2
  3. ナイーブな考え方①
 水源 a b d f c e h i

    k l j g 10 15 18 33 11 21 20 6 85 9 56 2 22 77 5 39 30 81 14 12 38 41 67 水源から村の各戸に水道を引きたい。コスト最小になる引き方は? 
 =>コストが小さい辺からとりあえず採用してみよう 
 途中経過
 3
  4. ナイーブな考え方②
 水源 a b d f c e h i

    k l j g 10 15 18 33 11 21 20 6 85 9 56 2 22 77 5 39 30 81 14 12 38 41 67 水源から村の各戸に水道を引きたい。コスト最小になる引き方は? 
 =>コストが小さい辺からとりあえず採用してみよう 
 途中経過
 次にコストの低い辺は これだが、採用すると 巡回が発生する・・・
 =>巡回が発生しそうならスキップ! 
 4
  5. ナイーブな考え方③
 水源 a b d f c e h i

    k l j g 10 15 18 33 11 21 20 6 85 9 56 2 22 77 5 39 30 81 14 12 38 41 67 水源から村の各戸に水道を引きたい。コスト最小になる引き方は? 
 =>コストが小さい辺からとりあえず採用してみよう 
 いつ終わるの?
 => 全ノードを繋いだら終わり! 
 5
  6. ナイーブな考え方 - アルゴリズムを書き下してみる
 • 入力の辺のコストが小さい順に採用していく
 ◦ コスト順にソートしておくと良さそう! 
 • 巡回が発生しそうだったらスキップする


    ◦ 巡回が発生しそうってなに? 
 • 全ノードを繋いだら終わり!
 ◦ 簡単に判別する方法はある? 
 ◦ => 最終的には「木」になるのでノード数 - 1個の辺を採用 したら終わり!
 水源 a b c e h 10 15 18 33 11 21 20 6 繋いでは ダメな辺
 繋いでもOK な辺
 6
  7. ナイーブな考え方 - 巡回が発生しそうってなに?
 水源 a b c e h 10

    15 18 33 11 21 20 6 同じ部分グラフ= 連結成分
 別の部分グラフ=連 結成分でない
 両ノードが連結成分なエッジを選ば ないようにしたい
 =>連結成分の判定って? 
 =>Union Find!
 7
  8. ナイーブな考え方 - アルゴリズムを書き下してみる
 • 入力の辺のコストが小さい順に採用していく
 ◦ コスト順にソートしておくと良さそう! 
 • 巡回が発生しそうだったらスキップする


    ◦ 両ノードが連結成分なエッジをスキップする 
 ◦ => Union Findで連結かどうか判定 
 • 全ノードを繋いだら終わり!
 ◦ 簡単に判別する方法はある? 
 ◦ => 最終的には「木」になるのでノード数 - 1個の辺を採 用したら終わり!
 あり本間違えてる・・・? 
 クラスカル(Kruskal)法といいます 
 8
  9. クラスカル法 - 計算量
 O(ElogE)
 O(V)
 O(Vα(n))
 ※α(n)はアッカーマン関数の逆関数 
 O(ElogE)
 O(ElogV)


    OR†
 † E < V2 なためlogE < logV2 = 2logV 9
  10. 別の考え方①
 水源 a b d f c e h i

    k l j g 10 15 18 33 11 21 20 6 85 9 56 2 22 77 5 39 30 81 14 12 38 41 67 水源から村の各戸に水道を引きたい。コスト最小になる引き方は? 
 => 連結を保ちながらちょっとずつ木を大きくすることもできるのでは? 
 最初は重み最小の辺で良さそう、次は? 
 10
  11. 別の考え方②
 水源 a b d f c e h i

    k l j g 10 15 18 33 11 21 20 6 85 9 56 2 22 77 5 39 30 81 14 12 38 41 67 水源から村の各戸に水道を引きたい。コスト最小になる引き方は? 
 => 連結を保ちながらちょっとずつ木を大きくすることもできるのでは? 
 最初は重み最小の辺で良さそう、次は? 
 => すでに連結されている成分で最小コストの辺を繋ぐ 
 次にコストの低い辺は これだが、採用すると 巡回が発生する・・・
 =>巡回が発生しそうならスキップ! 
 11
  12. 別の考え方③
 水源 a b d f c e h i

    k l j g 10 15 18 33 11 21 20 6 85 9 56 2 22 77 5 39 30 81 14 12 38 41 67 水源から村の各戸に水道を引きたい。コスト最小になる引き方は? 
 => 連結を保ちながらちょっとずつ木を大きくすることもできるのでは? 
 いつ終わるの?
 => 全てのノードが繋がれば終わり! 
 12
  13. 別の考え方 - アルゴリズムを書き下してみる
 • 初めはコスト最小の辺を選ぶ(必須ではない)
 • すでに連結な成分に繋がっている辺の中で最小 コストの辺を選ぶ、ただし巡回が発生しそうな時は スキップ
 ◦

    連結成分から伸びる辺で最小コストの辺ってどうやって 見つける?
 ◦ 巡回が発生しそうってなに? 
 • 全ノードを繋いだら終わり!
 ◦ 簡単に判別する方法はある? 
 ◦ => 最終的には「木」になるのでノード数 - 1個の辺を採用 したら終わり!
 d f h i k j g 9 2 22 5 81 41 最小がこれな ことをどう判定 する?
 13
  14. 別の考え方 - 最小コストの辺をどう見つける?
 d f h i k j g

    9 2 22 5 81 12 41 minCost[h] = 22 minCost[k] = 5 minCost[j] = 81 minCost[g] = 41 minCost[d] = 9 連結成分からそれ 以外のノードへの 最小移動コストを計 算しておき、その中 で最小の値をとる。
 d f e h i k j g 9 2 22 5 81 41 67 minCost[e] = INF 連結成分から いけないノー ドはINF e 67 minCost[h] = 22 minCost[k] = 5 used[k] = true minCost[j] = 81 minCost[d] = 9 minCost[e] = INF minCost[g] = min(41, 12) 14
  15. • すでに連結な成分に繋がっている辺の中で最小コストの辺を選ぶ、ただし巡回が 発生しそうな時はスキップ
 ◦ 連結成分から伸びる辺で最小コストの辺ってどうやって見つける? 
 ▪ 連結成分からのそれ以外のノードへの最小コストを保持しておく 
 ▪

    連結成分から直接いけないノードへの最小コストはINF 
 ▪ 辺を選択した後、最小コストを更新する必要がある 
 • 全てのノードについて新しく追加されたノードからのコストと現在のminCostを比較する 
 ◦ 巡回が発生しそうってなに? 
 ▪ クラスカル法と違い、同じ連結成分かどうか調べる必要はない(連結成分は必ず一つの木) 
 ▪ すでに連結成分になったかどうかを記録しておくだけでヨシ! 
 ▪ 次の最小の辺は「まだ連結されていない辺」から選ぶ 
 別の考え方 - 最小コストの辺をどう見つける?
 15
  16. 別の考え方 - 必要な要素を考える 連結成分からのそれ以外のノードへの最小コストを保持しておく連結 成分から直接いけないノードへの最小コストはINF 
 => minCostの配列が必要。|minCost| = VでINFで初期化する。

    
 すでに連結成分になったかどうかを記録しておくだけでヨシ! => 連結成分かどうかの配列が必要。|used| = Vでfalseで初期化する。 
 次の最小の辺は「まだ連結されていない辺」から選ぶ => 全てのノードvについてused[v] = falseかつminCost[v]が最小かを確認 
 全てのノードについて新しく追加されたノードからのコストと現在のminCostを比較する 
 => ノードからノードへのコストを保持する行列Costが必要。辺が実際には存在し ないところはINFで初期化する。 
 => 全てのノードvについて現在のminCost[v]とCost[v][u]の比較をし、Cost[v][u] の方が小さければminCost[v] = Cost[v][u]となる。 
 16
  17. 別の考え方 - アルゴリズム全体
 ノードからノードへのコストを保持する行列が必 要。辺が実際には存在しないところはINFで初期 化する。 minCostの配列が必要。|minCost| = VでINFで初 期化する。

    連結成分かどうかの配列が必要。|used| = Vで falseで初期化する。 全てのノードvについてused[v] = falseかつ minCost[v]が最小かを確認 全てのノードvについて現在のminCost[v]と Cost[v][u]の比較をし、Cost[v][u]の方が小さけ ればminCost[v] = Cost[v][u]となる。 17 プリム(Prim)法といいます 

  18. 別の考え方 - 計算量
 O(V)
 V回実行される
 単体でO(V)
 単体でO(V)
 O(V2)
 O(V2)
 V×


    V×
 O(V2)
 18
  19. 別の考え方 - 計算量改善
 プライオリティキューを使う => O(logV)
 隣接リストを使う
 O((V+E)logV)
 19