卒業研究報告書
1
ソートの原理
バブルソートの原理
先頭から後ろにスキャンして行き、隣合う要素の大小が逆だったら交換することを繰り返すアルゴリズムである。
選択ソートの原理
未整列部分から最小(大)値を選択し、先頭に持ってくることを
繰り返すアルゴリズムである。
挿入ソートの原理
整列済み配列の中の適切な位置に未整列要素を挿入することを繰り返すアルゴリズムである。
クイックソートのアルゴリズム
ある要素を枢軸とし、枢軸より小さい要素と大きい要素に振り分けることを
繰り返すアルゴリズムである。
マージソートのアルゴリズム
要素を半分に分割することを、要素が1つになるまで繰り返し、
整列しながら、各要素を結合することを繰り返すルゴリズムである。
2
実験手順まず、各アルゴリズムのプログラムを作成・実行し、動作の原理を理解する。
昇順、降順、重複する数値を含むランダムな数列、重複する数値を含まないランダムな数列、
の4種類を使って実際に整列されているかを確かめる。
次に、整列プログラムを実行する時間を計測する(数列ファイルを読み込むなどの
時間は含まない)。数列ファイルの大きさは、
500、1000、5000、10000を使った。(ファイルの大きさは、
2倍10倍の要素が考えやすいので、この大きさにした)
その次に、整列プログラムで実行される比較回数と交換(代入)回数を計測する。
数列ファイルの大きさは時間計測と同じ、
500、1000、5000、10000を使った。
3
各アルゴリズムの実験結果と考察計測された時間は、少数点以下は「…
000」「…333」「…667」のどれかで、(プログラムを工夫しないと)細かい時間は計測しきれないので少数点以下4桁目を四捨五入している。(また、通常オーダーを表す時はnを使用するが、mを使用すると、u(平方メートルの単位)が使えて、二乗を表すのに便利なため、mを使ってオーダーを表現している)
1、バブルソート
バブルソートのプログラムの一部(交換・比較回数のカウントを表す)
for (i=0; i<m-1; i++)
for (j=m-1; j>i; j--) {
compare++; /*
比較のカウント */if (a[j-1]>a[j]) {
exchange++; /*
交換のカウント */t=a[j]; a[j]=a[j-1]; a[j-1]=t;
}
}
比較回数は、外側の
for文と内側のfor文の回数を掛けた値になる。外側の
for文はm−1回行われ、内側のfor文は(最初はm−1回、最後が1回なので)平均m/2になる。よって比較回数の理論値は
(
m-1) × m/2 = u/2−m/2になる。
また、比較回数の最大次数の項がu
なのでバブルソートのオーダーはu になる。
図1−1
バブルソートの比較回数/交換回数|
データの 並び方 要素数 m |
昇順 比較回数(回) 交換回数(回) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
124750 0 |
124750 124750 |
124750 63197 |
124750 58334 |
|
1000 |
499500 0 |
499500 499500 |
499500 240764 |
499500 222242 |
|
5000 |
12497500 0 |
12497500 12497500 |
12497500 6233764 |
12497500 5619870 |
|
10000 |
49995000 0 |
49995000 49995000 |
49995000 24820080 |
49995000 22762526 |
(
m−1)×m/2となった。(理論値と一致)
・整列する必要のない昇順でも同じ回数だけ比較することになる。
・ランダムのファイルの交換回数は、比較回数の半分に近い値になっている。
図1−2
バブルソートの実行時間と実行時間の理論値|
データの 並び方 要素数 m |
昇順 実行時間(秒) 理論値(秒) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
0.017 0.017 |
0.033 0.043 |
0.033 0.036 |
0.033 0.034 |
|
1000 |
0.067 0.066 |
0.183 0.171 |
0.150 0.142 |
0.133 0.136 |
|
5000 |
1.617 1.655 |
4.550 4.276 |
3.517 3.553 |
3.400 3.394 |
|
10000 |
6.517 6.621 |
18.717 17.104 |
14.583 14.213 |
14.200 13.575 |
・実行時間の理論値はデータファイル毎に実行時間÷各要素数のu
の値を加算し、4で割り、その値を各要素数のu を掛けた値とした。(例えば、昇順では、
0.017÷250000 + 0.067÷1000000 + 1.617÷25000000 + 6.517÷100000000 の値を4で割った値を基準として、要素数が500なら、250000を掛けた値が実行時間の理論値となる。)実験に使用したワークステーションの処理速度が速いことと、プログラムで計測できる時間の最小が
0.0166… であったため、要素数500の実行時間は不正確である。(バブルソート以外のアルゴリズムでも不正確になってしまっている。)要素数500以外の3つは、実行時間÷u の値が近い値になっても、要素数500だけ、他とずれてしまうことがあり、その実行時間の理論値は、ずれてしまう。(バブルソートの場合、降順理論値は、要素数500だけ、理論値が大きくなってしまい、他3つは理論値の方が小さくなってしまう。)降順以外は大体実行時間とその理論値は近い値になっている。
2、選択ソート
選択ソートのプログラムの一部(交換・比較回数のカウントを表す)
for(i=0 ; i<m-1 ; i++){
lowest = i;
lowkey = a[i];
for(j=i+1 ; j<m ; j++)
compare++; /*
比較回数のカウント */if(a[j]<lowkey){
lowest = j; lowkey = a[j];
}
exchange++; /*
交換回数のカウント */t=a[i];
a[i]=a[lowest];
a[lowest]=t;
}
・交換回数は外側の
for文のループ回数なので、(m−1)になる。・比較回数は
if文の実行回数なので内側のfor文のループ回数 m/2と外側の
for文のループ回数m-1を掛けた値(
m−1)×m/2 = u/2− m/2になる。
図2−1
選択ソートの比較回数/交換以外の代入回数(if文内でlowkeyに代入された回数)|
データの 並び方 要素数 m |
昇順 比較回数(回) 交換以外の 代入回数(回) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
124750 0 |
124750 62500 |
124750 2411 |
124750 1526 |
|
1000 |
499500 0 |
499500 250000 |
499500 5633 |
499500 3212 |
|
5000 |
12497500 0 |
12497500 6250000 |
12497500 35254 |
12497500 18380 |
|
10000 |
49995000 0 |
49995000 25000000 |
49995000 80166 |
49995000 35743 |
・選択ソートでは適切な要素を選んでから交換するので、どの種類のデータファイルでも
比較・交換回数は理論値と一致した。(要素数が
10倍になれば,比較交換回数は100倍になる)(図2では交換回数ではなく、
if文の中で最小値の添え字(lowest)と最小値(lowkey)に代入が行われた回数を載せる。)・降順では、要素数が
2倍になれば、if文内でlowkeyに代入された回数は4倍になることが解る。・降順での
if文内での代入回数は比較回数の半分に近い。これは、未整列部分の先頭(降順なら最大値)と未整列部分の最小値が交換されるからである。
・降順以外の実行時間は比較・交換回数が同じなためあまり違いが出なかった。降順は
if文の中のlowkeyとlowestへの代入がたくさん発生するので他よりも時間が掛かる。(lowkeyには一番左側の値を最初代入するので降順では未整列部分の最小値を代入することになり、比較する度にlowkeyへの代入が発生する。)
図2−2
選択ソートの実行時間/実行時間の理論値|
データの 並び方 要素数 m |
昇順 比較回数(回) 交換以外の代入回数(回) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
0.017 0.023 |
0.033 0.031 |
0.033 0.027 |
0.033 0.026 |
|
1000 |
0.100 0.090 |
0.117 0.124 |
0.100 0.107 |
0.100 0.107 |
|
5000 |
2.350 2.256 |
3.017 3.100 |
2.367 2.666 |
2.367 2.665 |
|
10000 |
9.967 9.025 |
12.633 12.633 |
9.983 10.663 |
9.967 10.688 |
実験に使用したワークステーションの処理速度が速いことと、プログラムで計測できる時間の最小が0.0166… であったため、要素数500の実行時間は不正確である。要素数500以外の3つは、実行時間÷u の値が近い値になっても、要素数500だけ、他とずれてしまうことがあり、その実行時間の理論値は、ずれてしまう。降順以外は実行時間とその理論値は少し離れてしまった。
・選択ソートを改善するには、最小値のlowkeyとlowestだけでなく、最大値もスキャン時に探すことによって、配列の両側から整列済みになり、実行時間が早くなると考えられる。
・スキャンする前のlowkeyへの代入を、左端・中央・右端の中の最小値を選べば、降順だけ少し実行時間が掛かるのを防ぐことができると考えられる。
3、挿入ソート
挿入ソートのプログラムの例(交換・比較回数のカウントを表す)
for (i=1; i<m; i++){
j=i;
while(1){
compare++; /*
比較回数 :/if( !(j>=1 && a[j-1] > a[j]) )
break;
t=a[j]; a[j]=a[j-1]; a[j-1]=t;
j--;
exchange++; /*
交換回数 */}
}
・挿入ソートは
for文がm−1回実行され,while文は平均i/2回実行されるため,挿入ソートのオーダーはu となり、参考文献と一致する。
図3−1
挿入ソートの比較回数/交換回数|
データの 並び方 要素数 m |
昇順 比較回数(回) 交換回数(回) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
499 0 |
125249 124750 |
63696 63197 |
58833 58334 |
|
1000 |
999 0 |
500499 499500 |
241763 240764 |
223241 222242 |
|
5000 |
4999 0 |
12502499 12497500 |
6238763 6233764 |
5624869 5619870 |
|
10000 |
9999 0 |
50004999 49995000 |
24830007 24820080 |
22712519 22762526 |
・昇順では、交換回数が0で、比較回数も
m−1のため、時間が掛からず、要素数1万でも実行時間が計測できなかった。
・実行時間順の大きい順に並べると、降順・ランダム・ランダム(重複なし)昇順となる。挿入ソートは昇順(もしくは昇順に近いと)高速である。逆に降順はバブルソートよりも遅くなってしまう。整列するファイルの並び方によって、実行時間が大きく変化するアルゴリズムである。
・ランダムの比較交換回数は降順の比較交換回数の半分に近い値になっている。乱数なので、バラつきがちょうど、降順(
1番手間が掛かる)と昇順(1番手間が掛からない)の間の数であるとも言える。
図3−2
挿入ソートの実行時間と実行時間の理論値|
データの 並び方 要素数 m |
昇順 実行時間(秒) 理論値(秒) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
0.000 0.000 |
0.050 0.048 |
0.017 0.021 |
0.033 0.024 |
|
1000 |
0.000 0.000 |
0.183 0.191 |
0.083 0.085 |
0.083 0.096 |
|
5000 |
0.000 0.000 |
4.700 4.775 |
2.317 2.113 |
2.100 2.408 |
|
10000 |
0.000 0.000 |
19.333 19.100 |
9.450 8.455 |
8.633 9.633 |
・実行時間の理論値はデータファイル毎に実行時間÷各要素数のu
の値を加算し、4で割り、その値を各要素数のu を掛けた値とした。実験に使用したワークステーションの処理速度が速いことと、プログラムで計測できる時間の最小が
0.0166… であったため、要素数500の実行時間は不正確である。要素数500以外の3つは、実行時間÷u の値が近い値になっても、要素数500だけ、他とずれてしまうことがあり、その実行時間の理論値はずれてしまう。昇順はどの要素数でも0秒なので理論値と完全に一致した。
降順以外は実行時間とその理論値は少し離れてしまった。
・挿入ソートは隣り合った要素を比較しているので、離れた要素を比較するようにする(シェルソートの考えを使う)と実行時間が早くなると考えられる。
5、クイックソート
クイックソートのプログラムの一部(交換・比較回数のカウントを表す。
compare++;が比較回数のカウントで,exchange++;が交換回数のカウント)int partition(int a[],int l,int r)
{ int i,j,pivot,t;
i=l-1; j=r; /*
ポインタiとjを初期化する */pivot=a[r]; /*
一番右端の要素を枢軸にする */for(;;){ /*
ポインタiとjがぶつかるまで繰り返す */while(a[++i] < pivot){ compare++; /*
ポインタiを右へ進める */} compare++;
while(i < --j && pivot <a[j]){ compare++; /*
ポインタjを左へ進める */} compare++;
if(i >= j) break; /*
ポインタiとjがぶつかったらループを抜ける */t=a[i]; a[i]=a[r]; a[r]=t; exchange++; /* i
の指す要素とjの指す要素を交換する */}
t=a[i]; a[i]=a[r]; a[r]=t; exchange++; /* a[i]
と枢軸を交換する */return i;
}
上のプログラムのように、
whileでは真でも偽でも比較としてカウントし、交換回数はポインタ同士を交換しても、iと枢軸を交換してもカウントした。図4−1
クイックソート比較回数/交換回数|
データの 並び方 要素数 m |
昇順 比較回数(回) 交換回数(回) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
125748 499 |
125498 499 |
6290 1197 |
5799 1144 |
|
1000 |
501498 999 |
500998 999 |
12660 2699 |
13140 2545 |
|
5000 |
12507498 4999 |
12504998 4999 |
77893 15607 |
83117 15360 |
|
10000 |
50014998 9999 |
50009998 9999 |
173285 33606 |
173016 33265 |
・昇順と降順の比較・交換回数が近くて、バブルソートの昇順と同じ位の実行速度になっている。
・ランダムのファイルでは要素数が
500から5000になれば、交換・比較回数は13倍近くになり、m logmに比例していることが解る(5000 log5000 ÷ 500 log500=13.3…)。・昇順の1
2 3 4 の数列をクイックソートする場合、最大値が枢軸になると、枢軸より大きな値が無く、最大値である4だけの整列が終了し、1 2 3 の数列をソートしなければならず、1回のソートで整列が完了するのは1つだけなので手間が掛かる。(改善するには、配列の左端・右端・中央の3つの中央値を枢軸に決めるようなプログラムにすると良くなると考えられる。)
図4−2
クイックソートの実行時間と実行時間の理論値(ランダムの2つは理論値が出せなかった)|
データの 並び方 要素数 m |
昇順 実行時間(秒) 理論値(秒) |
降順 |
ランダム |
ランダム (重複なし) |
|
500 |
0.017 0.016 |
0.017 0.017 |
0.000
|
0.000
|
|
1000 |
0.067 0.065 |
0.067 0.067 |
0.000
|
0.017
|
|
5000 |
1.500 1.619 |
1.600 1.663 |
0.017
|
0.017
|
|
10000 |
6.417 6.475 |
6.750 6.650 |
0.033
|
0.033
|
u に比例していて、最悪の状態になっていることがわかる。
(u
+ 3m −4)/2である。
求めた手順
a[6]
={1 2 3 4 5 6} の数列をクイックソートすると考える関数
partition内で1回目のスキャンでは、左から右に進めるポインタiは6回(A[0]、A[1]、A[2]、A[3]、a[4]、a[5])、右から左に進めるポインタjは1回(A[5])、枢軸と比較する。そして、iが指すA[5]と枢軸A[5](自分同士)を交換し、要素が1つになったA[5]は整列終了。この手順を繰り返す。1
2 3 4 5 E (Eは枢軸)ポインタiの比較回数は6回、ポインタjの比較回数は1回1
2 3 4 D iは5回、jは1回1
2 3 C iは4回、jは1回1
2 B iは3回、jは1回1
A iは2回、jは1回
このように、昇順の数列では、要素が6つであれば、7回比較し、要素が6つであれば7回比較することから、要素+1回比較することが解る。また、要素数が1つになったらもうスキャンはしないので要素数2つのスキャンの比較回数3が最低値になる。
数列
Aでは、比較回数が7+6+5+4+3のように等差数列の和になっている。よって、昇順で要素数
mのクイックソートの比較回数は初項m+1、末項が3で項数がm−1の等差数列の和となり、(
m+1+3)/2×(m−1)になるので
(u + 3m −4)/2 が求まる。昇順の比較回数は、全て理論値と一致した。
m
/2+m−2である。
求めた手順
b
[6]={6 5 4 3 2 1}の数列をクイックソートすると考える。6
5 4 3 2 @ iは1回、jは5回(1と6が交換され、1は整列終了)5 4 3 2 E iは5回、jは1回
5 4 3 A iは1回、jは3回
2 4 B iは2回、jは1回
2 B iは2回、jは1回
比較回数は6、6、4、3、3となる。この数列は昇順の比較回数と近いので良く見ると
1
回目、3回目、4回目では昇順の比較回数−1になっている。スキャンのうち約半分は、昇順−1回比較している。よって昇順の理論値から、項数/2を引くと(
m+1+3)/2×(m−1)−m/2∴
m/2+m−2が求まる。
降順の比較回数は、全て理論値と一致した。
昇順と降順の比較/交換回数を比べてみると、どの要素数でも交換回数は同じで、比較回数は昇順の方が多かった。昇順は並び換えしなくてもいいのに比較回数が降順より多くなるのはおかしいように思える。これは、昇順の場合必ず、ポインタiが右端の枢軸まで比較するが、降順の場合は
2回に1回最大値が左端にあり、その時は、ポインタiもポインタjも枢軸を比較しない(ポインタjはプログラム上枢軸と比較することはない)ので、同じ要素数でも降順の方が、比較回数が少ないか同じになって比較回数は昇順の方が多くなる。しかし、交換回数は同じで比較回数は昇順の方が少ないのに実行時間では、昇順方が早くなっている。これは関数
partitionの中のポインタiとポインタjの比較回数の違いである。ポインタiを比較している
while(a[++i] < pivot)とポインタjを比較しているwhile(i < --j && pivot <a[j])では、jを比較している文の方が、ループの条件が複雑である。ループの条件が複雑な方が少し時間が掛かる。iの比較回数とjの比較回数に差がたくさんあれば、実行時間に差が出て来るはずである。上に示した、昇順と降順の比較回数の理論値を説明する例において、iとjは図4−3のように比較回数が増える。
図4−3
要素数6の昇順と降順におけるポインタiとポインタjの比較回数|
種類 ソート回数 |
昇順 iの比較回数 |
昇順 jの比較回数 |
降順 iの比較回数 |
降順 jの比較回数 |
|
1 回目 |
6 |
1 |
1 |
5 |
|
2 回目 |
5 |
1 |
5 |
1 |
|
3 回目 |
4 |
1 |
1 |
3 |
|
4 回目 |
3 |
1 |
2 |
1 |
|
5 回目 |
2 |
1 |
2 |
1 |
|
合計 |
20 |
5 |
11 |
11 |
図4−3で解ることは、降順では、jが
1回多いだけで、iとjの比較回数はあまり変わらないが、昇順では、条件が複雑でないiの方がかなり多くなる。理論値を計算すると、昇順のiは(
m+2)/2×(m−1)=(u+m−2)/2になり、昇順のjは
m−1になる。降順はiもjも比較回数の半分になる。ところで、要素数
10000で理論値を考えると昇順のiの比較回数は50004999回、昇順のjは9999回、降順のiとjは25004999回となる。これは、計測した値(iとjを別々にカウントするプログラム)と一致し理論値が正しいことが解った。仮に条件が複雑になると、2倍実行速度が遅くなるとすると、要素数6の例では、昇順は20+5×2=
30、降順は11+11×2=33で降順の方が3多くなる。また、倍率では、1.1倍の違いがある。比較回数だけでは降順の方が3回多いのに、条件文の実行速度の違いを考えると、昇順の方が、実行速度が早くなったのが説明できる。(交換回数は同じで比較以外の手順に違いは無いから比較の違いが出ることになる。)
要素数
10000では、昇順は50004999+9999×2=50024997、降順25004999+25004999×2=75014997で1.5倍近い違いが出ることになるので、要素数が多ければ多い程違いが出やすい。適当に選んで2倍で計算してみたが、参考文献によると、「ループ条件が複雑になっても、実行時間にさほど違いは出ない」とあるので、実験値で実行時間を計算してみる(交換回数は比較回数に比べて小さいので無視した)。要素数
10000では、昇順/降順は1.05倍近く降順が遅くなっている。昇順のi+j×倍率/降順のi+j×倍率=1.05で倍率を計算すると、1.12になった。この条件文の複雑さの倍率を、要素数6で考えると、昇順は
25.6降順は23.32になる。要素数
10000で考えると、昇順は、50016198降順は、53010598になる。要素数が小さいと、昇順の方が、時間が掛かり(大きな差ではない)、要素数が大きくなると降順の方がかなり時間が掛かることになる。
これらのことから、ループ条件の複雑さによって昇順の実行時間が降順よりも早くなった、ということが説明できると思われる。
・ランダムとランダム(重複なし)は実行時間が小さくて理論値が出せないので、
m logmに比例しているかどうかわからなかった。要素数を増やすか、プログラムを工夫すべきだった。昇順と降順の実行時間の理論値はデータファイル毎に実行時間÷各要素数のu
の値を加算し、4で割り、その値を各要素数のu を掛けた値とした。実験に使用したワークステーションの処理速度が速いことと、プログラムで計測できる時間の最小が
0.0166… であったため、要素数500の実行時間は不正確である。要素数500以外の3つは、実行時間÷u の値が近い値になっても、要素数500だけ、他とずれてしまうことがあり、その実行時間の理論値は、ずれてしまう。実験では、比較回数は
m logmよりもかなり多い数になっている。これは、枢軸がちょうど中央値になっていないため、分割の作業の効率が悪いためである。また、配列の右端を枢軸にしてしまうと、昇順や降順のような数列では非常に手間が掛かるので、配列の中央の値と右端の値と左端の値のうち中央値を選んで枢軸とすることで、最悪の事態を避けて、実行速度を早めることができると考えられる。
・クイックソートの作業用メモリは参考文献によると「平均でオーダー
log m 」必要とあるので他の単純なソートアルゴリズム(使用メモリはオーダー 1)よりも使用するメモリが多い・アルゴリズムが他のソートより複雑で理解するのに時間が掛かった。
6、マージソート
マージソートのプログラムの一部(交換・比較回数のカウントを表す。
compare++;が比較回数のカウントで,exchange++;が交換回数のカウント)/*
作業用配列bの両端から取り出したデータをマージして配列aに入れる */i=low; j=high;
for(k=low;k<=high;k++){
compare++; exchange++;
if(b[i] <= b[j])
a[k] = b[i++];
else
a[k] = b[j--];
}
・比較回数は
if文の実行回数なので要素数m掛ける、マージの実行回数(関数を呼び出す回数)logmになる。よってマージソートのオーダーはm logmになる。・代入は、マージの時の比較を実行する度に行い、また、マージと同じ回数分だけ、分割時に代入を行っているため、比較回数の
2倍の回数が代入回数である。
図5
マージソートの実行時間/比較回数(比較回数の理論値)|
データの 並び方 要素数 m |
昇順・降順・ランダム・ランダム(重複なし)の 4種類全て実行時間(秒) 比較回数 実験値/理論値 m logm(回) |
|
500 |
0.000 4488 / 4482 |
|
1000 |
0.000 9976 / 9966 |
|
5000 |
0.033 61818 / 61439 |
|
10000 |
0.067 133616 / 132877 |
・図5の理論値は
m logmの小数点以下を四捨五入した。比較回数に少数点以下の数値はありえないので(要素数が3なら 3log3=4.75… だが、比較回数は5になり、理論値より少し大きくなる)実験値では、理論値よりも多い値になっている。・どのようなデータであろうと、同じ手順でマージソートは分割後、比較をして結合するため、
比較・代入回数、実行速度共に要素数が同じならデータの種類によらず、同じ値になった。
(要素数を増やすか、プログラムを改善すべきだった)
(作業用配列を使わないプログラムにする場合は、かなりの手間がかかり現実的ではないらしい)
・クイックソートのランダムと比べてみると、比較回数はマージソートの方が少ないが、代入回数(クイックソートの交換回数×3とマージソートの比較回数×2を比べる)ではマージソートの方がかなり多くなり、実行時間もマージソートの方が多い。
また、作業用配列を大き目に宣言しておくのではなく、整列する配列と同じ大きさの領域を確保する(
malloc関数を使う)ことによって無駄なメモリ消費を抑えることができると思われる。
4
まとめまず、3つの基本的なソートアルゴリズムを比較してみる。
図
6 要素数1万における基本ソートアルグリズムの比較回数/交換回数/実行時間|
データの種類
ソートの種類 |
昇順 実行時間(秒)比較回数(回) 交換回数(回) |
降順 |
ランダム |
ランダム (重複なし) |
|
バブルソート |
6.517 49995000 0 |
18.717 49995000 49995000 |
14.583 49995000 24820080 |
14.200 49995000 22762526 |
|
選択ソート |
9.967 49995000 0 |
12.633 49995000 25000000 |
9.983 49995000 80166 |
9.967 49995000 35743 |
|
挿入ソート |
0.000 9999 0 |
19.333 50004999 49995000 |
9.450 24830007 24820080 |
8.633 22712519 22762526 |
・選択ソートは他と比べてもデータの種類によらず、実行速度にあまり変化が無いことがわかる。
5つのソートアルゴリズムの実行速度を比べてみる。
図 7 ランダムデータの各アルゴリズムにおける整列時間(秒)
|
アルゴリズム 要素数 m |
バブル ソート |
選択ソート |
挿入ソート |
クイックソート |
マージソート |
|
500 |
0.033 |
0.033 |
0.017 |
0.000 |
0.000 |
|
1000 |
0.150 |
0.100 |
0.083 |
0.000 |
0.000 |
|
5000 |
3.517 |
2.367 |
2.317 |
0.017 |
0.033 |
|
10000 |
14.583 |
9.983 |
9.450 |
0.033 |
0.067 |
・ランダムのデータを扱っているので、やはり名前の通り、クイックソートが一番速い。
また、マージソートもなかなか速く基本的な3つのソートとは雲泥の差が出た。グラフにするとその差がはっきりと解る。
・クイックソートもマージソートも、扱った要素数が小さく、時間測定は上手くいかなかった。
(改善するには、要素数をもっと増やすか、プログラム上で
10回ソートを行い、実行に掛かった時間を10で割る、のようなことで他のアルゴリズムと比べやすいデータが取れたと考えられる)
5
結論今までの研究から図8のようなことがわかった。
図8 各アルゴリズムの長所・短所
|
長所 |
短所 |
|
|
バブルソート |
プログラミングしやすい (覚えやすい作りやすい) 使用メモリが少ない |
実行速度が全体的に遅い。 比較・交換回数が多い。 逆順の並びでは遅くなる |
|
選択ソート |
交換回数が非常に少ない。 データの並び方で交換・比較回数に変化があまり無く、実行時間が安定 使用メモリが少ない |
比較回数が多い 逆順の並びになっていると少し遅くなる |
|
挿入ソート |
整列に近いデータの整列は高速 使用メモリが少ない |
逆順のデータの整列に非常に弱い 交換回数が多い |
|
クイックソート |
もっとも高速 比較・交換回数が少ない |
最悪のケースでは遅くなるので、回避するプログラムを作る必要がある 使用メモリが多目 |
|
マージソート |
データの並び方に関係なく高速 比較・代入回数が少ない (外部記憶の整列に応用できる) |
メモリがたくさん必要 |
参考文献
近藤
嘉雪 著 『Cプログラマのためのアルゴリズムとデータ構造』