| クラス | ソートアルゴリズム |
|---|---|
| データ構造 | 配列 |
| 最悪の場合のパフォーマンス | |
| 平均的なパフォーマンス | ここで、kはバケットの数である。。 |
| 最悪の場合の空間計算量 |


バケットソート(またはビンソート)は、配列の要素を複数のバケットに分配することでソートを行うアルゴリズムです。各バケットは、異なるソートアルゴリズムを使用するか、バケットソートアルゴリズムを再帰的に適用することで個別にソートされます。これは、バケットごとに複数のキーを許容するピジョンホールソートの一般化である分布ソートであり、最上位桁から最下位桁への基数ソートの仲間です。バケットソートは比較を用いて実装できるため、比較ソートアルゴリズムとみなすこともできます。計算量は、各バケットのソートに使用するアルゴリズム、使用するバケットの数、および入力が均一に分布しているかどうかによって異なります。
バケットソートは次のように機能します。
関数bucketSort(array, k)は バケット ← k 個の空のリストの新しい配列 M ← 1 + 配列内の最大キー値 i = 0からlength(array)まで、配列[i]をbuckets[floor(k × array[i] / M)] に 挿入します。 i = 0からkまで、 nextSort(buckets[i]) buckets[0]、...、buckets[k]の連結を返します。
配列をソート対象配列、kを使用するバケット数とします。すべてのキーを一度反復することで、線形時間で最大キー値を計算できます。浮動小数点数を整数に変換するには、 floor 関数を使用する必要があります (データ型のキャストも必要になる場合があります)。nextSort 関数は、各バケットをソートするために使用されるソート関数です。慣例として、要素数が少ない場合に比較的高いパフォーマンスを発揮する挿入ソートが使用されますが、選択ソートやマージソートなどの他のアルゴリズムも使用できます。bucketSort自体を nextSort として使用すると、基数ソートの類似アルゴリズムが生成されます。特に、n = 2 の場合はクイックソートに対応します(ただし、ピボットの選択が適切でない可能性があります)。
入力に互いに近いキーが複数含まれている場合(クラスタリング)、それらの要素は同じバケットに配置される可能性が高く、その結果、一部のバケットには平均よりも多くの要素が含まれることになります。最悪のシナリオは、すべての要素が単一のバケットに配置される場合です。この場合、全体のパフォーマンスは、各バケットをソートするために使用されるアルゴリズムによって大きく左右されます。たとえば、挿入ソートまたはマージソートなどの比較ソートアルゴリズム。
入力が均一に分布している場合を考えてみましょう。最初のステップであるバケットの初期化と配列内の最大キー値の検索は、次のように実行できます。時間。除算と乗算が一定時間で実行できる場合、各要素をそれぞれのバケットに分散させるのにもコストがかかります。各バケットのソートに挿入ソートを使用すると仮定すると、3 番目のステップのコストは、 どこバケットの長さはインデックス付けされています平均時間について考えているので、期待値は代わりに評価する必要があります。確率変数とは、要素の場合バケツに入れられる、 そしてそうでなければ。。 したがって、
最後の行は総和をケースごとに分割しますそしてケースオブジェクトがバケットに分散される可能性は、確率1でそれ以外の場合は0。
合計すると、
最後に、複雑さは。
バケットソートの最後のステップは、各バケット内のソート済みオブジェクトをすべて連結することです。時間。したがって、全体の複雑さはk を次のように選択すると、バケットソートは一様分布の入力が与えられた場合の平均時間。[ 1 ]
一般的な最適化方法としては、まずバケットの未ソート要素を元の配列に戻し、次に配列全体に対して挿入ソートを実行する方法があります。挿入ソートの実行時間は各要素が最終位置からどれだけ離れているかに基づいているため、比較回数は比較的少なく、リストをメモリに連続して格納することでメモリ階層をより有効に活用できます。[ 2 ]
入力分布が既知であるか推定可能な場合、一定密度(単に一定サイズであるだけでなく)を含むバケットを選択できることが多い。これにより、入力が均一に分布していなくても、平均的な時間計算量は以下の通りである。
バケットソートの最も一般的なバリアントは、0 からある最大値Mまでのn 個の数値入力のリストに対して動作し、値の範囲をそれぞれサイズM / bのb 個のバケットに分割します。各バケットを挿入ソートを使用してソートすると、ソートは期待線形時間で実行されることが示されています (すべての可能な入力について平均が取られます)。[ 3 ]ただし、このソートのパフォーマンスはクラスタリングによって低下します。多くの値が近くに発生すると、それらはすべて単一のバケットに落ち込み、ソートが遅くなります。このパフォーマンスの低下は、入力が区間[0,1)に要素を均等に分布させるランダムプロセスによって生成されると仮定することで、元のバケットソートアルゴリズムで回避されています。[ 1 ]
前述の一般的なバケットソートと同様に、ProxmapSort は、キーの部分的な順序を保持する「マップキー」関数を使用してキーの配列をサブ配列に分割することで機能します。各キーがサブ配列に追加される際に、挿入ソートを使用してそのサブ配列のソート状態を維持し、ProxmapSort が完了すると配列全体がソートされた状態になります。ProxmapSort は、マップキーを使用してデータをソートされた順序で適切な場所に配置する点でバケットソートと異なり、キーの「プロックスマップ」(近接マッピング)を生成します。
バケットソートの別のバリアントであるヒストグラムソートまたはカウンティングソートでは、カウント配列を使用して各バケットに収まる要素の数をカウントする最初のパスが追加されます。[ 4 ]この情報を使用して、配列の値は交換のシーケンスによってその場でバケットのシーケンスに配置され、バケットのストレージのためのスペースのオーバーヘッドは発生しません。
ポストマンソートは、要素の階層構造を利用するバケットソートの一種で、通常は属性のセットで記述されます。これは、郵便局の郵便仕分け機で使用されているアルゴリズムです。郵便物はまず国内郵便と国際郵便に分けられ、次に州、県、または準州ごとに、次に宛先の郵便局ごとに、次にルートごとに、といった具合に仕分けされます。キー同士が比較されないため、ソート時間は O( cn ) で、c はキーのサイズとバケットの数に依存します。これは、「トップダウン」または「最上位桁から」動作する基数ソートに似ています。 [ 5 ] [ 6 ]
シャッフルソート[ 7 ]はバケットソートの変種で、まずソート対象のn個のアイテムのうち最初の1/8を取り除き、それらを再帰的にソートして配列に格納します。これにより、残りの7/8個のアイテムが分配されるn /8個の「バケット」が作成されます。次に、各「バケット」がソートされ、それらの「バケット」が連結されてソート済みの配列が作成されます。
バケットソートは、計数ソートの一般化と見なすことができます。実際、各バケットのサイズが1の場合、バケットソートは計数ソートに退化します。バケットソートの可変バケットサイズにより、異なる値の数をMとした場合、O( M )のメモリではなくO( n )のメモリを使用できます。その代わりに、計数ソートのO( n + M )という最悪の場合の動作を放棄します。
2つのバケットを用いたバケットソートは、実質的にはクイックソートの一種であり、ピボット値は常に値の範囲の中央値として選択されます。この選択方法は入力が均一に分布している場合は効果的ですが、クイックソートでピボットを選択する他の方法(例えばランダムにピボットを選択する方法)を用いると、入力分布におけるクラスタリングに対してより耐性が高まります。
n分割マージソートアルゴリズムも、リストを n 個のサブリストに分割してそれぞれをソートすることから始まります。ただし、マージソートによって作成されるサブリストは値の範囲が重複しているため、バケットソートのように単純な連結で再結合することはできません。代わりに、マージアルゴリズムによってそれらをインターリーブする必要があります。しかし、この追加コストは、より単純な分散フェーズと、各サブリストのサイズが同じであることを保証できる機能によって相殺され、最悪ケースの時間制約が良好になります。
トップダウン基数ソートは、値の範囲とバケット数の両方が2のべき乗に制限されるバケットソートの特殊なケースと見なすことができます。したがって、各バケットのサイズも2のべき乗となり、この手順は再帰的に適用できます。このアプローチでは、各要素のビット表現の接頭辞を調べるだけでバケットを特定できるため、スキャッターフェーズを高速化できます。
バケットソートは平均して線形時間で実行されます。カウントソートと同様に、バケットソートは入力について何らかの仮定を置くため高速です。カウントソートは入力が小さな範囲の整数で構成されていると仮定しますが、バケットソートは入力が区間[0,1)に要素を均等に分布させるランダムプロセスによって生成されると仮定します。バケットソートの考え方は、区間[0, 1)をn 個の等しいサイズのサブ区間、つまりバケットに分割し、 n 個の入力数値をバケットに分配することです。入力は[0, 1)に均等に分布しているため、各バケットに多くの数値が入るとは予想されません。出力を生成するには、各バケットの数値をソートし、バケットを順番に処理して、各バケットの要素をリストします。