オブジェクト・プログラミング

オブジェクト・プログラミング
第7回
アルゴリズム
前回多かった質問

要素が少ないとき、0msと出てしまう。


この方法ではms以下の時間は計れないので、
仕方がないです。コンピュータはmsより小さい
単位で動いています。
毎回かかる時間が微妙に違う


Windowsがマルチタスクなためです。
よって、この手の考察を行う場合、何回かの
平均を取るべきです。(そんなプログラムは簡
単に書けるでしょう!?)
前回の経験より

リニアサーチを使うのは次の場合に限られ
る。




要素が少ない時(数十個程度)
ソートされていないデータに対する時
だけどバイナリサーチはソート(順番に並
んでいる)されていなければ、使えない!
今日は、ソートを学びます。
今週の目標

ソートのアルゴリズムについて説明するこ
とができる。(教科書第3章)


Javaでソートをするプログラムが書けるように
なる。
Javaのテクニックを学ぶ


ランダムに商品情報を作る。
ファイルの入出力を行う。
今日は簡単なソートをやります
ソートは重要で時間のかかる処理なので、これま
でにも多くのコンピュータ科学者が研究に取り組
み、いくつかの高度な方法も発明されています。
その中で比較的簡単な3つの方法を紹介します。




バブルソート
選択ソート
挿入ソート
これらのテクニックは素朴で、遅いアルゴリズム
ですが、勉強する価値はあります。単に分かりや
すいだけでなく、データの並び方や数によって、
これらの方法のほうが速い場合もあるからです。
バブルソート

アルゴリズム




(1)2つの商品番号を比較する
(2)左側の方の番号が大きければ商品情報を入れ替
える。
(3)右へ一つ移動する。
ソートの終ってない部分(毎回小さくなる)に対して上
のステップを繰り返す

※配列の一番左の箱の中の番号が最小になるように並べ替
える場合です。
バブルソート
比較する
比較する
比較する
比較する
比較する
比較する
比較する
入れ替える
そのまま
入れ替える
入れ替える
そのまま
入れ替える
入れ替える
ソート済み
ソート済み
103
22 103
22
51
3 103
51
3 103
64
18
3 103
18 103
64
18
[0]
[1]
[2]
[3]
[4]
[5]
[6]
もう一度
一つ移動する
一つ移動する
一つ移動する
一つ移動する
一つ移動する
一つ移動する
始めから
[7]
[8]
[9]
とやっていくと、最後に
ソートされます。
※実際にはオブジェクトが入りますが、今日は見やすいように番号
だけが入っているように見せます。
コンピュータでの「入れ替え」
(Swap)

前ページの例では、「入れ替え」を一瞬で
行っていましたが、実はコンピュータはそ
のような操作はできません。
入れ替える
51
3
51
3
[0]
[1]
コンピュータでの「入れ替え」
(Swap)やり方

temporaryの箱を利用します。
入れ替え完了!
これを何回もやるのは
結構時間かかります。
コピー
コピー コピー
51
3
51
3
3
[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)見つかったものと、一番左の商品情報を入
れ替える。
ソートの終ってない部分(毎回小さくなる)に対
して上のステップを繰り返す。
選択ソート
比較する
比較する
比較する
比較する
比較する
比較する
比較する
比較する
比較する
入れ替える
入れ替える
ソート済み
ソート済み
103
3 18
22
51
103
3 64
18
22
[0]
[2]
[3]
[5]
[1]
[4]
[6]
[7]
[8]
[9]
最小値を更新する
ここからまた同じことをはじめる
このようにして、ソートが終わるまで
最小値が見つかった!
最小値が
5130
続けます。
みつかった
最小値の入っている
箱の番号
選択ソートの効率

①比較の回数
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
18
51
22
64
51
22
64
18
103
[0]
[1]
[2]
[3]
[4]
[5]
[6]
ここだ!
入れるところを探す
[8]
[9]
コピーする
次に挿入する
ターゲット
次に挿入すべき商品情報
22
これを繰り返します。
[7]
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が増えたときにボトル
ネックにになってしまう。何とかして、条件は一つにしたい
な。
i<0の条件をなくすと何故まず
い?
ソート済み
18
35
2
46
8
[0]
[1]
[2]
[3]
[4]
挿入対象
[5]
[6]
[7]
[8]
[9]
こんな時、iは0以下になってしまい、
-1の配列をアクセスしてしまい、
ArrayIndexOutOfBoundsExceptionが
出てしまいます。
何故かは考えてみよう!
条件をなくしてもエラーをなくす
方法

-1
番兵くん登場!
18
35
2
46
8
[-1] [0]
[1]
[2]
[3]
[4]
番兵くん
[5]
[6]
[7]
[8]
[9]
こいつに絶対ありえない小さいの商品番号(この場合、
商品番号は必ず正だとすると-1とか-10000000とかを
入れておく)入れておけば、i>0の条件を抜いても、
絶対に配列からはみ出さない。
だけど-1なんていう配列は作れ
ない。

0番目を番兵用に空けます。
-1
18
35
2
46
8
[0]
[1]
[2]
[3]
[4]
[5]
番兵用
[6]
[7]
[8]
[9]
※0番目は番兵用に空けるため、挿入のときなどの
プログラムにも工夫が必要になります。
番兵を使うときの注意

番兵を使うと、使える配列は一つ少なくな
るので注意!


いっぱいにはしないとか、一つ増やした配列を
用意するなどの対応を取るのが一般的。
今回は配列の一番始めに番兵を立てます
が、アルゴリズムによっては、一番最後に
番兵を立てる場合もあります。
番兵を利用した場合のwhile文

番兵以前


while(i>0 && itemInfoArray[i-1].no > temp.no)
番兵以後

while(itemInfoArray[i-1].no > temp.no)
さらに、、
さっきはtemp用の箱を用意
しましたが、、、
18
-1
35
18
35
2
46
2
46
8
8
[0]
[1]
[2]
[3]
[4]
[5]
対象
[6]
[7]
[8]
[9]
2
temp
番兵を利用して、もっとスマート
なアルゴリズムに
18
-1
2
35
18
35
2
46
2
46
8
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型に
変換します。
実際の数字を入れます。
何回も同じ条件で実行したい


毎回ランダムに数字を発生させて実験す
ると、毎回違う条件になってしまいます。
→ソートは条件が変わると性能も変わる
毎回同じ条件で商品情報を生成できるよう
に、ランダムに数字を生成したら、ファイル
に書き出しておくことにします。
ファイルに書き出しておこう!
ランダムに数字を発生し、
ファイルに書き出すプログラム
書き出し
102
83
58
3
493
38
…
番号を書きとめておくファイル
randomNo.txt(名前は任意)
読み込み
ソートを実行するプログラム
(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に質問してください。
提出方法


[email protected]宛て。
今回もソースのほかに、
実行結果、考察を書いて送ってください。
(ただし、ソースはItemInfoFolderだけでよい。)


Subjectはログイン名を必ず書いてください。



(感想を任意でお願いします。)
ex) t96844ym
余計な[]などをつけないでください。
水曜まで。