プログラミング論II

プログラミング論
バブルソート,バケツソート,クイックソート
http://www.ns.kogakuin.ac.jp/~ct13140/Prog/
M-1
概要
• バブルソート,バケツソート
– 簡単
• クイックソート
– 難しいが重要.
M-2
ソート SORT
数字を降順/昇順に並び替える
アルゴリズム.
バブルソート,バケツソート,クイックソート
M-3
ソート
• 数を並び替える.
4,8,0,5,7
↓ソート
0,4,5,7,8
M-4
ソート
•例
int a[10] に10個の整数が格納されて
いるとする.
これを小さい順に並び替えて表示する.
M-5
ソートアルゴリズム
• バブルソート,バケツソート,クイックソート
を紹介する.
• 以下,数値を昇順に並び替えるとする.
– 降順への並び替えも基本は同じ.
M-6
バブルソート
• 「隣接する2要素の大小を比較し,正しくな
い状態だったらその2要素を交換する」こと
を繰り返す.
• O(n2)であり,一般には遅いソートアルゴリ
ズムと認識されている.
• 実装(プログラムの作成)はとても楽
M-7
バブルソートの概要
• ソート結果はa[0]≦a[1]のはずである.
• 同様に
a[1]≦a[2]のはず,a[2]≦a[3]のはず,
a[3]≦a[4]のはず,...
「a[i]≦a[i+1]」が成り立つはず.
M-8
バブルソートの概要
• 「もし,a[i]>a[i+1]であったら,この2個
を交換する」
という操作を全てのa[i],a[i+1]につい
て行う.
– 注意:配列長が10なら,
iは0~9では無くて,iは0~8.
i=9の時,a[9]とa[10]の処理になる.
• 上記「『もし逆なら交換する』を全てのiに
ついて行う」を何回も行うと,ソートができる.
M-9
バブルソートの例
• 「9,3,5,2」をソートする.
9
3
5
2
比較
順序が逆なので,交換する.
3
9
5
2
M-10
バブルソートの例
• 「9,3,5,2」をソートする.
3
9
5
2
比較&交換する.
3
5
9
2
「調査&交換」を
左から右に向かって
繰り返す.
これ1回を「走査」と
呼ぶこととする.
比較&交換する.
3
5
2
9
←初期状態より,ソートが進んだ.
これで,「a[i]とa[i+1]を比較し,逆なら交換する」
を左から右まで「全てのa[i]とa[i+1]の組」に行った.
M-11
バブルソートの例
• 「9,3,5,2」をソートする.
3
5
2
9
もう1回,左から右に
「調査&交換」繰り返した.
比較&交換しない.
3
5
2
9
比較&交換する.
3
2
5
9
比較&交換しない.
3
2
5
9
M-12
バブルソートの例
• 「9,3,5,2」をソートする.
3
2
5
9
もう1回,左から右に
「調査&交換」繰り返した.
比較&交換する.
2
3
5
9 ←ソート完了
比較&交換しない.
2
3
5
9
比較&交換しない.
2
3
5
9
M-13
バブルソート
• 「『a[i]とa[i+1]を比較して交換』を
左から右に繰り返す」
ことを繰り返せば,ソートが完了する.
• さて,何回繰り返せば完了するかを考える.
M-14
バブルソートの比較回数
9
3
5
2
3
9
5
2
3
5
9
2
3
5
2
9
• 「比較&交換」を左か
ら右に1回走査する.
すると,必ず最大値が
一番右に移動する.
よって,一番右は決
定!
• それ以外は,まだ決
定していない.
M-15
バブルソートの比較回数
3
5
2
9
3
5
2
9
3
2
5
9
3
2
5
9
• 「比較&交換」を左か
ら右.2走査目.
すると,右の2個が決
定する.
• それ以外は,まだ完
了していない.
実は,3回目の「比較&交換」は不要
M-16
バブルソートの比較回数
3
2
5
9
2
3
5
9
3
2
5
9
3
2
5
9
• 「比較&交換」を左か
ら右.3走査目.
すると,右の3個が決
定する.
• 実は,一番左も決定し
ている.
• よってこれで終了.
実は,2,3回目の「比較&交換」は不要
M-17
バブルソートの比較回数
• a[100](a[0]~a[99])のバブルソートの例
• 1走査目.以下が必要.
a[0]とa[1]の比較,a[1]とa[2]の比較,
(略)
a[98]とa[99]の比較.
よって,99回比較する.
– 注意:100回ではない.
M-18
バブルソートの比較回数
• 2走査目.以下が必要.
a[0]とa[1]の比較,a[1]とa[2]の比較,
(略)
a[97]とa[98]の比較.
よって,98回比較する.
– 1走査目より1回減っている.
最後の1個は既に決定しているので.
M-19
バブルソートの比較回数
• 3走査目.以下が必要.
a[0]とa[1]の比較,a[1]とa[2]の比較,
(略)
a[96]とa[97]の比較.
よって,97回比較する.
– 2走査目よりさらに1回減っている.
最後の2個は既に決定しているので.
M-20
バブルソートの比較回数
• n走査目.以下が必要.
a[0]とa[1]の比較,a[1]とa[2]の比較,
(略)
a[99-n]とa[100-n]の比較.
よって,100-n回比較する.
– 最後のn-1個は既に決定しているので.
M-21
バブルソートの比較回数
• 99走査目.以下が必要.
a[0]とa[1]の比較.
よって,1回比較する.
– 右の98個は既に決定しているので.
– 左の2個(a[0]とa[1])を決定して終了.
M-22
for(i=0; i<SIZE-1; i++){
for(j=0; j<SIZE-1-i; j++){
if( data[j+1] < data[j] ){
tmp = data[j+1];
data[j+1] = data[j];
data[j] = tmp;
}
}
}
M-23
バケツソート
• O(n)でソートできるアルゴリズム.
• 最速のアルゴリズムと言えるが,
大量のメモリが必要,
データの範囲(最小値,最大値)が既知で
あることが必要.
– 値の範囲が大きいほど大量のメモリが必要.
– 同じ値が2個以上あるとやりづらい.
– 浮動小数点には使いづらい.
M-24
バケツソートの例
• a[5]をソートする.各値は,0~9の整数
であることが分かっているとする.
• 以下のデータをソートする例を考える.
a[0] a[1] a[2] a[3] a[4]
9
3
5
2
0
M-25
バケツソートの例
• 値の範囲が0~9の10種類なので,
「バケツ0」~「バケツ9」の
バケツの中
10個のバケツを用意する. バケツ 0
空
a[0] a[1] a[2] a[3] a[4]
9
3
5
2
0
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
1
2
3
4
5
6
7
8
9
空
空
空
空
空
空
空
空
空
M-26
バケツソートの例
a[0] a[1] a[2] a[3] a[4]
9
3
5
2
a[0]に着目.
値は"9"なので,
バケツ9に入れる.
0
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
0
1
2
3
4
5
6
7
8
9
バケツの中
空
空
空
空
空
空
空
空
空
1個
M-27
バケツソートの例
a[0] a[1] a[2] a[3] a[4]
9
3
5
2
a[1]に着目.
値は"3"なので,
バケツ3に入れる.
0
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
0
1
2
3
4
5
6
7
8
9
バケツの中
空
空
空
1個
空
空
空
空
空
1個
M-28
バケツソートの例
a[0] a[1] a[2] a[3] a[4]
9
3
5
2
a[2]に着目.
値は"5"なので,
バケツ5に入れる.
0
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
0
1
2
3
4
5
6
7
8
9
バケツの中
空
空
空
1個
空
1個
空
空
空
1個
M-29
バケツソートの例
a[0] a[1] a[2] a[3] a[4]
9
3
5
2
a[3]に着目.
値は"2"なので,
バケツ2に入れる.
0
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
0
1
2
3
4
5
6
7
8
9
バケツの中
空
空
1個
1個
空
1個
空
空
空
1個
M-30
バケツソートの例
a[0] a[1] a[2] a[3] a[4]
9
3
5
2
a[4]に着目.
値は"0"なので,
バケツ0に入れる.
0
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
0
1
2
3
4
5
6
7
8
9
バケツの中
1個
空
1個
1個
空
1個
空
空
空
1個
M-31
バケツソートの例
• 各値の登場回数の表が完成した.
• この表を上から下に読んでいけば
ソート終了.
バケツ
バケツ
0,2,3,5,9
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
バケツ
0
1
2
3
4
5
6
7
8
9
バケツの中
1個
空
1個
1個
空
1個
空
空
空
1個
M-32
バケツソート
• 以下の手順でプログラミング可能
[1]バケツ0~バケツ9を作成する.
各バケツには,「登場回数」を記録するので,
intで長さ10の配列を宣言すればよい.
[2]全バケツの値を0個として初期化する.
バケツの長さ(10)でfor分を使う.
[3]全データをそれぞれのバケツに振り分け
ていく.データの長さ(5)でfor分を使う.
M-33
バケツソート
[4]全バケツに入ったデータを上から読んで
いく.バケツの長さ(10)でfor分を使う.
1個のバケツに2個以上のデータが入って
いることもあので,各バケツについて「バケ
ツの中の値を長さとするfor文」を使う必
要がある.
for(i=0; i<10; i++){
for(j=0; j<bucket[i]; j++){
...
}
}
M-34
#include <stdio.h>
#define DATA_NUM 5
#define VALUE_MAX 100
[1]バケツ0~バケツ9を作成する
void main(){
int data[DATA_NUM], bucket[VALUE_MAX], i, v, cnt, score;
data[0]=72; data[1]=46; data[2]=50; data[3]=46; data[4]=22;
for(v=0; v<VALUE_MAX; v++){
bucket[ v ] = 0;
[2]全バケツの値を0個として初期化する
}
for(i=0; i<DATA_NUM; i++){
score = data[i];
[3]全データを
bucket[score] ++;
それぞれのバケツに振り分けていく
}
cnt=0;
for(v=0; v<VALUE_MAX; v++){
for(i=0; i<bucket[v]; i++){
[4]全バケツに入ったデータを
printf("%d\n“, v);
上から読んでいく
}
}
}
M-35
#include <stdio.h>
#define DATA_NUM 5
#define VALUE_MAX 100
[1]バケツ0~バケツ9を作成する
void main(){
int data[DATA_NUM], bucket[VALUE_MAX], i, v, cnt, score;
data[0]=72; data[1]=46; data[2]=50; data[3]=46; data[4]=22;
for(v=0; v<VALUE_MAX; v++){
bucket[ v ] = 0;
[2]全バケツの値を0個として初期化する
}
for(i=0; i<DATA_NUM; i++){
score = data[i];
[3]全データを
bucket[score] ++;
それぞれのバケツに振り分けていく
}
cnt=0;
for(v=0; v<VALUE_MAX; v++){
for(i=0; i<bucket[v]; i++){
[4]全バケツに入ったデータを
data[cnt] = v;
上から読んでいく
cnt ++;
}
}
for(i=0; i<DATA_NUM; i++){
printf("%d\n", data[i]);
}
}
M-36
バケツソート
• データの取り得る範囲の数だけバケツが
必要.
– 例えば,多くの場合int型は32bitだが,
データの範囲が2^32通りある場合は,43億
個のバケツが必要.
バケツ1個が1バイトとしても,4GBの記憶領域
が必要.
– 可変長文字列のソートなど,バケツを用意しよ
うが無い場合は適応不可能.
• バケツが無限通りになってしまう.
M-37
クイックソート
• 通常,O(nlog(n))でソート可能なアルゴ
リズム.
– 最悪の場合,O(n2)となってしまう.
• かなり高速で,制限も無いため非常によく
使われるアルゴリズム.
– ただし,不安定なソートアルゴリズムである.
– つまり,同一の値が2個以上あったとき,
それらの順位は保証されない.(どうなるか分
からない)
M-38
クイックソート
• 配列の中から,キーとなる枢軸(pivot)
を決定する.
– 枢軸の決定方法は後述.
– 枢軸は,データの中間値が好ましい.
M-39
クイックソート
• 左半分が枢軸未満,右半分が枢軸以上と
なるようにデータ交換を繰り返す.
– 具体的には,左端からデータを調査していき
枢軸以上の値を探す.右端から枢軸未満の
値を探す.そして,この2個を交換する.
• 左に枢軸以上があってはならない.右も同様.
– 左からの走査と,右からの走査が出会ったら,
走査1回終わり.
• 必ずしも,左半分のデータの個数と,右半分の
データの個数が等しくない.
M-40
クイックソート
• 1回捜査をすると,「左半分が枢軸未満,
右半分が枢軸以上」となっている.
左グループと右グループに分割し,
それぞれをそれぞれの枢軸で再度走査し,
それぞれをまた分割する.
分割グループ内の数値が全部同じなるま
で分割を繰り返す.(グループの大きさが1
になった場合も,「数値が全部同じ」)
M-41
クイックソートの例
3 1 4 1 5 9 2 6 5 3
枢軸を決める."3"とする.枢軸決定方法は後述.
左から枢軸以上の値を,右から枢軸未満の値を探す.
3 1 4 1 5 9 2 6 5 3
両者を交換する
2 1 4 1 5 9 3 6 5 3
左右からの検索の続きを行う.
2 1 4 1 5 9 3 6 5 3
両者を交換する
2 1 1 4 5 9 3 6 5 3
左右からの検索の続きを行う.
2 1 1 4 5 9 3 6 5 3
出会ってしまったので走査終了. 「左半分枢軸未満,
2 1 1 4 5 9 3 6 5 3
右半分枢軸以上」
が達成された. M-42
クイックソートの例
2 1 1 4 5 9 3 6 5 3
分割する
2 1 1
枢軸2で分割
1 1 2
1 1
終了
2
終了
3 3
終了
4 5 9 3 6 5 3
枢軸5で分割
4 3 3 9 6 5 5
4 3 3
枢軸4で分割
3 3 4
4
終了
枢軸6
5 5
終了
9 6 5 5
枢軸9で分割
5 6 5 9
5 6 5
6
終了
9
終了
M-43
枢軸の決定方法
• 枢軸未満と枢軸以上に分割するため,
片方のグループの要素数が0個となってはな
らない.
– 例えば,「異なる数字を左から2個探し,その大
きい方を枢軸とする」とすれば,「枢軸未満」「枢
軸以上」ともに最低1個存在することになる.
– 異なる数がなければ(全部同じなら)分割不要.
失敗例
枢軸未満
4 5 9
一番左の4を枢軸として採用
枢軸以上
4 5 9
分割できなかった.
M-44
好ましい枢軸の決定方法
• ちょうど,2分割されることが好ましい.
– そのためには,データの中間値に近い値を枢軸
とすると良い.
– 例えば,ランダムに選んだ3個の中間値を採用.
好ましい例
M-45
好ましい枢軸の決定方法
• 分割に偏りがあると,計算時間が長くなる.
好ましくない例
M-46
好ましい枢軸の決定方法
• はじめからソートされている配列をソートしよ
うとすると,このような現象が発生する.
0 1 2 3 4 5 6 7
枢軸=1
0
1 2 3 4 5 6 7
枢軸=2
2 3 4 5 6 7
1
枢軸=3
3 4 5 6 7
2
枢軸=4
3
4 5 6 7
4
5 6 7
M-47
クイックソート
• 「枢軸でグループを分割」という処理を行い,
2個のグループを作る.
そして,両グループを再度「枢軸で分割」を
行う.
そして,再度「枢軸で分割」を行う...
• 通常,(C言語などでは)再帰により実装す
る.
M-48
補足
• 通常のプログラミング言語では,ソートや
検索のような基本的な機能は標準で提供
されている.
• ソートの場合
– C言語では,qsort()関数がある.
– Java,Ruby,Perlなどにもある.RDBMSに
ソート機能が用意されている.
• 検索では,
– Java,Ruby,Perlなどには,Hashがある.
– Hashは,O(1)で検索できる手法.
M-49
課題
• 4個の数字 12,7,1,9をバブルソートで
並び変えたとき,各数の推移を記せ.
M-50
課題
• int a[100] に整数が100個入っている.負の数も含
まれる.
– (い) a[0]~a[99] の合計を求めるプログラムを作成せよ
– (ろ) a[X]~a[Y] の合計を求めるプログラムを作成せよ.
– (は) a[X]~a[Y] の合計が最大になるのは,XとYがいくつ
の場合か求めるプログラムを作成せよ.負の数も含まれるた
め,範囲が広いほど合計が大きくなるわけではない. オーダ
ーはいくつか?
– (に) a[] の中に 0 が何個あるか数えるプログラムを作成
せよ.
M-51