離散システム特論

離散システム特論
整列(sorting)アルゴリズム 1
参考文献
1. 大森克史・木村春彦・広瀬貞樹 著 「アルゴリズム
の基礎」 共立出版(1997)
2. 渡邊敏正 著 「データ構造と基本アルゴリズム」
共立出版(2000)
整列とは
• 全順序集合Sが与えられたとき,Sのすべて
の要素をその全順序に従って並べ替えるこ
と.
• 簡単のため,n個の整数の集合をSとし,通
常の数字の大小関係で小さい数から大きい
数へ並べ替えるとする(昇順ソート
ascending sort).
ソーティングアルゴリズム
選択法(selection sort)
データ列の最小値を選択し,未整列部分の
先頭に置くことを順次繰り返す.
◆ min{a1, a2, … , an}とa1を交換
◆ min{a2, …, an}とa2を交換
...
◆ min{an-1, an}とan-1を交換
選択法の擬似コード
for (i=1; i≦n-1;i++){
min:= i;
// 最小値の添字をしまう.
for (j=i+1; i≦n; j++){
if(aj < amin) min :=j;
}
swap(ai, amin);
}
選択法の例
(4,10,5,2,1,7,9,3,8,6)
(1,10,5,2,4,7,9,3,8,6)
(1,2,5,10,4,7,9,3,8,6)
(1,2,3,10,4,7,9,5,8,6)
(1,2,3,4,10,7,9,5,8,6)
(1,2,3,4,5,7,9,10,8,6)
(1,2,3,4,5,6,9,10,8,7)
あとはよろしく
選択法の計算量
n個のデータのソートに必要な最悪比較回数
for (i=1; i≦n-1;i++){
min:= i;
// 最小値の添字をしまう.
for (j=i+1; i≦n; j++){
if(aj < amin) min :=j;
}
swap(ai, amin);
}
O( ? )
バブルソート(bubble sort)
隣接する2項を比較し,aj > aj+1のように左の項が
右の項よりもおおきければ,これを交換するという
操作をデータ列の左から右へ走査することを順次続
け,結果的に,右端から順にデータを確定させる.
背の順
に並ぶよ
バブルソートの擬似コード
for (i = n; i 2; i++){
for (j=1; j i-1; j++){
if (aj > aj+1) swap (aj, aj+1);
}
}
バブルソートの例(1)
(4,10,5,2,1,7,9,3,8,6)
i
j
(4,10,5,2,1,7,9,3,8,6)
i
j
(4,5,10,2,1,7,9,3,8,6)
j
i
(4,5,2,10,1,7,9,3,8,6)
j
あとはよろしく
i
(4,5,2,1,7,9,3,8,6,10)
i
バブルソートの例(2)
(4,5,2,1,7,9,3,8,6,10)
i
j
(4,5,2,1,7,9,3,8,6,10)
i
j
(4,2,5,1,7,9,3,8,6,10)
i
j
あとはよろしく
(4,2,1,5,7,3,8,6,9,10)
i
...をつづける!
バブルソートの計算量
n個のデータのソートに必要な最悪比較回数
for (i = n; i 2; i++){
for (j=1; j i-1; j++){
if (aj > aj+1) swap (aj, aj+1);
}
}
O( ? )
ちょこっと工夫:
j=1,…, i-1で入れ替えが一度もなければソート終了
挿入法(insertion sort)
a1からai-1がすでにソートされていると仮定して,aiをしか
るべき正しい位置に挿入する.すなわち,aiよりも大きいデ
ータを右側にシフトした跡,その空き場所にaiを挿入する.
挿入法の擬似コード
for(i = 2; i  n; i++){
w:=ai;
j:=i;
while(aj-1>w and j>1){
aj:=aj-1;
//移動
j:=j-1;
}
aj:=w;
}
//挿入
挿入法の例
(4,10,5,2,1,7,9,3,8,6)
j-1 W=10
(4,10,5,2,1,7,9,3,8,6)
j-1 W=5
(4,10,10,2,1,7,9,3,8,6)
j-1
W=5
(4,5,10,2,1,7,9,3,8,6)
j-1
W=5
(4,5,10,2,1,7,9,3,8,6)
j-1 W=2
あとはよろしく
挿入法の計算量
n個のデータのソートに必要な最悪比較回数
for(i = 2; i  n; i++){
w:=ai;
j:=i;
while(aj-1>w and j>1){
aj:=aj-1;
//移動
j:=j-1;
}
aj:=w;
}
//挿入
O( ? )
挿入法の計算量(続)
for(i = 2; i  n; i++){
w:=ai;
j:=i;
while(aj-1>w and j>1){
aj:=aj-1;
//移動
j:=j-1;
}
aj:=w;
}
//挿入
ちょこっと工夫:
リスト構造を用いれば,挿入場所を
移動をしなくてもよい.でも挿入場所
をみつけるのに,おなじだけwhileを
まわす?
挿入法の計算量(続々)
n個のデータのソートに必要な平均比較回数
場合
Whileの反復回数
ai<a1
i-1回
a1ai<a2
i-2回
...
各場合が等確率に
起こるとすると,
...
ai-2ai<ai-1
1回
ai-1ai
0回
i1 1  n i 1 1
 i k  2  4 n(n 1)
 i 2
i 2 k 0
n
Selection/Bubble/Insertionの比較
長所
選択法
バブル
挿入法
短所
ヒープソート(heap sort)
選択法で最小値をみつけるときに,ヒープを用い
て効率よく行う.
ヒープ:
◆ 最小値をO(1)でみつける.
◆ 最小値を削除してヒープ構造
を保つのに,O(log k)でできる.
(kはヒープの中の要素数)
ヒープ
根付き2分木:根をもつ(親子関係のある)木で各頂点の子が2
個以下.
完全2分木:頂点を左詰で並べた根付き2分木.
•すべて葉は根からの深さが高々1 しか違わない.
(深さmか,m-1に葉がある)
•深さmの葉は左詰め.
•深さがm-2以下の頂点は子をちょうど2つもつ.
ヒープ:
◆ 完全2分木
◆ 各頂点がデータを持つ
◆ 親頂点のデータは子頂点のデータよりも小さい
d-ヒープ:子がd個以下の完全d分木からなるヒープ.単にヒープというと,2-ヒープを指す.
ヒープかな?
図の上下が親子関係を表す
1
1
3
5
2
4
4
3
7
1
2
6
5
4
5
7
6
1
1
2
3
6
3
2
5
8
3
6
3
7
5
8
2
6
3
5
ヒープソートの擬擬似コード
1: {a1, …, an}をヒープ化;
2: for(i=1;in-1; i++){
3:
ヒープから最小値を取り出して,aiと入れ替え;
4:
{ai+1, …, an}を再ヒープ化;
5: }
•最小値はヒープの
•1行目と3行目は...
にあるので,3行目はO(
)でできる.
DeleteMin(1)
1.最小値を根からとる.
1
2
3
7
根のデー
タがない!
5
8
6
DeleteMin(2)
2.とりあえず,最後のデータを移動
6
2
3
7
5
8
6
親子のデータ
の大きさ関係
が崩れる
DeleteMin(3)
3.ダウンヒープ
ajで親子のデータの大小関係が崩れているとする.
(1)ajの子のデータの小さい方をaiとする
(2)aj<aiならば,ajとaiを入れ替える.
(3)これを子がなくなるか,あるいはどの子よりもajが小
さくなるまで繰り返す.
6
2
2
3
7
5
8
2
6
3
7
5
8
3
6
7
なぜ,小さい子どもと入れ替えるの?
5
8
ヒープソートの操作
1: {a1, …, an}をヒープ化;
2: for(i=1;in-1; i++){
3:
ヒープから最小値をみつけてi番目に格納;
4:
DeleteMin;
1
5: }
1行目は?
//最小値をヒープから取り除く
6
2 5
2 5
3 7 8 6 3 7 8
2
6 5
3 7 8
2
3 5
6 7 8
ヒープの初期化
データ数nが既知
1. 1頂点からなる完全2分木をn個作り,データを割り振る.
2. 以下の走査を繰り返して1つのヒープにする
2つのヒープがあるときに,新しい1点wを根として,
これらのヒープを部分木とするような完全2分木から
1つのヒープを作る.このとき,ダウンヒープの走査で
ヒープの条件を満たすようにする.
ヒープの初期化操作の例(1)
(4,10,5,2,1,7,9,3,8,6)のヒープをつくる
1.とりあえず,データを入れる
4
5
10
2
3
1
8
6
7
9
ヒープの初期化操作の例(2)
10
2.ダウンヒープを繰り返す
2
4
ダウンヒープ
1
7
8
6
1
2
9
3
3
8
5
10
2
OK
3
1
8
6
OK
10
6
1
2
3
8 10
6
ヒープの初期化操作の例(3)
2.ダウンヒープを繰り返す
ダウンヒープ
4
5
1
2
3
6
8
10
7
9
1
OK
3
4
5
2
6
8
10
7
9
ヒープソートの計算量
1: {a1, …, an}をヒープ化; O(ヒープの深さ)
2: for(i=1;in-1; i++){
3:
ヒープから最小値をみつけてi番目に格納; O(1)
4:
DeleteMin; //最小値をヒープから取り除く O(ヒープの深さ)
5: }
1
6
2 5
2 5
3 7 8 6 3 7 8
2
6 5
3 7 8
k個のデータを格納するヒープの深さは?
2
3 5
6 7 8
ヒープの深さ
k個のデータを格納するヒープの深さは?
深さ0
1
深さ1
5
2
3
4
6
8
7
9
10
深さ2
深さ3
深さiに格納で
きるデータの
個数は?
深さdのヒープ全
体に格納できる
データの個数
は?
2d+1-1
2d-1<k2d+1-1を満たす.
よって, 2dk<2d+1. すなわち,d=|_ log k 」
ヒープソートの計算量(続)
1: {a1, …, an}をヒープ化; O(要素数 × ヒープの深さ)
2: for(i=1;in-1; i++){
3:
ヒープから最小値をみつけてi番目に格納; O(1)
4:
DeleteMin; //最小値をヒープから取り除く O(log (n-i))
5: }
Totalで4行目:
log(n-1)+log(n-2)+…+log(1)=log( (n-1)! ) n log n
1行目もO(n log n)は明らか.ちゃんと算定すると,O(n)
ヒープソートの計算量:O(
)
ヒープの初期化はO(n)
ただし,データ数nは既知
(n=2d-1と仮定)
ヒープの大きさ
ヒープの数
ダウンヒープの回数
1 (葉に対応)
2d
0
3
2d-1
1
…
…
…
n=2d -1
1 = 20
d
Total:
2d-1+2・ 2d-2 +3・ 2d-3 +... +d・ 20= 2d+1-d-2 = O(n)
ヒープソートの例
(4,10,5,2,1,7,9,3,8,6)のヒープをつくる
1
5
2
6 7
3
4
10
5
2
9
8 10
10
6 7
4
10
8
3
5
4
5
6 7
9
8
8
4
5
3
6 7
3
4
3
2
8
9
10
6 7
9
あとはよろしく
9
完全2分木の実現
◆ 構造体:各頂点が親,子へのポインタを持つ.
◆ 配列:順番に格納
1
2
4
8
3
5
6
A=
1
2
3
7
A[i]の子はA[2i] とA[2i+1]
親は,A[└i/2」]
4
5
6
7
8