離散数学 09. 最短路 五島 DATE : 離散数学 深さ優先以外の探索 離散数学 深さ優先以外の探索 深さ優先探索: 「visit した先にある」頂点を次に visit 一般には, 訪問済み頂点に隣接したどの頂点も次に訪問可能 深さ優先探索が選ぶ頂点: 当然こちらを先に 選ぶことも可能 離散数学 様々な探索戦略 一般の優先度探索: 0 1 目的に応じた判断基準で優先度 を決める 2 3 深さ優先探索 (depth first search) 開始点から遠い頂点を先に 深さ優先 4 0 1-1 5 幅優先探索 (breadth first search) 開始点に近い頂点を先に 2-1 1-2 2-2 2-3 幅優先 2-4 離散数学 探索の一般形 最前線: 訪問済みから1 hop visit(s, G, visited) { F = { s }; /* Frontier */ visited[s] = 1; while (F {}) { u = F.delete_one(); for v adjacents(u) { if (visited[v] == 0) { visited[v] = 1; F.add(v); } } ここでどのノードを選ぶか } (だけ)で探索順序が決まる } visited[v] = 1 訪問済み 訪問済みから>1 hop or 離散数学 様々な探索戦略の実現 深さ優先探索 F の中で最後に F に入ったものを選ぶ F をスタック (Last-In-First-Out; LIFO) にする コードでは,再帰呼び出しのコール・スタックで実現 幅優先探索 F の中で,開始点からの深さが最小のものを選ぶ F をキュー (First-In-First-Out: FIFO) にする 一般の優先度探索 目的に応じた判断基準で優先度を決定 F を優先度付きキュー (priority queue) とする 離散数学 反復深化探索 深さ制限つき深さ優先探索 深さ優先探索を,開始点からの距離(再帰呼び出しを使うならその呼 び出しの深さ)に制限をつけて行う 反復深化 その深さ制限を徐々に増やしながら深さ優先探索を繰り返す メリット: 深さ優先と同じメモリ量 配列 visited を記憶しなくても,無限ループはしない 幅優先に似た広く浅い探索 浅い探索の結果を,「解の有望度」として,深い探索の順序制御 に用いることができる 離散数学 幅優先探索の性質 幅優先探索によってたどられた辺(visited[v] = 1の実行を引きおこしたu v)の集合は,sから各頂点への最短路(最小の辺数で到達する道)を与え る 「重みなしグラフ(重み一律)の最短路の問題」 bfs_visit(s, G, visited) { F = queue(s); /* C++ deque/list, Java : LinkedList */ visited[s] = 1; while (not F.is_empty()) { 9 u = F.deq(); /* 前から出す */ for v adjacents(u) { if (visited[v] == 0) { 6 visited[v] = 1; F.enq(v); /* 後ろに入れる */ }}}} 1 4 7 3 8 0 2 5 離散数学 最短路 離散数学 最短路の問題 2点間を結ぶ最短の道を求める 道の長さ: 重みなしグラフ: 含まれる辺の数,ホップ数 重みつきグラフ: 含まれる辺についた重みの和 1 3 4 3 2 3 3 1 7 4 離散数学 わかりやすい応用例 (1/2) 渋滞考慮無しのカーナビ 頂点: 交差点 辺 : 2交差点を結ぶ一本道 辺の重み: その一本道を抜けるのにかかる時間 渋滞無し 重みが時刻によらず一定 2頂点間の最短路: 2つの交差点間を最も早く移動するドライブ・ ルート クイズ: 一方通行,Uターン禁止,右折禁止などをどうモデル化する? 離散数学 わかりやすい応用例 (2/2) インターネットのルーティング 頂点: ネットワーク・ルータ 辺: ケーブル インターネットより下のレイヤ(2)で直接通信できるルータの対 辺の重み: パケットがそこを通ることに対するコスト ex) バンド幅の関数(バンド幅が大きければ小さい) 2頂点間の最短路: パケットを配送する適切な道 離散数学 少し自明でない応用例 (1/2) 時刻表を考慮した路線検索 頂点 (t, A) : 時刻 t に駅 A にいるという「状態」 辺: (t, A) (u, B): 時刻 t に A駅を発車して時刻 u に B駅につく列車が ある (t, A) (u, A): 時刻 t から u まで A駅にとどまる 辺の重み: かかる時間(上の例では u – t) (t, A) から (*, B) への最短路: 時刻 t に A 駅にいる人が最早で B駅に たどり着く経路 一見グラフではないものが,しばしばグラフとして表現できる 離散数学 少し自明でない応用例 (2/2) 編集距離 例 数字間の類似度は,その絶対差 0 1 4 9 3 と 4 の類似度は,|3 − 4| = 1 ギャップ - と数字の類似度は 3 数字列の類似度は,対応する数 字,ギャップ間の類似度の総和 1234 と 124- の 類 似 度 は , 0 + 0 + 1 + 3 = 4. 右の例の答え: 01-49 と 0263- 3 0 2 6 3 6 9 4 0 3 3 1 3 6 6 9 2 1 5 6 4 4 7 3 3 1 7 9 7 2 2 9 6 6 6 5 8 離散数学 アルゴリズム 1. 幅優先探索 重みなしグラフのみ 2. Dijkstra (ダイクストラ)法 (必修) 幅優先探索とほぼ同じ考え方 指定した一頂点から他の全頂点までの最短路を求める (一対全) 3. Floyd のアルゴリズム 全ての頂点対間の最短路を求める (全体全) 離散数学 幅優先探索と最短路 幅優先探索によって visit された頂点は,開始点からの最短路 「重みなしグラフ(重み一律)の最短路の問題」 bfs_visit(s, G, visited) { F = queue(s); /* C++ deque/list, Java : LinkedList */ visited[s] = 1; while (not F.is_empty()) { 9 u = F.deq(); /* 前から出す */ for v adjacents(u) { if (visited[v] == 0) { 6 visited[v] = 1; F.enq(v); /* 後ろに入れる */ }}}} 1 4 7 3 8 0 2 5 離散数学 Dijkstra 法 離散数学 Dijkstra 法 の 概要 優先度つきグラフ探索の一種: これまでに見つかっている s * u の距離が最短の u を優先して visit あとはほぼ「一般的な探索方法」に従うだけ 入力: 重みつきグラフ G = A, E とその一頂点 s ( A) 辺の重みは非負 アルゴリズムは無向でも同様 出力: D[i] (i A) が,s から i への最短距離を与えるような配列 最短路を与えるように変更するのは容易 離散数学 Dijkstra 法の概要 F 2 ∞ 12 10 ∞ 1 0 V 9 1 p 10 V : visited U : unvisited F : frontier U 10 1 ∞ 11 q ∞ 離散数学 Dijkstra 法: 擬似コード 4 8 3 1 6 start 4 2 1 5 goal Wu,v : 辺u vの重み(距離) dijkstra (s, G) { V = {}; D = new int[n]; for (i = 0; i < n; i++) { if (i == s) D[i] = 0; else D[i] = ; } while (V A) { pick u V that minimizes D; V = V + { 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 2 1 0 start 4 34 5 goal 789 離散数学 証明 F 2 ∞ 12 10 ∞ 1 0 V 9 1 p 10 V : visited U : unvisited F : frontier U 10 1 ∞ 11 q ∞ 離散数学 証明 前半 V 内の最短路は確定していると仮定 すると,U 内の仮距離は,V 内の頂点のみを経由して到達できる最短 の長さ 仮距離が最小の頂点 p F を選んだとき,p までの最短路が V 内の頂 点のみからなることを言えば,p までの最短路を確定できる. p までの最短路が V 内の頂点のみからなる: U 内の頂点を経由して p に至る最短路があると仮定, その経路上,F 内の頂点を q とする. distance(s, p) > distance(s, q) + distance(p, q) ⋀ distance(p, q) ≥ 0 distance(s, p) > distance(s, q) となり,矛盾. 離散数学 計算量 基本 while ループは常に n (頂点数) 回繰り返す ポイント 集合 D の表現と操作(2行) 単純アプローチ S : 配列 V[i] = 1 iff i V; : 線形探索 O(n) 全体の計算量 O(n2) 密なグラフならこれ以上は難し そう dijkstra(s, G) { V = {}; D = new int[n]; for (i = 0; i < n; i++) { if (i == s) D[i] = 0; else D[i] = ; } while (V A) { pick u V that minimizes D; V = V + { u }; for v adjacents(u) { d = D[u] + Wu,v ; if (d < D[v]) D[v] = d; } } } 離散数学 疎なグラフ用の Dijkstra 法 集合 F をヒープで表現 最小値の除去: O(log n) 優先度の変更: O(log n) 合計計算量 O((n + m) log n) m O(n) ならば O(n log n) 現実の道路・鉄道・ネット ワークetc.は? dijkstra (s, G) { F = 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 (F {}) { } delete u F 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 F to d; } } } } } } } 離散数学 ヒープ 1 二進木 0 1 3 2 2 親 < 子(または,親 > 子) 0 0 1 1 挿入,削除: O(log n) 3 1 2 2 1 3 2 2 離散数学 全対全 最短距離 方法 1: Dijkstra 法を各頂点を開始点として行う O(n3) または O(n (n + m) log n) 方法 2: Floyd のアルゴリズム O(n3) 離散数学 Floyd のアルゴリズム 離散数学 Floyd のアルゴリズム 概要: 全対全 最短距離 O(n3) 入出力: 入力: 辺重みつき有向グラフ G = V, E 出力: D[i][j] が i から j への最短距離を与えるような 2次元配列 D 仮定: Dijkstra 法と異なり,負の重みがあってもよい 重みの和が負となる閉路がない 離散数学 Floyd のアルゴリズム int D[−1 : n − 1][0 : n − 1][0 : n − 1]; (*) のループの不変条件: D[k][i][j] は, floyd (graph G) { for (i = 0; i < n; i++) for (j = 0; j < n; j++) D[−1][i][j] = G.weight(i, j); i から j への道で, for (k = 1; k < n + 1; k++) // (*) for (i = 0; i < n; i++) for (j = 0; j < n; j++) D[k][i][j] = min(D[k − 1][i][j] , D[k − 1][i][k] + D[k − 1][k][j]; 0, 1, …, k のみを経由する 最短路の長さ } 離散数学 考え方 考え方: D[k − 1][i][j]: 0, 1, …, k − 1 のみを経由する i, j 間の最短距離 から D[k ][i][j]: 0, 1, …, k − 1, k のみを経由する i, j 間の最短距離 を求 める 言い替え: D[k − 1][i][j]: 0, 1, …, k − 1 のみを経由する i, j 間の最短距離 が正しく分かっているとして, さらに k も経由していいとしたら,どうすればいいか? 離散数学 k 回目のイタレーション 0, 1, …, k − 1 のみを経由する i, j 間の最短距離 D[k − 1][i][j] i j D[k − 1][i][k] D[k − 1][k][j] 0, 1, …, k − 1 のみを経由する i, k 間の最短距離 k 0, 1, …, k − 1 のみを経由する k, j 間の最短距離 D[k][i][j] = min(D[k − 1][i][j], D[k − 1][i][k] + D[k − 1][k][j]) 0, 1, …, k のみを経由する i, j 間の最短距離 離散数学 k = 0 回目のイタレーション どこも経由しない i, j 間の最短距離 D[−1][i][j] i j D[−1][i][0] D[−1][0][j] どこも経由しない i, 0 間の最短距離 0 どこも経由しない 0, j 間の最短距離 D[0][i][j] = min(D[−1][i][j], D[−1][i][0] + D[−1][0][j]) 0 のみを経由する i, j 間の最短距離 離散数学 k = 1 回目のイタレーション 0 のみを経由する i, j 間の最短距離 D[0][i][j] i j D[0][i][1] D[0][1][j] 0 のみを経由する i, 1 間の最短距離 1 0 のみを経由する 1, j 間の最短距離 D[1][i][j] = min(D[0][i][j], D[0][i][1] + D[0][1][j]) 0, 1 のみを経由する i, j 間の最短距離 離散数学 動作例 0 1 1 2 1 0 1 0 1 1 0 2 3 3 1 0 1 3 1 0 3 0 1 0 1 1 2 0 1 0 2 3 4 3 1 2 3 0 1 2 3 0 0 1 2 3 1 4 0 1 2 1 2 3 4 0 1 0 3 1 2 3 0 D[k = 0]:0 を経由 2 3 0 1 0 0 1 2 0 1 2 0 1 1 D[k = 2]:0, 1, 2 を経由 3 0 1 2 3 0 0 1 2 3 1 3 0 1 2 1 2 3 4 0 1 2 2 3 0 1 0 3 1 2 3 0 3 1 2 3 0 D[k = −1]:初期化後 D[k = 1]:0, 1 を経由 D[k = 3]:0, 1, 2, 3 を経由 離散数学 Floyd のアルゴリズム int D[n][n]; D は2次元でよい 単に3次元目を省略 → floyd (graph G) { for (i = 0; i < n; i++) for (j = 0; j < n; j++) D[i][j] = G.weight(i, j); for (k = 0; k < n; k++) // (*) for (i = 0; i < n; i++) for (j = 0; j < n; j++) D[i][j] = min(D[i][j] , D[i][k] + D[k][j]; 記憶量は O(n2) でよい } 離散数学 一対一 最短距離 離散数学 一対一 最短距離? 一対一 最短距離: 与えられた2点間の最短距離を求める Dijkstra 法(一対全)を使って全頂点までの距離をまず求め, 目的地までの最短距離を取り出す 一対一だけを(一対全よりも)高速に計算する方法は知られていない 離散数学 探索問題 離散数学 抽象的(暗黙的)なグラフ データ構造として表せないようなグラフ ex) 巨大 抽象的(暗黙的)なグラフ: 隣接点の集合 adjacents(u) が計算できれば,「グラフ」と見ることが 可能 ex) ゲームのグラフ 頂点: ゲームの状態(盤面) u v : 状態 u から状態 v へ「一手で」移ることが可能 離散数学 探索問題 これまで述べた探索アルゴリズムは,抽象的なグラフに対しても適用可 能 巨大な状態空間の中からある性質を持った「ゴール・解」の「探索」 このような問題をしばしば「探索問題」と呼ぶ 離散数学 練習問題 グラフ探索の考え方を使って,「8パズル」を解くプログラムを書いてみ よ 2 3 4 7 1 5 8 1 2 3 4 5 6 7 8 グラフのノード数(の上界)は? 連結(どこからでもゴールできる)か? 最短(最小手数)でゴールするには? 6 ゴール 2 3 4 7 1 5 8 6 離散数学 探索問題の困難と戦略 困難: 探索空間が巨大(頂点数 n が大きい) すべてを探索するには時間がかかりすぎる メモリに収まりきらない (visited 配列すべてを記憶できない) 目的: 特定の頂点(ゴール,解)を見つけることで, 全頂点を見る必要はない ゴールに早くたどり着く戦略(経験則,heuristics)が必要 離散数学 いくつかの経験則・戦略 隣接頂点 adjacents(u) の順序付け 答えに到達する可能性が高い頂点を先に探索 頂点の「有望度」(例:解への近さ)を測る経験則が必要 メモリの節約 深さ優先探索で訪問済み頂点 (visited) を記憶しない 同じ頂点を 2度訪問してしまう危険性? ● グラフが木で,あり得ない場合 ● 許容される場合 浅く広いグラフの場合になお有用(必要メモリ量 O(深さ)) 離散数学 挑戦しがいのある課題 15パズル,ルービックキューブなど,状態空間が大きいパズルの解法 ルービックキューブくらいなら課題2として良いかも
© Copyright 2024 ExpyDoc