離散数学 08. グラフの探索 五島.

Slides:



Advertisements
Similar presentations
組合せ最適化輪講 2.3 連結性 川原 純. 2.3 連結性 内容 – グラフ上の節点をすべてたどるアルゴリズム 計算機上でのグラフの表現 – 強連結成分を求めるアルゴリズム トポロジカル順序を求める方法も – k- 連結、 k- 辺連結について – 2- 連結グラフの耳分解について.
Advertisements

©2008 Ikuo Tahara探索 状態空間と探索木 基本的な探索アルゴリズム 横形探索と縦形探索 評価関数を利用した探索アルゴリズム 分岐限定法 山登り法 最良優先探索 A ( A* )アルゴリズム.
2015 年度 有限幾何学 期末試験 次の各問に答えよ.( (1), (2), (3), (4), (5), (7), (8), (10) は答えだけでよい) (1) 有向閉路が存在しない位数 4 のトーナメントを描け. (2) 内点の個数が 10 個の正則 2 分木の葉の個数を求めよ. (3) グラフ.
A Simple Constant Time Enumeration Algorithm for Free Trees 中野 眞一 宇野 毅明 群馬大学 情報学研究所 2003 年 9 月 19 日 アルゴリズム研究会.
アルゴリズムとデータ構造 2011年7月7日
2009/12/4 グラフ (2) 第10講: 平成21年12月4日 (金) 4限 E252教室 コンピュータアルゴリズム.
データ構造と アルゴリズム 第十二回 知能情報学部 知能情報学科 新田直也.
ラベル付き区間グラフを列挙するBDDとその応用
アルゴリズムとデータ構造 2013年6月18日
アルゴリズムとデータ構造1 2005年7月8日
アルゴリズムとデータ構造 2010年7月5日
算法数理工学 第9回 定兼 邦彦.
    有限幾何学        第8回.
On the Enumeration of Colored Trees
Problem G : Entangled Tree
An Algorithm for Enumerating Maximal Matchings of a Graph
アルゴリズムとデータ構造 2012年6月14日
情報工学概論 (アルゴリズムとデータ構造)
データ構造とアルゴリズム 第7回 木 ~ データ構造(3)~.
データ構造とアルゴリズム 第6回の2 木 ~ データ構造(3)~.
第11講: 平成18年12月 8日 (金) 4限 E352教室 グラフ (1).
第4章 空間解析 2.ネットワーク分析 (3) ネットワーク構造分析
ベイジアンネットワーク概説 3.6 構造の探索アルゴリズム
s a b f c e d 2016年度 有限幾何学 期末試験 問1:15点
アルゴリズムとデータ構造 2011年6月14日
アルゴリズム入門.
アルゴリズムとデータ構造 2012年7月12日
二分探索木によるサーチ.
2009/10/16 いろいろなデータ構造 第3講: 平成21年10月16日 (金) 4限 E252教室 コンピュータアルゴリズム.
Cプログラミング演習 中間まとめ2.
茨城大学 工学部 知能システム工学科 井上 康介 E2棟801号室
アルゴリズムとデータ構造 2011年7月4日
アルゴリズムとデータ構造 2012年6月28日
アルゴリズムとデータ構造 2011年6月27日
第25章 単一始点最短路 3節 Bellman-Fordのアルゴリズム
動的スライスを用いた バグ修正前後の実行系列の比較
算法数理工学 第8回 定兼 邦彦.
アルゴリズムとデータ構造 2013年7月16日
グラフアルゴリズムの可視化 数理科学コース 福永研究室 高橋 優子 2018/12/29.
アルゴリズムとデータ構造1 2006年7月4日
Ibaraki Univ. Dept of Electrical & Electronic Eng.
動的スライスを用いたバグ修正前後の実行系列の差分検出手法
b f c a d e g h a b c d e f g 図1 図2 2012年度 有限幾何学 期末試験
データ構造と アルゴリズム 第七回 知能情報学部 新田直也.
First Course in Combinatorial Optimization
A Simple Algorithm for Generating Unordered Rooted Trees
離散数学 07. 木 五島.
Cプログラミング演習 第10回 二分探索木.
アルゴリズムとデータ構造 2010年7月26日
アルゴリズムとデータ構造 2011年7月8日課題の復習
コンパイラ 2011年10月20日
アルゴリズムとデータ構造 第3章 ヒープ 5月31日分
IF文 START もしも宝くじが当たったら 就職活動する 就職活動しない YES END NO.
第9回 優先度つき待ち行列,ヒープ,二分探索木
離散数学 06. グラフ 序論 五島.
アルゴリズムとデータ構造1 2008年7月24日
アルゴリズムとデータ構造 2013年7月1日
情報工学概論 (アルゴリズムとデータ構造)
アルゴリズムとデータ構造1 2009年7月2日
アルゴリズムとデータ構造 2013年7月8日
2009/11/27 グラフ (1) 第9講: 平成21年11月27日 (金) 4限 E252教室 コンピュータアルゴリズム.
コストのついたグラフの探索 分枝限定法 A*アルゴリズム.
離散数学 11. その他のアルゴリズム 五島.
回帰テストにおける実行系列の差分の効率的な検出手法
関数と再帰 教科書13章 電子1(木曜クラス) 2005/06/22(Thu.).
プログラミング演習I 2003年6月11日(第9回) 木村巌.
第10回 関数と再帰.
7.木構造 7-1.データ構造としての木 7-2.2分探索木 7-3.高度な木.
グラフの帯域幅連続多重彩色 を求めるアルゴリズム (Bandwidth Consective Multicolorings of Graphs) 西関研究室 西川和秀.
アルゴリズムとデータ構造 2012年7月9日
Presentation transcript:

離散数学 08. グラフの探索 五島

頂点の探索 頂点の探索: 「ある頂点から到達可能な頂点をすべて発見する」という手続き 離散数学 頂点の探索 頂点の探索: 「ある頂点から到達可能な頂点をすべて発見する」という手続き 以下のような,様々な問題が 頂点の探索 の応用で効率的に解ける: ある頂点から到達可能な頂点の列挙 (強)連結成分へ分解 無向グラフの各連結成分の全域木を(ひとつ)見つける 閉路の検出 DAG のトポロジカル・ソート

離散数学 木の巡回

再帰呼び出しによる二分木の巡回 call call visit (int u) { if (g.left(u)) 離散数学 再帰呼び出しによる二分木の巡回 call call visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } return visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } c b a d e

再帰呼び出しによる二分木の巡回 行きがけ: call 直後 call visit (int u) { if (g.left(u)) 離散数学 再帰呼び出しによる二分木の巡回 行きがけ: call 直後 call visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } return 帰りがけ: return 直前 visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } visit (int u) { if (g.left(u)) visit(g.left(u)); if (g.right(u)) visit(g.right(u)); } 行きがけ c b a d 帰りがけ e

巡回の順序 木の巡回順序: 頂点の処理順序 行きがけ順 (pre-order) 通りがけ順 (in-order) (二分木のみ) 離散数学 巡回の順序 木の巡回順序: 頂点の処理順序 行きがけ順 (pre-order) 通りがけ順 (in-order) (二分木のみ) 帰りがけ順 (post-order) 頂点を訪れる (visit) 順番は変わらない が, 頂点を処理する順番が変わる 処理: コードでは頂点番号の表示 (print).

二分木の巡回 (binary tree traversal) 離散数学 二分木の巡回 (binary tree traversal) 行きがけ順 (pre-order) 通りがけ順 (in-order) 帰りがけ順 (post-order) visit (int u, Graph g) { print u; if (g.left(u)) visit(g.left(u), g); if (g.right(u)) visit(g.right(u), g); } visit (int u, Graph g) { if (g.left(u)) visit(g.left(u), g); print u; if (g.right(u)) visit(g.right(u), g); } visit (int u, Graph g) { if (g.left(u)) visit(g.left(u), g); if (g.right(u)) visit(g.right(u), g); print u; } 3 1 1 2 2 3 4 3 2 1 4 7 6 5 4 5 6 6 7 7 5

木の巡回 (tree traversal) 行きがけ順 (pre-order) 帰りがけ順 (post-order) 離散数学 木の巡回 (tree traversal) 行きがけ順 (pre-order) 帰りがけ順 (post-order) visit (int u, Graph g) { print u; foreach (v in g.adjacent(u)) visit(v, g); } visit (int u, Graph g) { foreach (v in g.adjacent(u)) visit(v, g); print u; } 3 1 2 4 4 2 5 3 1 9 7 5 6 8 8 6 9 7

離散数学 グラフの深さ優先探索

深さ優先探索 深さ優先探索 (depth first search: DFS) 入力: グラフ G と開始点 s 目的: 離散数学 深さ優先探索 深さ優先探索 (depth first search: DFS) 入力: グラフ G と開始点 s 目的: s から到達可能な頂点をすべて見つける 以下のコードでは,頂点を一度ずつ print

離散数学 疑似コード enum State {unvisited, visiting}; State state[N]; visit(int u, Graph g); visit_all (Graph g) { for (int i = 0; i < N; i++) state[i] = unvisited; for (int i = 0; i < N; i++) if (state[i] == unvisited) visit(i); } visit (int u, Graph g) { state[u] = visiting; print u; foreach (int v in g.adjacents(u) ) if (state[v] == unvisited) visit(v, g); }

離散数学 visit() の説明 visit (int u, Graph g) { state[u] = visiting; print u; foreach (int v in g.adjacents(u) ) if (state[v] == unvisited) visit(v, g); } 行きがけに, visiting にし, 表示. g.adjacents(u) のそれぞれに対し, unvisited なら visit()

動作 a a a a a b b b b b c d c d c d c d c d > a > ab > ab 離散数学 動作 visit(a) visit(a) visit(a) visit(a) visit(a) a a a a a unvisited ? visit(b) visit(b) visit(b) visit(b) visit(b) b b b b b visit(c) visit(c) visit(c) ? c d c d c d c d c d > a > ab > ab > abc > abc visit(a) visit(a) visit(a) visit(a) a a a a : unvisited a visit(b) visit(b) visit(b) visit(b) : visiting b b b b a : visited visit(d) visit(d) visit(d) a ? c d c d c d c d > abc > abcd > abcd > abcd

閉路がある場合の動作 a a a b b b c d c d c d > abcd > abcd > abcd 離散数学 閉路がある場合の動作 visit(a) visit(a) visit(a) a a a visit(b) visit(b) visit(b) b b b unvisited ? visit(c) visit(c) visit(c) c d c d c d visit(d) visit(d) visit(d) > abcd > abcd > abcd : unvisited a : visiting a : visited a

離散数学 無向グラフの場合の挙動 3 1 4 8 7 6 9 5 2 s

全頂点探索の計算量 時間計算量 : O(n + m) n: 頂点数,m: 辺数 空間計算量: O(n) 配列 visited[] の大きさ 離散数学 全頂点探索の計算量 時間計算量 : O(n + m) n: 頂点数,m: 辺数 空間計算量: O(n) 配列 visited[] の大きさ

離散数学 無向グラフの閉路 と 全域木 の検出

全域木 (spanning tree) 全域木(spanning tree,スパニング・トゥリー,「張る木」): 離散数学 全域木 (spanning tree) 全域木(spanning tree,スパニング・トゥリー,「張る木」): ある無向連結グラフの部分グラフで, 親グラフの全頂点を含み, 木(非循環)であるもの 非連結にならないように,閉路を切断 できればよいが… 1 4 1 2 5 2 6 7 3 6 3 4 5 7

無向グラフに対する閉路の検出 閉路:a1  a2  …  am  a1 a1 : s から始めて,最初に visit(). 離散数学 無向グラフに対する閉路の検出 閉路:a1  a2  …  am  a1 a1 : s から始めて,最初に visit(). visit(s) ⇒…⇒ visit(a1) ⇒visit(a2) ⇒ …⇒ visit(am) の visit(am) 内で state[a1] == visiting 閉路が存在する am  a1 は,全域木に含めない s a1 visiting ? am a2 visit(am) a5 a3 a4

離散数学 無向グラフの閉路の検出(修正前) enum State {unvisited, visiting}; State state[N]; visit(int u, Graph g); find_cycle_undir (Graph g) { for (int i = 0; i < N; i++) state[i] = unvisited; for (int i = 0; i < ; i++) if (state[i] == unvisited) visit(i, −1); } visit (int u, Graph g) { state[u] = visiting; print u; foreach (int v in g.adjacents(u) ) if (state[v] == unvisited) visit(v); else // if (state[v] == visiting) print “cycle found!”; // (*) }

バグ w → u に対して, visit(w) から visit(u) が呼ばれたとき w ∈ adjacent(u) 離散数学 バグ w → u に対して, visit(w) から visit(u) が呼ばれたとき w ∈ adjacent(u) state[w] != unvisited u → w を閉路として検出! 修正: w を(処理対象から)除外 visit(w) から visit(u) を呼ぶとき, 引数で w を渡す s visit(w) w visit(u) !unvisited u !unvisited

離散数学 無向グラフの閉路の検出(修正後) enum State {unvisited, visiting}; State state[N]; visit(int u, int w, Graph g); find_cycle_undir (Graph g) { for (int i = 0; i < N; i++) state[i] = unvisited; for (int i = 0; i < N; i++) if state[i] == unvisited) visit(i, −1, g); } visit (int u, int w, Graph g) { state[u] = visiting; print u; foreach (int v in g.adjacents(u) ) if (v != w) if (state[v] == unvisited) visit(v, u, g); else // if (state[v] == visiting) print “cycle found!”; // (*) }

閉路がある場合の動作 a a a b b b c d c d c d > abcd > abcd > abcd 離散数学 閉路がある場合の動作 visit(a, ?) visit(a, ?) visit(a, ?) a a a visit(b, a) visit(b, a) visit(b, a) b b b unvisited ? visit(c, b) visit(c, b) visit(c, b) c d c d c d visit(d, c) visit(d, c) visit(d, c) > abcd > abcd > abcd v == w ? : unvisited a : visiting a : visited a

全域木 (spanning tree) 全域木(spanning tree,スパニング・トゥリー,「張る木」): 離散数学 全域木 (spanning tree) 全域木(spanning tree,スパニング・トゥリー,「張る木」): ある無向連結グラフの部分グラフで, 親グラフの全頂点を含み, 木(非循環)であるもの 非連結にならないように,閉路を切断 できればよいが… 1 4 1 2 5 2 6 7 3 6 3 4 5 7

全域木の検出 s から始まる visit() の再帰呼び出しのグラフ ⇒ (s を含む連結成分の)全域木 証明: 離散数学 全域木の検出 s から始まる visit() の再帰呼び出しのグラフ ⇒ (s を含む連結成分の)全域木 証明: visit() できるのは,s を含む連結成分 閉路を発見すると,その先には visit() しない visit() の再帰呼び出しからなるグラフには閉路がない ⇒ 木

離散数学 有向グラフの閉路の検出

無向グラフに対する閉路の検出 閉路: a1  a2  …  am  a1 a1 : s から始めて,最初に visit(). 離散数学 無向グラフに対する閉路の検出 閉路: a1  a2  …  am  a1 a1 : s から始めて,最初に visit(). visit(s) ⇒…⇒ visit(a1) ⇒visit(a2) ⇒ …⇒ visit(am) の visit(am) 内で state[a1] == visiting 閉路が存在する am  a1 は,全域木に含めない s a1 visiting ? am a2 visit(am) a5 a3 a4

無向 / 有向グラフに対する閉路の検出 無向グラフの場合: unvisited でなく visiting なら閉路 有向グラフの場合: 離散数学 無向 / 有向グラフに対する閉路の検出 無向グラフの場合: unvisited でなく visiting なら閉路 有向グラフの場合: ⇒ ウソ s ① ② a1 visiting ? am a2 visit(am) a5 a3 a4

有向グラフ向け修正 visit(s) ⇒…⇒ visit(a1) ⇒visit(a2) ⇒ …⇒ visit(am) の 離散数学 有向グラフ向け修正 visit(s) ⇒…⇒ visit(a1) ⇒visit(a2) ⇒ …⇒ visit(am) の 探索中に a1 に到達したら閉路 探索中でなければ閉路でない 探索終了後の頂点を visited として区別 s ① ② a1 visiting ? am a2 visit(am) a5 a3 a4

離散数学 有向グラフ用閉路の検出 enum State {unvisited, visiting, visited}; State state[N]; visit(int u, Graph g); find_cycle_dir (Graph g) { for (int i = 0; i < N; i++) state[i] = unvisited; for (int i = 0; i < N; i++) if (state[i] == unvisited) visit(i); } visit (int u, Graph g) { state[u] = visiting; print u; foreach (int v in g.adjacents(u) ) if (state[v] == unvisited) visit(v, g); else if (state[v] == visiting) print “cycle found!”; // (*) state[u] = visited; }

バグではない w → u に対して, visit(w) から visit(u) が呼ばれたとき w ∈ adjacent(u) 離散数学 バグではない w → u に対して, visit(w) から visit(u) が呼ばれたとき w ∈ adjacent(u) state[w] != visiting u → w を閉路として検出! ⇒ あってる s visit(w) w visiting ? visit(u) u visiting ?

閉路がある場合の動作 a a a b b b c d c d c d > abcd > abcd cycle found! 離散数学 閉路がある場合の動作 visit(a) visit(a) visit(a) a a a visit(b) visit(b) visit(b) b b visiting? b visit(c) visit(c) visit(c) c d c d c d visit(d) visit(d) visit(d) > abcd > abcd cycle found! > abcd cycle found! : unvisited a : visiting a : visited a

閉路でない場合の動作 a a a b b b c d c d c d > abc > abcd > abcd 離散数学 閉路でない場合の動作 visit(a) visit(a) visit(a) a a a visit(b) visit(b) visit(b) b b b visit(c) visit(c) visit(d) visit(d) c d c d c d visiting? > abc > abcd > abcd : unvisited a : visiting a : visited a

離散数学 連結成分への分解

無向グラフの連結成分への分解 s を含む連結成分: s から到達可能な全頂点(無向グラフの性質) 離散数学 無向グラフの連結成分への分解 s を含む連結成分: s から到達可能な全頂点(無向グラフの性質) s から始まる visit() で訪れた頂点の集合 証明: 基本的には,v in adjacents(u) なるすべての v に visit(v) する v in adjacents(u) なのに visit(v) しない v は,既に visit() したものだけ

有向グラフの場合 どの2つも一般には一致しない: s を含む強連結成分(の頂点の集合) ⊆ s から到達可能(な頂点の集合) 離散数学 有向グラフの場合 どの2つも一般には一致しない:   s を含む強連結成分(の頂点の集合) ⊆ s から到達可能(な頂点の集合) ⊆ s を含む連結成分(の頂点の集合) s を含む強連結成分 s s s を含む連結成分 sから到達可能

有向グラフの(強)連結成分への分解 連結成分への分解 辺の向きを無視した無向グラフを作り,それを連結成分へ分解すればよい 強連結成分への分解 離散数学 有向グラフの(強)連結成分への分解 連結成分への分解 辺の向きを無視した無向グラフを作り,それを連結成分へ分解すればよい 強連結成分への分解 少し難しい 省略(石畑の教科書参照)

離散数学 DAG のトポロジカル・ソート

DAG のトポロジカル・ソート 有向グラフの全頂点を一列に並べる v1, v2,..., vn 離散数学 DAG のトポロジカル・ソート 有向グラフの全頂点を一列に並べる v1, v2,..., vn ただし,vi * vj (i < j) * で表される半順序関係を包含す る全順序関係の構築 グラフに閉路がある? ある (DCG): 並べ方は存在しない ない (DAG): 一通り以上 存在する c a b d f e a, b, c, d, e, f a, b, d, e, c, f a, b, d, c, e, f

トポロジカル・ソートの応用例 例1:論文 辺 a  b: 項目 b を説明するには a を事前に説明しておく必要がある 離散数学 トポロジカル・ソートの応用例 例1:論文 辺 a  b: 項目 b を説明するには a を事前に説明しておく必要がある トポロジカル・ソート: 項目を並べる順番 例2:式の評価(コンパイラ,etc) 頂点: 評価したい(値を求めたい)式の部分式 トポロジカル・ソート: 式を評価すべき順番(の逆順) * (a + b) * (a + b + c) *; +2; +1; a; b; c +1 +2 a b c

離散数学 巡回順序 と トポロジカル・ソート × 行きがけ順 × 左優先: *; +1; a; b; +2; c ∵ +1 と +2 の順序が逆 ○ 右優先: *; +2; c; +1; b; a ○ 帰りがけ順(の逆順) ○ 左優先: a; b; +1; c; +2; * ○ 右優先: c; b; a; +1; +2; * * (a + b) * (a + b + c) *; +2; +1; a; b; c +1 +2 a b c

離散数学 トポロジカル・ソートの疑似コード enum State {unvisited, visiting, visited}; State state[N]; queue q; visit (int u, Graph g); queuetopological_sort (Graph g) { for (int i = 0; i < n; i++) state[i] = unvisited; q.empty(); for (int i = 0; i < n; i++) if (state[i] == unvisited) visit(i, g); return q.reverse(); } visit (int u, Graph g) { state[u] = visiting; foreach (v in adjacents(u) ) if (state[v] == unvisited) visit(v, g); else if (state[v] == visiting) print “not a DAG!”; // (*) state[u] = visited; q.append(u); // add to tail }

証明 u  v とする 「u より v が先に visited になる」ことを言えばよい 離散数学 証明 u  v とする 「u より v が先に visited になる」ことを言えばよい 先に visited になる ⇒ 先に q.append() される ⇒ 後に出力される visit(u, ...) 中で… visited[v] == visiting DAG なので,起こり得ない(エラーで終了) visited[v] == visited すなわち, v はすでに visited になっている visited[v] == unvisited visit(v, ...) が実行され,u より v が先に visited になる

離散数学 まとめ

頂点の探索 頂点の探索: 「ある頂点から到達可能な頂点をすべて発見する」という手続き 離散数学 頂点の探索 頂点の探索: 「ある頂点から到達可能な頂点をすべて発見する」という手続き 以下のような,様々な問題が 頂点の探索 の応用で効率的に解ける: ある頂点から到達可能な頂点の列挙 (強)連結成分へ分解 無向グラフの各連結成分の全域木を(ひとつ)見つける 閉路の検出 DAG のトポロジカル・ソート

離散数学 次回 今回: 深さ優先探索 「行けるところまで行く」 次回: 深さ優先以外の探索 ⇒ 最短路,etc.