440 likes | 555 Views
オブジェクト・プログラミング. 第 7 回 アルゴリズム. 前回多かった質問. 要素が少ないとき、 0ms と出てしまう。 この方法では ms 以下の時間は計れないので、仕方がないです。コンピュータは ms より小さい単位で動いています。 毎回かかる時間が微妙に違う Windows がマルチタスクなためです。 よって、この手の考察を行う場合、何回かの平均を取るべきです。(そんなプログラムは簡単に書けるでしょう!?). 前回の経験より. リニアサーチを使うのは次の場合に限られる。 要素が少ない時 ( 数十個程度 ) ソートされていないデータに対する時
E N D
オブジェクト・プログラミング 第7回 アルゴリズム
前回多かった質問 • 要素が少ないとき、0msと出てしまう。 • この方法ではms以下の時間は計れないので、仕方がないです。コンピュータはmsより小さい単位で動いています。 • 毎回かかる時間が微妙に違う • Windowsがマルチタスクなためです。 • よって、この手の考察を行う場合、何回かの平均を取るべきです。(そんなプログラムは簡単に書けるでしょう!?)
前回の経験より • リニアサーチを使うのは次の場合に限られる。 • 要素が少ない時(数十個程度) • ソートされていないデータに対する時 • だけどバイナリサーチはソート(順番に並んでいる)されていなければ、使えない! • 今日は、ソートを学びます。
今週の目標 • ソートのアルゴリズムについて説明することができる。(教科書第3章) • Javaでソートをするプログラムが書けるようになる。 • Javaのテクニックを学ぶ • ランダムに商品情報を作る。 • ファイルの入出力を行う。
今日は簡単なソートをやります • ソートは重要で時間のかかる処理なので、これまでにも多くのコンピュータ科学者が研究に取り組み、いくつかの高度な方法も発明されています。 その中で比較的簡単な3つの方法を紹介します。 • バブルソート • 選択ソート • 挿入ソート これらのテクニックは素朴で、遅いアルゴリズムですが、勉強する価値はあります。単に分かりやすいだけでなく、データの並び方や数によって、これらの方法のほうが速い場合もあるからです。
バブルソート • アルゴリズム • (1)2つの商品番号を比較する • (2)左側の方の番号が大きければ商品情報を入れ替える。 • (3)右へ一つ移動する。 • ソートの終ってない部分(毎回小さくなる)に対して上のステップを繰り返す • ※配列の一番左の箱の中の番号が最小になるように並べ替える場合です。
入れ替える 入れ替える 入れ替える 入れ替える そのまま そのまま 入れ替える 入れ替える 入れ替える 比較する 比較する 比較する 比較する 比較する 比較する 比較する 比較する 比較する ソート済み 22 3 22 3 51 64 18 18 51 103 103 64 103 103 64 51 103 51 ソート済み 103 22 51 3 64 18 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] 一つ移動する 一つ移動する 一つ移動する 一つ移動する 一つ移動する 一つ移動する 一つ移動する 一つ移動する 一つ移動する もう一度 始めから バブルソート とやっていくと、最後に ソートされます。 ※実際にはオブジェクトが入りますが、今日は見やすいように番号 だけが入っているように見せます。
入れ替える 51 3 3 51 コンピュータでの「入れ替え」(Swap) • 前ページの例では、「入れ替え」を一瞬で行っていましたが、実はコンピュータはそのような操作はできません。 [0] [1]
51 3 コピー コピー コピー 3 3 51 コンピュータでの「入れ替え」(Swap)やり方 • temporaryの箱を利用します。 入れ替え完了! これを何回もやるのは 結構時間かかります。 [0] [1] tmp
バブルソートの効率 • 問題:バブルソートの効率を考えなさい。 • ①比較の回数 • ②入れ替えの回数 を考えなさい。
バブルソートの効率(答え) • ①比較の回数 • 2要素のとき→1回 • 3要素のとき→1+2回 • 4要素のとき→1+2+3回 • N要素のとき→(N-1)+(N-2)+(N-3)…+1回 =N*(N-1)/2=N^2/2回 • ②入れ替えの回数 • 確率的に、比較したうちの半分を入れ替える。 • よって、約N^2/2 /2 = N^2/4回
バブルソートの実装 //バブルソートをするメソッド(ItemInfoFolderクラス) public void bubbleSort(){ for(int i=size-1;i>1;i--){//要素数回始めから作業を繰り返す for(int j=0;j<i;j++){//ソート済みの所まで作業する if(itemInfoArray[j].no > itemInfoArray[j+1].no){ swap(j,j+1);//もし右側のほうが大きかったら入れ替える } } } } ※swapメソッドは次のページ
入れ替え(swap)の実装 //要素を入れ替えるメソッド public void swap(int target1,int target2){ ItemInfo temp;//temporaryの箱を用意する temp = itemInfoArray[target1]; itemInfoArray[target1] = itemInfoArray[target2]; itemInfoArray[target2] = temp; }
選択ソート • アルゴリズム • (1)全商品番号から、一番小さい番号を探す。 • (2)見つかったものと、一番左の商品情報を入れ替える。 • ソートの終ってない部分(毎回小さくなる)に対して上のステップを繰り返す。
比較する 比較する 比較する 比較する 比較する 比較する 比較する 比較する 入れ替える 比較する 入れ替える ソート済み ソート済み 3 18 103 22 103 22 51 3 64 18 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] 最小値を更新する 最小値を更新する 最小値を更新する 最小値を更新する ここからまた同じことをはじめる 最小値が見つかった! 最小値が みつかった 3 5 1 1 選択ソート このようにして、ソートが終わるまで 続けます。 0 最小値の入っている 箱の番号
選択ソートの効率 • ①比較の回数 • 2要素のとき→1回 • 3要素のとき→1+2回 • 4要素のとき→1+2+3回 →バブルソートと同じ。約N^2/2回 • ②入れ替えの回数 • 2要素のとき→1回 • 3要素のとき→2回 • 4要素のとき→3回 →位置の入れ替えはN回ですむ バブルソートより少しだけ速い。
選択ソートの実装 //選択ソートをするメソッド(ItemInfoFolderクラス) public void selectionSort(){ for(int i=0;i<size-1;i++){//要素数回始めから作業を繰り返す int minimum = i;//最小値の配列番号を保存しておく for(int j=i+1;j<size;j++){//ソート済み以降を作業する if(itemInfoArray[j].no < itemInfoArray[minimum].no){ minimum = j;//最小値より小さかったら、新しい最小値に } } swap(i,minimum);//一番左の要素(ソート済み除く)と //最小と入れ替える } }
挿入ソート • アルゴリズム • (1)途中までソートが終わっているとします。(そのほうが理解しやすいので) • (2)ソートが終わっていない中で、一番左にある商品情報をソート済みの商品情報の中で、あるべき位置に挿入します。 • ソートの終ってない部分(毎回小さくなる)に対して上のステップを繰り返します。
ソート済み部分が増えた! コピー 移動 3 3 3 3 18 18 18 18 51 22 51 22 22 64 51 51 51 64 22 64 64 64 22 18 18 18 18 103 103 103 103 [0] [0] [0] [1] [1] [1] [2] [2] [2] [3] [3] [3] [4] [4] [4] [5] [5] [5] [6] [6] [6] [7] [7] [7] [8] [8] [8] [9] [9] [9] コピーする 次に挿入する ターゲット ここだ! 入れるところを探す 次に挿入すべき商品情報 22 これを繰り返します。 挿入ソート ソート済み tmp
挿入ソートの効率 • ①比較回数 • 2要素のとき→1回 • 3要素のとき→1+2回 • 4要素のとき→1+2+3回 • 挿入ソートの場合、上記は最大比較回数で、確率的に、だいたいそれより早く見つかるので、 N^2/2 /2=N^2/4となる。 N2乗のオーダーは変わらないが、バブル、選択ソートより、約2倍速い。 • ②入れ替え回数 • 入れ替えも比較の回数とほぼ同じであるが、挿入ソートでは、入れ替えではなく、隣にコピーするだけであるため、多少効率は良い。
場合によって効率が変わる! • 挿入ソートは、ほとんどソートされているときには、とても効率的。 • 逆に、逆順にソートされている場合、最大の比較回数と移動回数が実行されてしまい、バブルソートと同じ遅さになってしまう。
挿入ソートのアルゴリズム //挿入ソートをするメソッド(ItemInfoFolderクラス) public void insertionSort(){ int target;//挿入する対象 for(target = 1;target<size;target++){ ItemInfo temp = itemInfoArray[target];//対象をコピーする int i=target; while(i>0 && itemInfoArray[i-1].no > temp.no){//挿入場所に 出会うまでシフトしつづける itemInfoArray[i] = itemInfoArray[i-1];//右へシフト i--; } itemInfoArray[i] = temp;//対象を挿入する } }
挿入ソートをあと少しだけ効率化する。 //前ページのプログラムと同じです。 public void insertionSort(){ int target; for(target = 1;target<size;target++){ ItemInfo temp = itemInfoArray[target]; int i=target; while(i>0 && itemInfoArray[i-1].no > temp.no){ itemInfoArray[i] = itemInfoArray[i-1]; i--; } itemInfoArray[i] = temp; } } この行に注目!!
非効率な条件文 • while(i>0 && itemInfoArray[i-1].no > temp.no) • 日本語に直すと、 • ①iが0以上で、 • かつ、 • ②配列の中の商品番号が、対象商品番号より大きい間 となる。 • ようは、挿入すべき場所を探しているのだが、この中で①の条件は、ほとんど成り立たない(後述)にもかかわらず、N^2/4回行われてしまう。これはNが増えたときにボトルネックにになってしまう。何とかして、条件は一つにしたいな。
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] i<0の条件をなくすと何故まずい? ソート済み 18 35 2 46 8 こんな時、iは0以下になってしまい、 -1の配列をアクセスしてしまい、 ArrayIndexOutOfBoundsExceptionが 出てしまいます。 何故かは考えてみよう! 挿入対象
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] 条件をなくしてもエラーをなくす方法 • 番兵くん登場! -1 18 35 2 46 8 [-1] こいつに絶対ありえない小さいの商品番号(この場合、 商品番号は必ず正だとすると-1とか-10000000とかを 入れておく)入れておけば、i>0の条件を抜いても、 絶対に配列からはみ出さない。 番兵くん
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] 番兵用 だけど-1なんていう配列は作れない。 • 0番目を番兵用に空けます。 -1 18 35 2 46 8 ※0番目は番兵用に空けるため、挿入のときなどの プログラムにも工夫が必要になります。
番兵を使うときの注意 • 番兵を使うと、使える配列は一つ少なくなるので注意! • いっぱいにはしないとか、一つ増やした配列を用意するなどの対応を取るのが一般的。 • 今回は配列の一番始めに番兵を立てますが、アルゴリズムによっては、一番最後に番兵を立てる場合もあります。
番兵を利用した場合のwhile文 • 番兵以前 • while(i>0 && itemInfoArray[i-1].no > temp.no) • 番兵以後 • while(itemInfoArray[i-1].no > temp.no)
18 35 2 46 8 さらに、、 さっきはtemp用の箱を用意 しましたが、、、 -1 18 35 2 46 8 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] 2 対象 temp
18 35 2 46 8 番兵を利用して、もっとスマートなアルゴリズムに 2 -1 18 35 2 46 8 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] 自分自身を番兵として入れると Tempの代わりになり、スマートです。 対象 なぜ、自分自身を入れても、番兵として の役割を果たせるのか考えてみよう!
今週の課題 • 今週はソートの時間を計ってもらいます。 • ItemInfoFolderに • バブルソートを行うbubbleSort()メソッド • 選択ソートを行うselectionSort()メソッド • 挿入ソートを行うinsertionSort()メソッド • を加え、実装しなさい。
今週の課題(続き) • 実装した3つのソートの性能を時間を計って、比べなさい。 • 要素数は、最低4種類で行いなさい。 • どれぐらいがよいかは自分で判断すること。 • 発展問題(必修ではない) • 番兵付き挿入ソートを行うinsSortWithGuard()メソッドを作ってそれも考察しなさい。
課題のために • 今回の課題では、ソートされていない、商品情報をたくさん作る必要があります。 • ランダムに商品情報を作る方法を紹介します。
ランダムに数値を発生させる • Math.random()メソッドを使います。 • このメソッドの返り値はdouble型で、0~1の間のランダムな数を返します。
1~Nまでのランダムな数を発生させる。 • double randomNo = Math.random(); • int itemNo = (int)(randomNo*N) 実際の数字を入れます。 double型をint型に 変換します。
何回も同じ条件で実行したい • 毎回ランダムに数字を発生させて実験すると、毎回違う条件になってしまいます。→ソートは条件が変わると性能も変わる • 毎回同じ条件で商品情報を生成できるように、ランダムに数字を生成したら、ファイルに書き出しておくことにします。
ファイルに書き出しておこう! ランダムに数字を発生し、 ファイルに書き出すプログラム 番号を書きとめておくファイル randomNo.txt(名前は任意) 102 83 58 3 493 38 … 書き出し 読み込み ソートを実行するプログラム (VendingMachineMain)
ファイルに読み書きする方法 Javaでは、簡単にできるように以下のクラスが用意されています。 書き出し BufferedWriterクラス OutputStreamクラス FileOutputStreamクラス プログラム ファイル 読み込み BufferedReaderクラス InputStreamクラス FileInputStreamクラス
Javaでファイルを扱う • これらのクラスを扱う際には、必ず、import java.io.*;と宣言します。(クラス宣言の前にすること) package vendingmachine; import java.io.*; public class RandomItemInfoMaker{ … } 注:Package文は なくてもよい。
ファイルの書き出し 書き出すファイル名 //ファイルの書き出し部分だけ抜粋 try{ //ファイル出力のためのBufferedWriterクラスを生成します。 FileOutputStream fos = new FileOutputStream("randomNo.txt"); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter bw = new BufferedWriter(osw); bw.write(“Test”);//write()メソッドで書き込みます。 bw.newLine();//newLine()メソッドで改行します。 bw.close();//close()メソッドでファイルを閉じます。 }catch(Exception ex){ ex.printStackTrace(); } 全体をtry{ }catchで囲む必要があります。 これについては、後にやります。
ファイルの読み込み 読み込むファイル名 //ファイルの読み込み部分だけ抜粋 try{ //ファイル出力のためのBufferedReaderクラスを生成します。 FileInputStream fis = new FileInputStream("randomNo.txt"); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); String line = br.readLine()//readLine()メソッドで一行読み込み、 読み込んだ文字列を返り値として返します。 bw.close();//close()メソッドでファイルを閉じます。 }catch(Exception ex){ ex.printStackTrace(); } 全体をtry{ }catchで囲む必要があります。 これについては、後にやります。
その他は質問どうぞ • 以下の2つのプログラムを配ります。 • Randomに数字を生成して、ファイルへの書き出しができるプログラムのRandomItemInfoMakerクラス • そのファイルを読み込んで、ランダムな商品番号のItemInfoクラス生成できるVendingMachineMainクラス • なるべくコメントを書いておきますが、分からない点はTAに質問してください。
提出方法 • objprog-07@crew.sfc.keio.ac.jp宛て。 • 今回もソースのほかに、実行結果、考察を書いて送ってください。(ただし、ソースはItemInfoFolderだけでよい。) • (感想を任意でお願いします。) • Subjectはログイン名を必ず書いてください。 • ex) t96844ym • 余計な[]などをつけないでください。 • 水曜まで。