アルゴリズムからプログラムへ GRAPH-SEARCH

Slides:



Advertisements
Similar presentations
Generic programming と STL
Advertisements

プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
プログラムのパタン演習 解説.
プログラミング基礎I(再) 山元進.
プログラミング言語としてのR 情報知能学科 白井 英俊.
データ構造とアルゴリズム論 第6章 探索のアルゴリズム
アルゴリズムとデータ構造 第2章 リスト構造 5月17日分
実行時のメモリ構造(1) Jasminの基礎とフレーム内動作
最適化ソルバーのための Python言語入門
C言語 配列 2016年 吉田研究室.
プログラミング言語論 第6回 型 情報工学科 篠埜 功.
情報工学概論 (アルゴリズムとデータ構造)
Natural Semantics 実行過程の、最初と最後の状態(state)の関係を考える。
情報理論2 第6回 小林 学 湘南工科大学 2011年11月15日 〒 神奈川県藤沢市辻堂西海岸1-1-25
プログラミング論 II 電卓,逆ポーランド記法電卓
第11講: 平成18年12月 8日 (金) 4限 E352教室 グラフ (1).
データ構造とアルゴリズム 第13回 スタックとキュー
アルゴリズムと データ構造 第4回 スタック・キュー.
関数 関数とスタック.
データ構造と アルゴリズム 第十一回 理工学部 情報システム工学科 新田直也.
2009/10/16 いろいろなデータ構造 第3講: 平成21年10月16日 (金) 4限 E252教室 コンピュータアルゴリズム.
アルゴリズムとデータ構造 2011年7月4日
アルゴリズムとデータ構造 2012年6月28日
アルゴリズムとデータ構造 2011年6月27日
アルゴリズムとデータ構造 2013年7月16日
Ibaraki Univ. Dept of Electrical & Electronic Eng.
プログラミング 4 記憶の割り付け.
プログラミング言語入門.
二分木のメソッド(続き).
データ構造と アルゴリズム第4回 知能情報学メジャー 和田俊和.
2011/06/21-24 情報知能学科「アルゴリズムとデータ構造」 担当:白井英俊
講義では、Cプログラミングの基本を学び 演習では、やや実践的なプログラミングを通して学ぶ
 情報の授業 アルゴリズムとプログラム(1) Go.Ota.
アルゴリズムとデータ構造 第3章 ヒープ 6月10日分
アルゴリズムとデータ構造1 2005年7月1日
アルゴリズムとデータ構造 2010年7月26日
アルゴリズムとデータ構造 はじめに 第1章 アルゴリズムと計算量
プログラミング 4 探索と計算量.
プログラミング 3 スタックとキュー.
プログラムの基本構造と 構造化チャート(PAD)
アルゴリズムとデータ構造 2011年7月8日課題の復習
6.データ構造入門 6-1.連結リスト(Linked List) 6-2.スタック(Stack) 6-3.キュー(Queue)
15.cons と種々のデータ構造.
アルゴリズムとデータ構造 第2章 リスト構造 5月20日分
アルゴリズムとデータ構造 第3章 ヒープ 5月31日分
情報知能学科「アルゴリズムとデータ構造」
アルゴリズムとデータ構造 第3章 ヒープ 5月31日分の復習
アルゴリズムとプログラミング (Algorithms and Programming)
データ構造とアルゴリズム (第5回) 静岡大学工学部 安藤和敏
基本情報技術概論(第10回) 埼玉大学 理工学研究科 堀山 貴史
データ構造とアルゴリズム (第3回) 静岡大学工学部 安藤和敏
プログラミング入門 電卓を作ろう・パートI!!.
アルゴリズムとプログラミング (Algorithms and Programming)
アルゴリズムとデータ構造1 2008年7月24日
サブゼミ第7回 実装編① オブジェクト型とキャスト.
アルゴリズムとデータ構造1 2006年6月23日
アルゴリズムとデータ構造 2013年7月1日
アルゴリズムとデータ構造 2013年7月8日
2009/11/27 グラフ (1) 第9講: 平成21年11月27日 (金) 4限 E252教室 コンピュータアルゴリズム.
アルゴリズムとデータ構造 第2章 リスト構造 5月24日分
情報処理Ⅱ 第7回 2004年11月16日(火).
アルゴリズム入門 (Ver /10/07) ・フローチャートとプログラムの基本構造 ・リスト ・合計の計算
ヒープソート.
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
Javaとは Javaとはオブジェクト指向言語でJava VM(Java仮想マシン)と呼ばれるプログラム上で動作します。
データ構造と アルゴリズムI 第三回 知能情報学部 新田直也.
情報処理Ⅱ 2005年11月25日(金).
プログラミング入門2 第5回 配列 変数宣言、初期化について
計算技術研究会 第5回 C言語勉強会 関数(function)を使う
アルゴリズムとデータ構造 2012年7月9日
Presentation transcript:

アルゴリズムからプログラムへ GRAPH-SEARCH 情報知能学科 白井 英俊

GRAPH-SEARCH (教科書、p.62) 入力: グラフGを表す隣接頂点関数Tと初期頂点v0 出力: Gのすべての頂点のリスト 手続き: 頂点を入れるための入れ物A,B(初期値は空) v0をAとBに入れる Aから一つの頂点vを取り出し「T(v)に属す頂点でBに入っていないものがあれば、AとBに入れる」 Aが空ならBを出力して終了。そうでなければ3へ。 3の実現方法はいろいろ---p.62の方式を次に示す 2

グラフの表現 グラフGは頂点の集合Vと枝の集合Eの組合せ V={v1, v2, v3,v4} E={ {v1, v2 }, {v2 , v3}, {v1, v3 }, {v3, v4 } } ここでは頂点v0から始めてVを求めることが目的。Gは上記の形で与えられてはおらず、 「グラフGを表す隣接頂点関数Tと初期頂点v0」で与えられている ⇒ 頂点ごとに、それにTという関数によって隣接する頂点の集まりが得られる、と考える

ひとつの表現方法 V={v1, v2, v3,v4} E={ {v1, v2 }, {v2 , v3}, {v1, v3 }, {v3, v4 } } に対しては、 T(1)={2, 3} T(2)={1, 3} T(3) ={1,2,4} T(4)={3} という関数Tを作る v2 v4 v1 v3

集合と配列 たとえば T(1)={2, 3} をRubyで表現(実現)することを考えよう。 しかし、「データの集まり」を表すには「配列」でもよさそう。。。 とすると、E={ {v1, v2 }, {v2 , v3}, {v1, v3 }, {v3, v4 } }は T=[nil, [2,3], [1,3], [1,2,4], [3] ] で表現できそう T(1) T(2) T(3) T(4)

別な方法 今までの講義では、木をClassを用いて表してきた 今示した配列を使う方法とは相容れない… class GraphNode def initialize(x="") @label=x @edges=Array.new end # def 一つ一つの頂点を表現 頂点のラベル 隣接頂点を要素とする配列

Classによるグラフの表現 Classを用いてグラフの頂点を表すとすれば、 頂点v1に対する隣接頂点関数の値 T(v1)は、 v1.edges で与えられる。つまり、 v1.edges ⇒ [v2, v3] v2 v4 v1 注意: edgeとは「辺」のこと。でも上では 隣接頂点を要素とする配列が得られる。 このままだと名前が誤解を与えかねないので、 adjacentNodes (実質はedgesと同じ) によって隣接頂点関数(T)の代わりをさせることとする つまり、上の例では v1.adjacentNodes によってv1の隣接頂点が得られる v3

GRAPH-SEARCH (教科書、p.62) 入力: グラフGを表す隣接頂点関数Tと初期頂点v0 出力: Gのすべての頂点のリスト 手続き: 頂点を入れるための入れ物A,B(初期値は空) v0をAとBに入れる Aから一つの頂点vを取り出し「T(v)に属す頂点でBに入っていないものがあれば、AとBに入れる」 Aが空ならBを出力して終了。そうでなければ3へ。 3の実現方法はいろいろ---p.62の方式を次に示す 8

関数graphSearch アルゴリズムの記述から def graphSearch(v0) end という大まかな構造が見える (1) 入れ物A, Bを用意する (2) 入れ物A, Bにv0を入れる (3) Aから一つの頂点vを取り出し「T(v)に属す頂点 でBに入っていないものがあれば、AとBに入れる」 (4) Aが空ならBを出力して終了。そうでなければ(3)へ

(1)入れ物A, Bを用意する プログラムにおいて「入れ物」とは何か? 答え:データを記憶するための変数、配列、などのこと ただし、変数で記憶できるのは高々1つ 配列やハッシュ、スタック、キューなど、いろいろな要素を記憶できるものが「入れ物」と呼ぶのにふさわしい

(1)入れ物A, Bを用意する(続き) どういう用途の「入れ物」かが選択の鍵 (3)で「Bに入っているかどうか」が重要 なぜ重要かは(3)で説明 どういう用途の「入れ物」かが選択の鍵 (3)で「Bに入っているかどうか」が重要 ⇒Bの中の要素を「すばやく」検索できるデータ構造が向いている⇒それならハッシュ! でも、どうやって「用意する」?

(1)入れ物A, Bを用意する(続き) 配列、ハッシュ、キュー、スタックなどの「データ構造」は、newメソッドでインスタンスを作る 例 配列: Array.new ハッシュ:  Hash.new これらはRubyの組み込み (最初からある)のデータ構造 スタック: Stack.new キュー: Queue.new 木: … これらはRubyの組み込みではない 別途Classの宣言が必要 しかし、newによってインスタンスを 作ることは同じ

キューとスタックの準備 キューのクラス queueX.rb スタックのクラス stackX.rb class Queue attr_accessor :head, :tail def enqueue(item) … end def dequeue … end def empty … end def find(item) … end end class Stack            attr_accessor :data, :top def push(item) … end def popup … end def empty … end end

GRAPH-SEARCHプログラム(queue版) def graphSearchQ(v) # キューを用いたグラフ探索 check = Hash.new(false) # 頂点が記録済みかどうかのチェック用(B) agenda = Queue.new # 調査すべき頂点の記憶用(A) agenda.enqueue(v) # 始点vをAに入れる(enqueue) result = [v]          # 始点vを訪問リストに入れる check[v] = true #始点vを記録済みとしてチェック while (! agenda.empty)       # 調査すべき頂点がある限り繰り返し v = agenda.dequeue # 次に調査すべき頂点(v)を取出す for w in v.adjacentNodes # vに隣接する頂点それぞれ(w)に対し if (! check[w]) # wが記録済みでなければ agenda.enqueue(w) # wを調査すべき頂点に登録(A)         check[w]=true # wを記録済みとして登録(B) result << w # wを訪問リストの末尾に入れる end # if end # for end # while return result # (すべての頂点を調査したので) 訪問リストを返す end # def 14

関数graphSearch アルゴリズムの記述から def graphSearch(v0) end という大まかな構造が見える (1) 入れ物A, Bを用意する (2) 入れ物A, Bにv0を入れる (3) Aから一つの頂点vを取り出し「T(v)に属す頂点 でBに入っていないものがあれば、AとBに入れる」 (4) Aが空ならBを出力して終了。そうでなければ(3)へ

(2) 入れ物A, Bにvを入れる では、スタックやキューに入れるには? 入れ物とは、配列、ハッシュ、スタック、キュー等のこと これにvを入れるとは? 配列の場合:インデックスを指定するか、後に付加えるのが普通。 例:arrを配列、iをインデックス用の変数とすれば、 arr[i]=v や arr << v など。 ハッシュの場合:vそのものをインデックスとして、値にnil以外を与える. 例: h をハッシュとすれば、 h[v] = true 参考: ハッシュの場合、「入っていないもの(たとえばw)を参照しようとするとnilが値となる. h[w] -> nil では、スタックやキューに入れるには?

(2) 入れ物A, Bにvを入れる(続) スタックやキューに入れるには? そう、もう学んだこと… スタック: stをスタックとすれば、st.push(v) キュー: qをキューとすれば、 q.enqueue(v) 質問: スタックやキューの中のものを取り出すには? 答: スタック: stをスタックとすれば、st.popup キュー: qをキューとすれば、 q.dequeue

GRAPH-SEARCHプログラム(queue版) def graphSearchQ(v) # キューを用いたグラフ探索 check = Hash.new(false) # 頂点が記録済みかどうかのチェック用(B) agenda = Queue.new # 調査すべき頂点の記憶用(A) agenda.enqueue(v) # 始点vをAに入れる(enqueue) result = [v]          # 始点vを訪問リストに入れる check[v] = true #始点vを記録済みとしてチェック while (! agenda.empty)       # 調査すべき頂点がある限り繰り返し v = agenda.dequeue # 次に調査すべき頂点(v)を取出す for w in v.adjacentNodes # vに隣接する頂点それぞれ(w)に対し if (! check[w]) # wが記録済みでなければ agenda.enqueue(w) # wを調査すべき頂点に登録(A)         check[w]=true # wを記録済みとして登録(B) result << w # wを訪問リストの末尾に入れる end # if end # for end # while return result # (すべての頂点を調査したので) 訪問リストを返す end # def 18

関数graphSearch アルゴリズムの記述から def graphSearch(v0) end という大まかな構造が見える (1) 入れ物A, Bを用意する (2) 入れ物A, Bにv0を入れる (3) Aから一つの頂点vを取り出し「T(v)に属す頂点 でBに入っていないものがあれば、AとBに入れる」 (4) Aが空ならBを出力して終了。そうでなければ(3)へ

(3) Aから一つの頂点vを取り出し... Aにはスタックかキューを使う(指定済み) Aから要素を取り出してそれをvとする、ということ Aがスタックの場合(もうわかっていますね?) スタックから要素を取り出すには… だからstをスタックとすれば v= Aがキューの場合 キューから要素を取り出すには… だからqをキューとすれば v= 穴埋め!

…「T(v)に属す頂点でBに入っていないものがあれば、AとBに入れる」 (3)の前半でAから取り出された頂点をv‘とし、T(v’)の一つの要素をvとしておく vがBに入っているかいないかは、どうやってわかる? Bはハッシュを使うことにした(スライド11枚目)  …それは、検索が速いから。でもどうやって? 答え:hをハッシュとすれば h[v]の値でわかる 説明: hに要素(例えばw)を入れる時に、h[w]=true として値を設定。だから「true以外」の値が返されれば、「入っていない」ことがわかる

ここで、条件式について一言 条件式は、 if 条件式 … elsif 条件式 … end や、 while 条件式… end などで用いられる Rubyの場合、条件式がtrueを返すと、その条件式が「成り立った」ことになり、たとえばifではその次のプログラムが、whileではendまでのプログラムが実行される たとえば、 x==3はxが3ならtrue、そうでなければfalseを返す 条件式の値がfalse(やnil)の場合、その条件式は「成り立たなかった」ということになる

vがBに入っているかいないか なぜこれが重要なのだろうか? このチェックをしないプログラムを書いた人からのコメント:「キューにものが入りすぎてQueue overflowが表示されてしまう」 グラフは(木と異なり)頂点から枝をたどっていくと、その頂点に戻る可能性がある。つまり迷路でいえば同じところをぐるぐる回ってしまう可能性がある。これを何とかしたい! → 頂点をAにいれるための条件として「すでに訪れたことがない」 (Bに入っていない)とすればよい

(3)をまとめて書くと。。。 おっと!v.adjacentNodesは配列だ! A(キューqとする)から一つの頂点v1を取り出し 確認: 「T(v)に属す頂点でBに入っていないものがあれば、AとBに入れる」は大丈夫ですね?(「AとBに入れる」方法はもうわかっている?) 以下ではAをキューとします(スタックの場合は自分で書いてみてください) v.adjacentNodesでT(v)の代わりをする A(キューqとする)から一つの頂点v1を取り出し v1 = q.dequeue 「T(v1)に属す頂点vでB(hとする)に入っていないものがあれば、AとBに入れる if (h[v1.adjacentNodes] != true) q.enqueue(v.adjacentNodes); h[v.adjacentNodes]=true end おっと!v.adjacentNodesは配列だ!

(3)をまとめて書くと。。。 ちょっと焦りすぎました 頂点vに隣接する頂点集合が v.adjcentNodes (T(v)と同じ)で得られるのですが、これは頂点を要素とする配列を返す これをそのままAやBに入れてはいけません(AやBに入れるのは「頂点」と決めているので) ここは「繰り返し」(forやwhile)を使って、 v.adjcentNodesの要素を一つ一つBにないことを確認して次々AやBに入れる必要があります

GRAPH-SEARCHプログラム(queue版) def graphSearchQ(v) # キューを用いたグラフ探索 check = Hash.new(false) # 頂点が記録済みかどうかのチェック用(B) agenda = Queue.new # 調査すべき頂点の記憶用(A) agenda.enqueue(v) # 始点vをAに入れる(enqueue) result = [v]          # 始点vを訪問リストに入れる check[v] = true #始点vを記録済みとしてチェック while (! agenda.empty)       # 調査すべき頂点がある限り繰り返し v = agenda.dequeue # 次に調査すべき頂点(v)を取出す for w in v.adjacentNodes # vに隣接する頂点それぞれ(w)に対し if (! check[w]) # wが記録済みでなければ agenda.enqueue(w) # wを調査すべき頂点に登録(A)         check[w]=true # wを記録済みとして登録(B) result << w # wを訪問リストの末尾に入れる end # if end # for end # while return result # (すべての頂点を調査したので) 訪問リストを返す end # def 26

関数graphSearch まだ完成ではありません。(4)を忘れています。 def graphSearch(v0) end という大まかな構造が見える (1) 入れ物A, Bを用意する (2) 入れ物A, Bにv0を入れる (3) Aから一つの頂点vを取り出し「T(v)に属す頂点 でBに入っていないものがあれば、AとBに入れる」 (4) Aが空ならBを出力して終了。そうでなければ(3)へ

(4) Aが空ならBを出力して終了。そうでなければ(3)へ 1に対しては、hをハッシュとすれば、h.keysによって、記憶された頂点を配列にして返してくれます(?!) 2に対しては、A(スタックかキュー)が空かどうかを判定するメソッドを使います(そのためにemptyというメソッドをあらかじめ定義しておいた)

(4) Aが空ならBを出力して終了 「何を返すか」は本当は大事な問題 B(ハッシュhとする)に入っている頂点の集合を返すだけなら、h.keys でよいが。。。 ここでは、どの頂点がどういう順番で入ってきたかも示したい(と思いません?) だから、resultという配列を別に用意して、それを返すようにした 最終形は次のスライド…

GRAPH-SEARCHプログラム(queue版) def graphSearchQ(v) # キューを用いたグラフ探索 check = Hash.new(false) # 頂点が記録済みかどうかのチェック用(B) agenda = Queue.new # 調査すべき頂点の記憶用(A) agenda.enqueue(v) # 始点vをAに入れる(enqueue) result = [v]          # 始点vを訪問リストに入れる check[v] = true #始点vを記録済みとしてチェック while (! agenda.empty)       # 調査すべき頂点がある限り繰り返し v = agenda.dequeue # 次に調査すべき頂点(v)を取出す for w in v.adjacentNodes # vに隣接する頂点それぞれ(w)に対し if (! check[w]) # wが記録済みでなければ agenda.enqueue(w) # wを調査すべき頂点に登録(A)         check[w]=true # wを記録済みとして登録(B) result << w # wを訪問リストの末尾に入れる end # if end # for end # while return result # (すべての頂点を調査したので) 訪問リストを返す end # def 30

出欠課題(その2) 本日のウェブページから、graphSearch.rb をダウンロードする そこには queue版のGraph-Searchプログラムが載っている (1) それを動かしてみる。v6を出発点とした時の頂点のリストが表示されるので、自分の解答と比較する(最後にエラー表示されるが無視すること) (2) queue版にならってstack版を作る(graphSearch.rbのgraphSearchS関数を完成させる) (3) 完成したら、v6を出発点とした時の頂点のリストが表示されるので自分の解答と比較する