安定しており、O(n)の時間の複雑さがあります。QuicksortやMergesortなどのアルゴリズムよりも高速であるべきですが、使用されることはほとんどありません。
安定しており、O(n)の時間の複雑さがあります。QuicksortやMergesortなどのアルゴリズムよりも高速であるべきですが、使用されることはほとんどありません。
回答:
基数ソートとは異なり、クイックソートは汎用的ですが、基数ソートは固定長の整数キーにのみ有用です。
また、O(f(n))は実際にはK * f(n)の順序を意味することを理解する必要があります。ここで、Kは任意の定数です。基数ソートの場合、このKはたまたまかなり大きく(少なくともソートされた整数のビット数の順序)、クイックソートはすべてのソートアルゴリズムの中で最も低いKの1つであり、n * log(n)の平均複雑度です。したがって、実際のシナリオでは、クイックソートは基数ソートよりも非常に頻繁に高速になります。
ほとんどのソートアルゴリズムは汎用です。比較関数を指定すると、それらは何でも動作し、QuicksortやHeapsortなどのアルゴリズムはO(1)の追加メモリでソートされます。
基数ソートはより専門的です。辞書編集順になっている特定のキーが必要です。キー内の可能なシンボルごとに1つのバケットが必要であり、バケットは多くのレコードを保持する必要があります。(あるいは、考えられるすべてのキー値を保持するバケットの1つの大きな配列が必要です。)基数ソートを行うには、より多くのメモリが必要になる可能性が高く、ランダムに使用します。Quicksortでキャッシュミスが発生するなど、ページフォールトが発生する可能性が高いため、これはどちらも現代のコンピューターには適していません。
最後に、人々は一般的に、もはや独自のソートアルゴリズムを作成しません。ほとんどの言語には、並べ替えるライブラリ機能があり、通常は正しい言語を使用します。基数ソートは普遍的に適用可能ではなく、通常は実際の使用に合わせて調整する必要があり、多くの追加メモリを使用するため、ライブラリ関数またはテンプレートに入れるのは困難です。
O(n^2)
ため、最悪の場合、クイックソートにはメモリが必要n
です。実装が末尾再帰最適化を使用している場合、O(n)
適切なパーティションへの呼び出しに余分なスペースが必要ないため、これを下げることができます。(en.wikipedia.org/wiki/Quicksort#Space_complexity)
S(n) \in O(n)
基数でソートするためのスペースのみが必要です。つまり、ヒープまたはクイックソートと同じです。
n^2
クイックソートについてはもう言及していないようですが、O(log n)
...
ソートするキーが実際に既知の疎な範囲の整数であることは非常にまれです。通常、非比較ソートをサポートしているように見えるアルファベットフィールドがありますが、実際の文字列はアルファベット全体に均等に分散されていないため、理論的にはうまくいきません。
それ以外の場合、基準は運用上のみ定義されます(2つのレコードが与えられた場合、どちらが最初になるかを決定できますが、分離されたレコードがどの程度スケールダウンするかを評価することはできません)。そのため、この方法は多くの場合、適用できないか、信じられないほど適用性が低いか、O(n * log(n))よりも高速ではありません。
私は実際に比較ベースのソートよりも常にそれを使用していますが、私は明らかに、他の何よりも数字でよりうまく動作する奇妙なボールです(私はほとんど文字列では動作しません。並べ替えは、重複を除外して集合の交差を計算するのに再び役立ちます。辞書編集による比較は実際には行いません)。
基本的な例は、検索または中央分離の一部として与えられた次元でポイントを基数でソートすること、一致ポイント、深度ソートフラグメント、または複数のループで使用されるインデックスの配列を基数ソートして、キャッシュフレンドリーなアクセスを提供する迅速な方法です。パターン(再び戻って同じメモリをキャッシュラインにリロードするためだけにメモリを行ったり来たりしない)。少なくとも私のドメイン(コンピューターグラフィックス)には、固定サイズの32ビットおよび64ビットの数値キーでソートするための非常に幅広いアプリケーションがあります。
私が提案したいことの1つは、基数ソートは浮動小数点数と負数に対して機能するということです。ただし、可能な限り移植性の高いFPバージョンを作成することは困難です。また、O(n * K)ですが、Kはキーサイズのバイト数である必要があります(例:100万の32ビット整数は、バケットに2 ^ 8エントリがある場合、通常4バイトサイズのパスを取得します)。メモリアクセスパターンは、通常、並列配列と小さなバケット配列を必要とする場合でも、クイックソートよりもはるかにキャッシュフレンドリーになる傾向があります(2番目の配列は通常、スタックにうまく収まります)。QSは、5000万のスワップを実行して、散発的なランダムアクセスパターンで100万の整数の配列をソートします。基数ソートは、データに対する4つの線形のキャッシュフレンドリーパスでこれを実行できます。
ただし、浮動小数点と一緒に負の数で小さなKでこれを実行できるという認識の欠如は、基数ソートの人気の欠如に大きく貢献する可能性があります。
なぜ人々がそれをより頻繁に使用しないのかについての私の意見に関しては、それは一般的に数字を並べ替えたり検索キーとして使用する必要がない多くのドメインに関係しているかもしれません。しかし、私の個人的な経験に基づいて、以前の同僚の多くは、それが完全に適している場合、それがFPとネガで動作するようにできることを知らなかったため、それを使用しませんでした。そのため、数値型のみで機能することは別として、実際よりも一般的に適用されることは少ないと考えられています。浮動小数点数と負の整数で機能しないと思った場合も、ほとんど使用しません。
いくつかのベンチマーク:
Sorting 10000000 elements 3 times...
mt_sort_int: {0.135 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
mt_radix_sort: {0.228 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
std::sort: {1.697 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
qsort: {2.610 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
そして、それは私の素朴な実装だけです(mt_sort_int
基数ソートでもありますが、キーが整数であると想定できる場合、コードのより高速な分岐があります)。専門家によって書かれた標準的な実装がどれくらい速いか想像してみてください。
基数ソートがC ++の非常に高速な比較ベースよりも悪いと判断した唯一のケースはstd::sort
、32などの非常に少数の要素の場合で、この時点std::sort
で、ヒープソートや挿入はソートしますが、その時点で私の実装は単にを使用しstd::sort
ます。
もう1つの理由:最近のソートは、通常、コンパイラーが提供するソートロジックにアタッチされたユーザー提供のソートルーチンで実装されます。基数ソートでは、これはかなり複雑になり、ソートルーチンが可変長の複数のキーに作用するとさらに悪化します。(言う、名前、生年月日。)
現実の世界では、一度基数ソートを実際に実装しました。これは、メモリが制限されていた昔で、一度にすべてのデータをメモリに取り込むことができませんでした。つまり、データへのアクセス数は、O(n)対O(n log n)よりもはるかに重要でした。各レコードをビンに割り当てるデータを1回パスしました(実際には何も移動せずに、どのレコードがどのビンにあったのかをリストします)。空でないビンごとに(ソートキーはテキストで、空のビン)実際にデータをメモリに取り込むことができるかどうかをチェックしました-はいの場合は、データを取り込んでクイックソートを使用します。いいえの場合、ビン内の項目のみを含む一時ファイルを作成し、ルーチンを再帰的に呼び出します。(実際には、オーバーフローするビンはほとんどありません。)これにより、ネットワークストレージへの2回の完全な読み取りと1回の完全な書き込みが発生し、その10%がローカルストレージになりました。
最近では、このようなビッグデータの問題に遭遇するのははるかに難しく、おそらくそのようなことは二度と書きません。(最近同じデータに直面した場合、単純に64ビットOSを指定し、そのエディターでスラッシングが発生した場合はRAMを追加します。)
すべてのパラメーターが整数で、1024を超える入力パラメーターがある場合、基数ソートは常に高速です。
どうして?
Complexity of radix sort = max number of digits x number of input parameters.
Complexity of quick sort = log(number of input parameters) x number of input parameters
したがって、基数ソートは次の場合に高速になります。
log(n)> max num of digits
Javaの最大整数は2147483647です。これは10桁の長さです
したがって、基数ソートは常に高速です
log(n)> 10
したがって、基数ソートは常に高速です
n>1024