ソートのアルゴリズムの一つにバケットソート(bucket sort)があります。

バケットソート(bucket sort)とは?

バケットとはバケツのことです。このソートアルゴリズムはどんな場合でも使えるわけではありませんが、データの取りうる種類数が限定されている場合は強力な手法となります。

データの取りうる種類数が m 種類であることがわかっているなら、まず m 個のバケツを用意し、値ごとに1個のバケツを対応づけます。次に元のデータ列を走査して、各データを対応するバケツに入れていきます。最後に、整列したい順序に従ってバケツから値を取り出すことで、データをソートすることができます。

データ数が N 個でバケツの数が K 個のとき、計算量はO(N + K)です。これは普通のソートの計算量であるO(NlogN)と比較すると高速であるといえます。

ただしデータの取りうる種類数が多すぎるとバケツを用意することができなくなるので、データの取りうる値が実数全体であったり、整数であっても最大値と最小値の差が大きすぎるなどの理由でその種類数が多い場合は使えないので万能の方法ではありません。

バケットソートの例を示します。以下のコードは実際にバケツを用意するのではなく、各データの個数を数えることで、ソート後のデータの位置を算出してメモリーの消費量を抑えています。

バケットソートは速かった

試しに動作速度を比較してみることにします。ここでは 0 ~ 100,000 – 1 の乱数をつかって長さ1,000,000の配列を生成してソートにかかる時間を計測しています。

差は歴然です。

基数ソートとは?

あとバケットソートを応用したアルゴリズムに基数ソートがあります。

これは最下位桁から最上位桁へ向けてソートする処理を繰り返すと、全体が順序通りに並ぶという手法です。

たとえば 170, 45, 75, 90, 2, 24, 802, 66 をソートする場合を考えます。

まず1の位でソートします。すると、170, 90, 2, 802, 24, 45, 75, 66 となります。次に10の位でソートします。すると 2, 24, 45, 66, 170, 75, 802, 90 のようになります。このとき、ソートは安定ソートでなければなりません。

次に100の位でソートすると 2, 24, 45, 66, 75, 90, 170, 802 となります。1000の位以上はすべて0なのでこれ以上処理を続けても順番は変わりません。そして結果を見てみるとたしかにソートされています。

実際にこの方法で実装して実験してみたのですが、各桁の数を取得するのに時間がかかるらしくあまりいい結果にはなりませんでした。そこで10進法ではなく256進法で考えてみることにします。これだとビット演算で各桁の値を計算するので処理が軽くなります。

基数ソートもそれなりに速かった

long.MinValue以上long.MaxValue以下の乱数を 1,000,000個生成して普通のソートと実行速度の比較をしてみることにします。

バケットソートを複数回おこなっているのでやや遅くなっていますが、それでもArray.Sortメソッドよりは速いみたいです。