アルゴリズムとデータ構造:クイックソート
[クイックソートのロジック]
1.ある値を基準として、それより大きい値は後ろ、小さい値は前へ移動
2.2つのグループそれぞれでまた適当な値を基準にして大きい値は後ろ、小さい値は前へ移動
3.またそれぞれのグループで同じ様に、1-2を繰り返す
4.グループ分けが出来なくなった時点で終了
[計算量のオーダ]
作業の効率性を比較する指標。
[クイックソートの計算量]
最良:O(N Log N)
最悪:O(N2)
平均:O(N Log N)
[ソースコード:quick_sort.c]
#include <stdio.h> #include <stdlib.h> #include <time.h> #define N 10 /* データ件数 */ int sort[N]; /* 対象データリスト */ int cnt; /* 交換回数 */ void output(void); void QuickSort(int bottom, int top, int *data); void QuickSort(int bottom, int top, int *data){ int lower, upper, div, temp; /* 再帰処理を終了するために必要 */ if(bottom >= top){ return ; } /* 先頭の値を適当な値とする */ div = data[bottom]; for(lower = bottom, upper = top; lower < upper;){ /* 閾値より大きい数を持つ配列番号を小さい配列番号から順に検索 */ while(lower <= upper && data[lower] <= div){ lower++; } /* 閾値より小さい数を持つ配列番号を大きい配列番号から順に検索 */ while(lower <= upper && data[upper] > div){ upper--; } /* 配列番号が以下の条件に合えば入れ替え発生 */ if(lower < upper){ temp = data[lower]; data[lower] = data[upper]; data[upper] = temp; output(); } ++cnt; } /* 最初に選択した値を中央に移動する */ temp = data[bottom]; data[bottom] = data[upper]; data[upper] = temp; output(); /* 配列の先頭まで繰り返す */ QuickSort(bottom, upper - 1, data); /* 配列の最後尾まで繰り返す */ QuickSort(upper + 1, top, data); } int main(void){ int i; time_t t; srand((unsigned int) time(NULL)); printf("Standby...\n"); for(i = 0; i < N; i++){ /* 配列にランダムな値を格納 */ sort[i] = rand() % 10; } printf("Before => "); output(); printf("Start:%ld\n",time(&t)); QuickSort(0, N - 1, sort); printf("End:%ld\n",time(&t)); printf("After => "); output(); printf("Count:%d\n",cnt); return EXIT_SUCCESS; } void output(void){ int i; for(i = 0; i < N; i++){ printf("%d,",sort[i]); } printf("\n"); }
データ件数10件、10未満の乱数を発生させて実行結果
$ ./quick_sort Standby... Before => 2,9,0,7,8,0,3,7,1,0, Start:1322690066 2,0,0,7,8,0,3,7,1,9, 2,0,0,1,8,0,3,7,7,9, 2,0,0,1,0,8,3,7,7,9, 0,0,0,1,2,8,3,7,7,9, 0,0,0,1,2,8,3,7,7,9, 0,0,0,1,2,8,3,7,7,9, 0,0,0,1,2,7,3,7,8,9, 0,0,0,1,2,7,3,7,8,9, 0,0,0,1,2,3,7,7,8,9, End:1322690066 After => 0,0,0,1,2,3,7,7,8,9, Count:9
ロギングをコメントアウト、乱数の制限も解除、データ件数を1万件にして実行
$ ./quick_sort Standby... Before => Start:1322690264 End:1322690264 After => Count:31519
すごく速いのでもっと件数を増やしてみる。
1000万件でやっと処理時間が1秒を超えた。
$ ./quick_sort Standby... Before => Start:1322690412 End:1322690416 After => Count:54921891
改修予定
- 処理時間をマイクロ秒で出力する
- 引数をデータ件数にする
C言語の標準関数のqsortは処理系によって異なりますが
おおよそクイックソートで実装されているそうです。
クイックソートは基本的にN Log Nの計算量で効率が良いが
元のデータの並び方が悪いとバブルソートと同じ計算量になってしまう。
確実に効率よくソートする方法はないか?ということで
次回のマージソートに続きます。