データをソートする処理をよく使いますが、どのようなアルゴリズムになっているのでしょうか? また一番速い方法はどれでしょうか?

挿入ソート

挿入ソートは、データ列を整列済みとそうでないものに分け、未整列な部分からデータを1つ取り出し、整列済み部分の適切な位置に挿入することを繰り返す手法です。未整列な部分がなくなるまで処理を繰り返すと、ソートされた配列にすることができます。

選択ソート

選択ソートは、挿入ソートと同じように整列済み部分とそうでない部分にわけるところまでは同じですが、未整列な部分から最小値を取り出し(昇順の場合)、整列済み部分の末尾に付け加えることを繰り返す手法です。

バブルソート

バブルソートは、データ列の隣り合う要素を比較し交換することを繰り返すことによりデータ列をソートする手法です。ソートの過程でデータが移動する様子が、水中で泡が浮かんでいくように見えることからこのような名前になっています。前二者と比べるとコードが短いです。

シェルソート

シェルソートは、挿入ソートを改良したアルゴリズムです。挿入ソートが整列済みのデータ列に強いことを利用しています。

シェルソートは、データ列において一定の間隔 h だけ離れた要素からなる部分列を対象に、挿入ソートをhを小さくしながら繰り返します。h は適当に大きな値から始め、段階的に小さくしていき、最終的に 1 にします。

シェルソートの計算量は間隔列 H に強く依存します。シェルソートの計算量解析を正確に行うことは難しく未解決なのですが、いくつかの有名な間隔列に対しては計算量解析が行われています。例えば漸化式 Hi = 3 * H(i+1) + 1を満たす整数列、1, 4, 13, 40, 121, 364を逆にしたものを間隔列とした場合のシェルソートは平均計算量が O(n^{1.25}) になることが知られています。

マージソート

マージソートは、データ列を二分し、それぞれをソートした後それらを統合することを繰り返すソートアルゴリズムです。

クイックソート

クイックソートは、ピボットと呼ばれる値を1つ選び、それを基準としてデータ列をピボット未満の要素とピボット以上の要素のふたつにわける処理を再帰的に行うアルゴリズムです。

ピボットの選び方にはいくつか方法があります。

データ列の先頭をとる
データ列の末尾をとる
データ列からランダムにとる
データ列からランダムに2つとり、その中央値をとる

ピボットを選択したら、データ列の先頭からデータを1つずつ確認していき、ピボット未満のデータをデータ列の先頭に集め、ピボットをそれより左側がピボット未満、右側がピボット以上となるように移動します。ここでデータ列を二つにわけ、分割された2つのデータ列に対して再び同じ操作を繰り返します。

処理が高速なのはどれ?

実際にどれくらい時間がかかるのか計測してみることにします。

クイックソートとマージソートはすぐに終わりますが、選択ソートと挿入ソートとバブルソートは時間がかかります。

以下のようなコードを書いて実験してみることにします。

クイックソートとマージソートのほうが速いです。

では挿入ソートと選択ソート、バブルソートの場合はどうでしょうか?

速い順に挿入ソート、選択ソート、バブルソートです。

シェルソートと挿入ソートの比較

次にシェルソートと挿入ソートを比較してみることにします。

シェルソートのほうが圧倒的に速いです。マージソートとも比較してみましたが、マージソートのほうが速いです。

クイックソートが一番速い?

いまのところクイックソートが一番速いですが、いつも使っているLinq.OrderByメソッドをつかったソートとではどちらが速いのでしょうか? int型配列で試してみましたが、クイックソートのほうが約1.6倍速いことがわかりました。

またint型配列をソートするのであればArray.Sort()を使うのが一番速いです。クイックソートと比べると3倍以上違います。