アルゴリズムとデータ構造 第1章 アルゴリズムと計算量 4月15日 情報知能学科 白井英俊 アルゴリズム+データ構造=プログラム • 本講義は「プログラミング」の講義ではない。 復習 • 使用するプログラミング言語に(あまり)依存しな い、いろいろな種類のプログラムを作るための 「基本的な知識と技術の習得」 が本講義の目的 • その基本知識と技術が アルゴリズム と データ構造 2 例えて言えば... 復習 • アルゴリズムは料理のレシピ 入力 ... 料理の材料 (小麦粉、キャベツなど) 出力 ... 料理 (お好み焼き、カレーライスなど) 料理を作るには材料をどんな手順でどのように調理 するか、が必要 ... アルゴリズム • データ構造は、料理のための道具 • レシピが読めても、料理を作れないのでは意味が ない!⇒ ちゃんと動くプログラムを書けなければ 意味がない。。。さらに効率性も大事。。。 3 前回の問題:配列を使ったプログラム • 問題1.変数aには配列が値として入っている。 たとえば、 a= [ 1, 2, 3,“a”,“ab”,“abc” ] ここで、a[1]とa[2]の値を取り換えるコード(プ ログラムの断片)は? • 問題2.変数aには配列が値として入っている。 aの偶数番目の要素と奇数番目の要素をす べて取り換えるコードは? たとえば、 a= [ 1, 2, 3,“a”]ならa= [ 2,1, “a”,3]となる 4 問題1. a[1]とa[2]の値を取り換える 例題: a= [ 1, 2, 3,“a”,“ab”,“abc” ] 一案: a= [ 1, 3, 2, “a”,“ab”,“abc” ] これは確かに例題には適用できるが aがどんな値でもできるものではない --- 解として不適 問題1の関連問題 • 変数x と y の値を取り替える(swap) x=3, y=5ならば、これによりx=5, y=3になる x,yの値に関係なくちゃんと動くことが条件 ; は複数のコードを一行で書くためのもの ダメな解答: x = y ; y = x その理由が即答できない人は『よく考えてみよう』 正解:使われていない変数(ここではzとする)を用いる z = x; x = y ; y = z 別解(Rubyの特性): x, y = y, x プログラミング言語における=の意味 プログラミング言語の等号(=)の左辺と右辺は だから、アルゴリズムの記述では代入に←を 意味が異なる 使ったりする *例えば、数学的には、 x = x+1 はありえない = の右側は「値」(変数なら、それが記憶している数 値や文字列) =の左側は「記憶場所」 * x = x+1 x ← x+1 と書いた方がしっくりくる? xの値に1を足した結果を記憶場所xに記憶 *a[1] = a[2] a[2]の「値」をa[1]の場所に書き込む 問題1. a[1]とa[2]の値を取り換える 例題: a= [ 1, 2, 3,“a”,“ab”,“abc” ] 一案: 別解: 使われていない変数xを用いて x = a[1] ; a[1] = a[2] ; a[2] = x a[1], a[2] = a[2], a[1] Rubyでは多重代入と呼びます 問題2.配列aの偶数番目の要素と奇数番目の要素を すべて取り換えるコード a= [ 1, 2, 3,“a”] なら a= [ 2,1, “a”,3] となる ダメな解答(1): a[2n] = a[2n+1]; a[2n+1] = a[2n] ダメな解答(2): a[0], a[1], a[2], a[3] = a[1], a[0], a[3], a[2] 配列の要素の個数だけ、「値の取替」の必要がある ⇒ 繰り返し を使う 例題だけではなく、より広い範囲で(「一般 解答例: i = 0 的」に)適用できる解を求める! while ( i < a.length) a[i], a[i+1] = a[i+1],a[i] i = i+2 end # while アルゴリズムの例 入力 • 問題(教科書p.1): n個の実数x1, x2, …, xnが 与えられて、その中の最大値を求める 出力 • 素朴版(p.1) x1, x2, …, xnを順にみていく。その途中でいつ も、「それまでに見た数の最大値」を覚えてお く。最後まで見終わったとき、「それまでに見 た数の最大値」が求めるべき最大値 10 アルゴリズムの例(続) • より明確な記述(p.2) 入力: 正の整数nとn個の実数値x1, x2, …, xn 出力: x1, x2, …, xnの中の最大値 手続き: 「変数y に x1の値を代入する」 1. y ← x1 (yは「それまでの最大値」を記録) 2. i = 2,3,…,nに対して順に次を行う: xi > y ならば y ← xi 3. yを出力する 11 アルゴリズムの例(続) MAXはアルゴリズムの名前。続けて、入 力である「引数」を書く。だからnが必要 • 「プログラム」風(p.3) Algorithm MAX(n, x1, x2, …, xn): 入力(Input): 正整数 n と n 個の実数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中の最大値 記法の注意:ここではプログラムの 手続き(Procedure): 命令の後に「;」を書く 1. y ← x1 ; ここでの記法についてはウェブページの 「教科書のアルゴリズムの記述とRUBY言 2. for i ← 2 until n do 語の対応」参照 if xi > y then y ← xi ; アルゴリズムにおいて「出力する」とは、 printするという意味でなく、returnによって 3. return y; 値を「返す」ということ 12 アルゴリズムの検証 •このアルゴリズムがちゃんと動くかは (1)頭で考え、手を動かす (2)アルゴリズムを反映したプログラムを作り、 適切な入力を与えて動かし、結果を調べる ことで検証する、つまり、正しいかどうかを確認 する 13 アルゴリズムからRubyプログラムへ • 2ページと3ページのどちらの記述でもよいが それに基づいてRubyプログラムを書く • それにはRuby言語の書き方を知らないといけ ない… 君たちはちゃんとマスターしていると信じたい! 14 アルゴリズムからRubyプログラムへ(続 き:代入と配列の使用) • 入力は、変数nとxとする。 ただし、nの値は正整数、 xの値は実数を要素とす Algorithm MAX(n, x1, x2, …, xn): 入力(Input): 正整数 n と n 個の実 る配列 数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中 • 手続きの最初をRubyで の最大値 手続き(Procedure): 書くのは簡単… 1. y ← x1 ; ← はアルゴリズムの記述における 『代入』の書き方。Rubyプログラムで は=を使う y = x[1] 2. for i ← 2 until n do if xi > y then y ← xi ; 3. return y; 実はこれだと問題があるが、今はこれで進める 15 アルゴリズムからRubyプログラムへ(続 き:繰り返し) 2番目の手続きは「繰返し」 Rubyには、『繰り返し』のメソッ ドはいろいろある: while, for, downto, times, … (適切なものを選べる ようにしよう!) ここではforを使うのが適切 Algorithm MAX(n, x1, x2, …, xn): 入力(Input): 正整数 n と n 個の実 数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中 の最大値 手続き(Procedure): 1. y ← x1 ; 2. for i ← 2 until n do if xi > y then y ← xi ; 3. return y; 実はこれだと問題があるが、今はこ れで進める 書き方: for i in 2..n 繰り返される処理 end 16 アルゴリズムからRubyプログラムへ(続 き:条件分岐) •「繰り返される処理」は Algorithm MAX(n, x1, x2, …, xn): 正整数 n と n 個の実 ここでは「条件分岐」である。入力(Input): 数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中 Rubyの条件文の書き の最大値 方: 手続き(Procedure): if (条件式) 「条件式が成り立つ時の処理」 else 「条件式が不成立の時の処理」 end 1. y ← x1 ; 2. for i ← 2 until n do if xi > y then y ← xi ; 3. return y; elseと「条件式が不成立の時の処理」は、なくても可 17 アルゴリズムからRubyプログラムへ(2番 目の処理のまとめ) • 2番目の処理をまとめ Algorithm MAX(n, x1, x2, …, xn): 入力(Input): 正整数 n と n 個の実 ると以下のように書け 数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中 る: の最大値 手続き(Procedure): for i in 2..n 1. y ← x1 ; 2. for i ← 2 until n do if (x[i] > y) if xi > y then y ← xi ; 3. return y; y = x[i] end #if これらは本来必要ないものだが、 end #for プログラムを書くときには役に立つもの 18 アルゴリズムからRubyプログラムへ(3番 目の処理) •3番目の return y はyの値を返すも の。 •でも、どこに返すのか? •ここでの選択 (1) 画面に表示する(printを使う) (2) アルゴリズムMAXの結果として MAXを呼び出したモノに返す Algorithm MAX(n, x1, x2, …, xn): 入力(Input): 正整数 n と n 個の実 数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中 の最大値 手続き(Procedure): 1. y ← x1 ; 2. for i ← 2 until n do if xi > y then y ← xi ; 3. return y; ここでは(2)を考える。 でも、「アルゴリズムMAXの結果とし て返す」とはどういうこと? 19 アルゴリズムからRubyプログラムへ (関数/メソッド) •「アルゴリズムMAXの結 Algorithm MAX(n, x1, x2, …, xn): 入力(Input): 正整数 n と n 個の実 果として返す」とは、 数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中 Ruby の関数(メソッド)を の最大値 手続き(Procedure): つくり、それにmaxと名を 1. y ← x1 ; つけ、maxの関数の処理と 2. for i ← 2 until n do if xi > y then y ← xi ; して1から3までの処理を 3. return y; やらせる、 Rubyでは関数名を大文字にできないのでmaxとする ということ return文が大事 20 アルゴリズムを関数として表現 関数: 入力を取り、それに基づいて一連の手続き (計算)を行い、その結果を出力する(返す)、という プログラムの単位 21 関数について 関数定義の引数(xとy) は仮引数という。『定義』 のための仮のもの • 関数名を func とすると 関数の定義の書き方: def func(x,y) … プログラム(「コード」とも)… …どこかにreturn文が必要… end 関数呼出の引数(3と5) は実引数。計算に用い 関数の呼び出し: られる実物。 ans = func(3,5) 22 関数の利点 • 一連の手続きに『名前』をつけ、入力と出力を 明示することで、その手続きが何をしているか が明確になる • プログラムを関数に分けることで、そのプログ ラムの構造が明確になる • 関数ごとにデバッグをすることで、誤りが発見 しやすい。また変更も容易になる • 同じような処理をする場合に、冗長性が減る • 『再帰』は関数を使わないと書けない 23 アルゴリズムからRubyプログラムへ (完成) • 関数は以下のようにdef を使っ て定義する: def max(n,x) y = x[1] for i in 2..n if (x[i] > y) y = x[i] end # if end # for return y end # def Algorithm MAX(n, x1, x2, …, xn): 入力(Input): 正整数 n と n 個の実 数x1, x2, …, xn 出力(Output): x1, x2, …, xnの中 の最大値 手続き(Procedure): 1. y ← x1 ; 2. for i ← 2 until n do if xi > y then y ← xi ; 3. return y; Rubyのプログラムでは、endがdefやforやifなどと 対応するため対応関係がわかりにくい。そこで、 インデント(段付け)をして対応が分かるように書く 24 実際に検証してみよう • 先のプログラムは単に「アルゴリズム」をプログ ラムに直しただけ。 • それがちゃんと期待通りに計算するかは、 (1) 具体的な(入力)データを与える (2) アルゴリズムが返した結果を表示させる ということが必要。 25 実際に検証してみよう(続き) 具体的には以下のようなプログラムを作る: # maxアルゴリズムの検証 def max(n,x) y = x[1] for i in 2..n if (x[i] > y) y = x[i] end # if end # for return y end # def # ここからが検証用のデータ print max(10, [0.3, 0.1, 0.7, 0.9, 1, 0.2, 0.5, 0.8, 0.4, 1.0]) この内容をmax.rbとして、実行させてみると… 26 動かない… • ちゃんと書いたはずなのだが、以下のようなエ ラーメッセージが出て動かない… max.rb:5:in `max': undefined method `>' for nil:NilClass (NoMethodError) from max.rb:4:in `each' from max.rb:4:in `max' from max.rb:12 27 めげない! • エラーメッセージに理由が書いてある。それを 読んで、理由を理解して、直してみよう。 • 講義用のウェブページにある「Rubyのエラー メッセージとその対処 」を読んでみよう。 • これはそのうちのどこにあたるか? 28 プログラムの修正 • 「(5) 未定義(undefined)なメソッド(method)/関数に よるエラー 」に相当することが分かる • 解釈:この場合は、5行目に使われている> という 「未定義なわけがない」演算子がやり玉にあがって いる。 ここは、つまり「for nil:NilClass」が問題。つまり 「nil」には > が使えない、と言っている。 • 対処法:このようなエラーが起きるのは、配列の要素 の指定が間違っているか、要素の値の初期化(例え ば0にする)ことを忘れている場合が多い 29 プログラムの修正(続き) • わかっただろうか?配列の要素の指定に問題があったので ある。 • プログラムを見てみよう: def max(n,x) yに配列の先頭の要素を与えた「つもり」 y = x[1] for i in 2..n 配列の2番目の要素から最後の要素ま if (x[i] > y) で、要素を一つ一つ調べている「つもり」 y = x[i] end # if Rubyの配列のインデックスは、いくつから始まり、いく end # for つが最後の要素をさすものだったかな? return y end # def 30 プログラムの修正(完成) # maxアルゴリズムの検証 def max(n,x) y = x[0] for i in 1..(n-1) if (x[i] > y) y = x[i] end # if end # for return y end # def # ここからが検証用のデータ print max(10, [0.3, 0.1, 0.7, 0.9. 1.1, 0.2, 0.5, 0.8, 0.4, 1.0]) 実行させた結果は… 31 参考 • プログラミング言語Cで書くと: float max(int n, float x[ ]) { int i; float y=x[0]; for (i=1; i < n; i++) { if (x[i] > y) { y=x[i]; } } return y; } 実際には以下のプログラムに挿入 #include <stdio.h> #define Num 10 /* ここにいれる */ int main() { static float data[Num] = { 0.3, 0.1, 0.7, 0.9. 1.1, 0.2, 0.5, 0.8, 0.4, 1.0}; printf(“%f\n”, max(Num,data)); } 32 4月15日の課題 2. 配列aのx番目の要素とy番目の要素を取り 換えて得られる配列を返 す関数定義 torikae(a,x,y) 例: a = [1,2,3,4,5] 、とすると torikae(a,1,3)は [1,4,3,2,5] を返す 回答 概略は次のようになる(わからない人はいます か?) 4月15日の課題(2) 2. 配列aのx番目の要素とy番目の要素を取り換えて 得られる配列を返す関数定義torikae(a,x,y) def torikae(a,x,y) z = a[x] 配列aのx番目の要素とy番目の ここに課題を満たすコード a[x] = a[y] 要素を取りかえるコードは? (プログラム)を書く a[y] = z # 最後に配列を返す--- return a end # def 参考: 最初の3行は a[x], a[y] = a[y], a[x] とも書ける 4月15日の課題(3) 3. 2つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並び かえた配列を返す関数の定義seiret2 例: a = [1,2] とすると seiret2(a)は [1,2] を返し、 a = [20,10] とすると seiret2(a)は [10,20] を返 す 回答 概略は次のようになる(わからない人はいます か?) 4月15日の課題(3)続 3. 2つの要素(いずれも数)をもつ配列aの中身を、小 さいものから大 きいものになるよう並びかえた配列 を返す関数の定義seiret2 例: a = [1,2] とすると seiret2(a)は [1,2] を返し、 a = [20,10] とすると seiret2(a)は [10,20] を返す def seiret2(a) 参考: 2行目は if (a[0] > a[1]) 配列aの1番目の要素が2番目の要素より a[1], a[0] = a[0], a[1] a = torikae(a,0,1) とも書ける 大きければ、これらを取りかえるコード! end # if return a # 最後に配列を返す end #def 4月15日の課題(4) 4. 三つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並びか えた配列を返す関数seiret3の定義 例: a = [1,2,3] とすると seiret3(a)は [1,2,3] を返し、 a = [20,10,30] とすると seiret3(a)は [10,20,30] を返し、 a = [33,22,11] とすると seiret3(a)は [11,22,33] を返す 回答 概略は次のようになる(わからない人はいますか?) 4月15日の課題(4)続 4. 三つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並びか えた配列を返す関数seiret3の定義 def seiret3(a) ここに課題を満たすコード これはちょっと考えないといけない… (プログラム)を書く end # def 4月15日の課題(4)続 4. 三つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並びか えた配列を返す関数seiret3の定義 考え方: 3つの要素(仮にx,y,zとする)を小さい ものから大きいものに並び替えるとすれば6通り の可能性がある( 3! = 6) (1) x ≧ y ≧ z (2) x ≧ z ≧ y (3) y ≧ x ≧ z (4) y ≧ z ≧ x (5) z ≧ x ≧ y (6) z ≧ y ≧ x 4月15日の課題(4)続 4. 三つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並びか えた配列を返す関数seiret3の定義 考え方: まずxとyの大きさを比べる… は x ≧ y の場合の可能性 (1) x ≧ y ≧ z (2) x ≧ z ≧ y (3) y ≧ x ≧ z (4) y ≧ z ≧ x (5) z ≧ x ≧ y (6) z ≧ y ≧ x 4月15日の課題(4)続 4. 三つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並びか えた配列を返す関数seiret3の定義 考え方: まずxとyの大きさを比べる… 次にx と zの大きさを比べる (1) x ≧ y ≧ z (2) x ≧ z ≧ y 最後にy と zの大きさを比べて(1)か(2)が決まる (5) z ≧ x ≧ y 4月15日の課題(4)続 4. 三つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並びか えた配列を返す関数seiret3の定義 if (x >= y) # xとyの大きさを比べる… if ( x >= z) # 次にx と zの大きさを比べる if (y >= z) # 最後にy と zの大きさをる return [z,y,x] # x>= y>= z else return [y,z,x] # x>= z >= y end #if (y>=z) これにならって他の場合を書きこむ! end # if (x>=z) end # if (x>=y) 4月15日の課題(4)続 4.三つの要素(いずれも数)をもつ配列aの中身 を、小さいものから大 きいものになるよう並びか えた配列を返す関数seiret3の定義 完成させよう! 練習問題(1) • n個の実数x1, x2, …, xnが与えられて、その中の 最小値を求める (1)アルゴリズムを書く (2)プログラムを書く (3)適当なデータを与えて、プログラムを実行して みる。そして最小値を返すことを確認する。 44 練習問題(2) • n個の実数x1, x2, …, xnが与えられて、その中の 2番目に大きな数を求める (1)アルゴリズムを書く (2)プログラムを書く (3)適当なデータを与えて、プログラムを実行して みる。そして2番目に大きな数を返すことを確認 する。 45 出欠レポート • ここまでの作業内容を出欠レポートとして書く • 次は、宿題について 課題(1と2は提出を求める宿題) • プログラムを書くために、Rubyを復習しよう • 次の問題にチャレンジしてみよう (1)最古のアルゴリズム(ユークリッドの互除法)をプロ グラム化する (2) ファイルから実数を読み込み、その中の最大値を 答える(つまり、数がファイルに書いてある) (3) 最大値と最小値からなる配列を返す (4) 最大値と最小値からなる配列を答えるだけではな く、それらが何番目の要素であるかも答える 宿題の提出期限は4月21日(木曜日)まで。あて先はウェブページ 参照 47 プログラム課題提出の注意 • (これこれの問題を解く)「プログラムを書け」、もしく は「アルゴリズムを書け」という課題が多く出される • この時に求められているのは「プログラム(やアルゴリ ズム)」だけではないことに注意 • 要求事項がちゃんと満たされていることも示す(検証 する)必要がある • そのために、プログラムの中身、その実行例(複数個 の例題)、実行結果の吟味(ちゃんと目的を果たして いるかどうか)を示す 48 ユークリッドの互除法 入力:正整数p, q 出力:pと q の最大公約数 手続き: 1. q > p ならば、 p と q の値を入れ替える 2. r ← (p の q による剰余) 3. r = 0 ならば、q が最大公約数(終了) さもなくば、 p ← q, q ← r として、1へ。 p = 72, q = 30 として、試してみよう 49 基本的な用語の確認 整数xが整数aの約数 ⇔ aがxで割り切れる • aとbの公約数:aもbも正整数であることが前提 aの約数とbの約数で共通する数のこと 例: 30 の約数は 1, 2, 3, 5, 6, 10, 15, 30 72の約数は 1, 2, 3, 4, 6, 8, 9, 12, …, 36, 72 • aとbの最大公約数(GCD):aとbに共通する公約 数のうち最大のもの 30と72の最大公約数(これをGCD(30,72)と書く) は… 50
© Copyright 2024 ExpyDoc