面接でこの質問をされました。どちらもO(nlogn)ですが、ほとんどの人はMergesortではなくQuicksortを使用しています。何故ですか?
"easier to hack a mergesort to do it than a quicksort"
ですか?引用できる具体的な例はありますか?
面接でこの質問をされました。どちらもO(nlogn)ですが、ほとんどの人はMergesortではなくQuicksortを使用しています。何故ですか?
"easier to hack a mergesort to do it than a quicksort"
ですか?引用できる具体的な例はありますか?
回答:
クイックソートには、O(n 2)ワーストケースランタイムとO(n log n)平均ケースランタイムがあります。ただし、多くの要因がアルゴリズムのランタイムに影響を与えるため、多くのシナリオでマージソートの方が優れています。これらすべてをまとめると、クイックソートが優先されます。
特に、よく引用されるソートアルゴリズムのランタイムとは、データのソートに必要な比較の数またはスワップの数を指します。これは、特に基礎となるハードウェア設計とは独立しているため、確かにパフォーマンスの優れた指標です。ただし、参照の局所性(つまり、おそらくキャッシュにある多くの要素を読み取るか)などの他の事柄も、現在のハードウェアで重要な役割を果たします。特にクイックソートは、追加のスペースをほとんど必要とせず、キャッシュの局所性が優れているため、多くの場合、マージソートよりも高速になります。
さらに、ピボットの適切な選択を使用することで、クイックソートのO(n 2)の最悪の場合の実行時間をほぼ完全に回避できます(ランダムに選択するなど)(これは優れた戦略です)。
実際には、クイックソートの多くの最新の実装(特にlibstdc ++のstd::sort
)は実際にはイントロソートであり、その理論的な最悪のケースはマージソートと同じO(n log n)です。これは、再帰の深さを制限し、log nを超えると別のアルゴリズム(ヒープソート)に切り替えることでこれを実現します。
多くの人が指摘しているように、クイックソートの平均ケースパフォーマンスはマージソートよりも高速です。 ただし、これは、オンデマンドで任意のメモリにアクセスするための一定の時間を想定している場合にのみ当てはまります。
RAMでは、この仮定は一般的に悪くありません(キャッシュのために常にそうであるとは限りませんが、悪くありません)。あなたのデータ構造がディスク上に生きるために十分に大きいである場合しかし、その後、クイックソートます殺さランダム200は毎秒追求のようなあなたの平均的なディスクが何かをするという事実によって。しかし、同じディスクでも、メガバイト/秒のデータを順番に読み書きするのに問題はありません。これはまさにmergesortが行うことです。
したがって、データをディスク上でソートする必要がある場合は、mergesortのバリエーションを実際に使用したいと考えています。(通常、サブリストをクイックソートしてから、サイズのしきい値を超えてそれらをマージし始めます。)
さらに、そのサイズのデータセットで何かをする必要がある場合は、ディスクへのシークを回避する方法について十分に検討してください。たとえば、データベースで大量のデータをロードする前にインデックスを削除し、後でインデックスを再構築することが標準的なアドバイスであるのはこのためです。ロード中にインデックスを維持することは、常にディスクをシークすることを意味します。対照的に、インデックスを削除する場合、データベースは、最初に処理する情報を(もちろん、mergesortを使用して!)ソートし、次にそれをインデックスのBTREEデータ構造にロードすることにより、インデックスを再構築できます。(BTREEは自然に順番に保持されるため、ディスクへのシークがほとんどないソート済みデータセットからロードできます。)
ディスクシークを回避する方法を理解することで、データ処理ジョブに数日または数週間ではなく数時間かかる場合がいくつかあります。
0
向かっn
て、次にからにn
向かっていき0
ます。これには、メモリ(キャッシュ)ですでに使用可能なデータブロックを後退(ソート)し、1回のディスクアクセスで2回攻撃するという利点があります。ほとんどのDBMSはこの最適化手法を使用していると思います。
実際、QuickSortはO(n 2)です。その平均ケース実行時間はO(nlog(n))ですが、最悪のケースはO(n 2)です。これは、一意のアイテムをほとんど含まないリストで実行すると発生します。ランダム化はO(n)を取ります。もちろん、これで最悪のケースが変わることはありません。悪意のあるユーザーが並べ替えに時間がかかるのを防ぐだけです。
QuickSortは、次の理由により人気があります。
「それでも、ほとんどの人はMergesortの代わりにQuicksortを使用しています。なぜですか?」
与えられていない心理的な理由の1つは、Quicksortがより巧妙に命名されていることです。すなわちよいマーケティング。
はい、トリプルパーティショニングを使用したクイックソートは、おそらく最も優れた汎用ソートアルゴリズムの1つですが、「クイック」ソートは「マージ」ソートよりもはるかに強力に聞こえるという事実を克服することはできません。
他の人が指摘したように、Quicksortの最悪のケースはO(n ^ 2)ですが、mergesortとheapsortはO(nlogn)のままです。ただし、平均的なケースでは、3つすべてがO(nlogn)です。ですから、それらは大多数の場合に匹敵します。
クイックソートの平均的な改善点は、内部ループが複数の値を1つの値と比較することを意味し、他の2つの値は比較ごとに異なることです。つまり、Quicksortは、他の2つのアルゴリズムの半分の数の読み取りを実行します。最近のCPUでは、パフォーマンスはアクセス時間によって大きく左右されるため、結局のところ、クイックソートは最初の選択肢として最適です。
これまでに述べた3つのアルゴリズム(マージソート、クイックソート、ヒープソート)のうち、マージソートのみが安定しているアルゴリズムを追加したいと思います。つまり、同じキーを持つ値の順序は変わりません。これが望ましい場合もあります。
しかし、実のところ、実際の状況では、ほとんどの人は良い平均パフォーマンスしか必要とせず、クイックソートは... quick =)
すべてのソートアルゴリズムには、浮き沈みがあります。概要については、Wikipediaのソートアルゴリズムの記事を参照してください。
クイックソートは、別の再帰的ソートアルゴリズムであるマージソートとも競合しますが、最悪の場合のΘ(nlogn)実行時間の利点があります。Mergesortは、quicksortやheapsortとは異なり、安定したソートであり、リンクリストや、ディスクストレージやネットワーク接続ストレージなどのアクセスの遅いメディアに保存された非常に大きなリストを操作するように簡単に調整できます。リンクされたリストを操作するようにクイックソートを作成できますが、ランダムアクセスなしでは、ピボットの選択が不十分になることがよくあります。マージソートの主な欠点は、配列を操作する場合、最良の場合にはΘ(n)補助スペースが必要ですが、インプレースパーティション分割と末尾再帰を使用するクイックソートのバリアントはΘ(logn)スペースのみを使用することです。(リンクリストを操作する場合、mergesortが必要とするのは少量の一定量の補助記憶域だけであることに注意してください。)
ムー! クイックソートの方が優れているわけではなく、マージソートとは異なる種類のアプリケーションに適しています。
Mergesortは、速度が本質的なものであり、最悪の場合のパフォーマンスの低下を許容できず、追加のスペースが利用可能であるかどうかを検討する価値があります。1
あなたは彼らが«彼らは両方ともO(nlogn)[…]»だと述べました。これは間違っています。«最悪の場合、Quicksortは約n ^ 2/2の比較を使用します。» 1。
しかし、私の経験によれば、最も重要な特性は、命令型パラダイムでプログラミング言語を使用するときにソート中に使用できる順次アクセスの簡単な実装です。
1セジウィック、アルゴリズム
ウィキペディアの説明は:
通常、クイックソートは他のΘ(nlogn)アルゴリズムよりも実際に非常に高速です。これは、その内部ループがほとんどのアーキテクチャで効率的に実装でき、ほとんどの実際のデータでは、2次時間を必要とする確率を最小限に抑える設計の選択を行うことができるためです。 。
クイックソートの実装にはないMergesort(Ω(n))に必要なストレージの量にも問題があると思います。最悪の場合、それらは同じアルゴリズム時間ですが、mergesortはより多くのストレージを必要とします。
既存の優れた回答に加えて、QuickSortが最良のケースから逸脱した場合のパフォーマンスとその可能性についていくつかの数学を追加したいと思います。これが、O(n ^ 2)ケースが本当ではない理由を人々が少しよく理解するのに役立つことを願っていますQuickSortのより洗練された実装への懸念。
ランダムアクセスの問題以外に、QuickSortのパフォーマンスに影響を与える可能性のある2つの主な要因があり、どちらもピボットと並べ替えられているデータとの比較に関連しています。
1)データ内の少数のキー。ピボットの位置を除くすべての値が毎回片側に配置されるため、すべて同じ値のデータセットは、バニラ2パーティションQuickSortでn ^ 2時間でソートされます。最近の実装では、3パーティションソートを使用するなどの方法でこれに対処しています。これらのメソッドは、O(n)時間ですべて同じ値のデータセットに対して実行されます。したがって、このような実装を使用すると、少数のキーを使用した入力で実際にパフォーマンス時間が向上し、もはや問題ではなくなります。
2)ピボットの選択が極端に悪いと、最悪の場合のパフォーマンスが発生する可能性があります。理想的なケースでは、ピボットは常に50%のデータが小さく、50%のデータが大きいため、各反復中に入力が半分に分割されます。これにより、O(n * logn)時間に対するlog-2(n)再帰のn回の比較とスワップ時間が得られます。
非理想的なピボット選択は実行時間にどのくらい影響しますか?
データの75%がピボットの片側にあるようにピボットが一貫して選択されているケースを考えてみましょう。まだO(n * logn)ですが、ログのベースが1 / 0.75または1.33に変更されました。ベースを変更するときのパフォーマンスの関係は、常にlog(2)/ log(newBase)で表される定数です。この場合、その定数は2.4です。したがって、このピボット選択の品質は、理想の2.4倍かかります。
これはどれほど速く悪化しますか?
ピボットの選択が(一貫して)非常に悪くなるまで、それほど速くありません:
片側で100%に近づくと、実行のログ部分はnに近づき、実行全体は漸近的にO(n ^ 2)に近づきます。
QuickSortの素朴な実装では、ソートされた配列(最初の要素ピボットの場合)または逆ソートされた配列(最後の要素ピボットの場合)などのケースは、最悪の場合のO(n ^ 2)実行時間を確実に生成します。さらに、予測可能なピボット選択のある実装は、最悪の場合の実行を生成するように設計されたデータによるDoS攻撃を受ける可能性があります。最新の実装では、ソート前にデータをランダム化する、3つのランダムに選択されたインデックスの中央値を選択するなど、さまざまな方法でこれを回避しています。このランダム化の組み合わせでは、2つのケースがあります。
ひどいパフォーマンスを見る可能性はどのくらいありますか?
チャンスはある無視できるほどに小さいです。5,000種類の値について考えてみましょう。
架空の実装では、ランダムに選択された3つのインデックスの中央値を使用してピボットを選択します。25%〜75%の範囲にあるピボットは「良好」と見なし、0%-25%または75%-100%の範囲にあるピボットは「不良」と見なします。3つのランダムインデックスの中央値を使用して確率分布を見ると、再帰ごとに11/16の確率で最終的に適切なピボットが得られます。計算を単純化するために、2つの保守的な(そして誤った)仮定をしてみましょう。
優れたピボットは常に正確に25%/ 75%の分割であり、2.4 *理想的なケースで動作します。理想的な分割、または25/75を超える分割はありません。
悪いピボットは常に最悪のケースであり、本質的にソリューションには何も貢献しません。
QuickSortの実装はn = 10で停止し、挿入ソートに切り替わるので、5,000個の値の入力をそこまで壊すには、22個の25%/ 75%ピボットパーティションが必要です。(10 * 1.333333 ^ 22> 5000)または、4990の最悪の場合のピボットが必要です。いずれかの時点で22個の優れたピボットが蓄積されると、並べ替えが完了するため、最悪の場合、またはそれに近いものは、非常に運が悪いことを覚えておいてください。n = 10にソートするために必要な22の優れたピボットを実際に達成するために88回の再帰が必要な場合、それは4 * 2.4 *理想的なケース、または理想的なケースの実行時間の約10倍になります。88回の再帰後、必要な22個の優れたピボットを達成できない可能性はどのくらいありますか?
二項確率分布はこれに答えることができ、答えは約10 ^ -18です。(nは88、kは21、pは0.6875)ユーザーは、[並べ替え]をクリックするのにかかる1秒の間に、5,000アイテムの並べ替えがさらに悪いことを確認するよりも、約1000倍稲妻に当たる可能性が高くなります。10 *理想的なケースより。データセットが大きくなると、このチャンスは小さくなります。以下は、いくつかの配列サイズと、10 *よりも長く実行される対応する可能性です。
これには、現実よりも悪い2つの保守的な仮定があることに注意してください。そのため、実際のパフォーマンスはさらに良くなり、残りの確率のバランスは理想よりも理想に近くなります。
最後に、他の人が述べたように、再帰スタックが深くなりすぎた場合は、ヒープのソートに切り替えることで、これらの非常にありそうもないケースでも排除できます。したがって、TLDRは、QuickSortの適切な実装では、設計されて実行がO(n * logn)時間で完了するため、最悪のケースは実際には存在しません。
なぜクイックソートが良いのですか?
クイックソートは常にマージソートより優れていますか?
あんまり。
注: Javaでは、Arrays.sort()関数は、プリミティブデータ型にはQuicksortを使用し、オブジェクトデータ型にはMergesortを使用します。オブジェクトはメモリのオーバーヘッドを消費するため、Mergesortに追加された少しのオーバーヘッドは、パフォーマンスの観点では問題にならない場合があります。
参照:コース3の第3週のプリンストンアルゴリズムコースのQuickSortビデオを見る
クイックソートはマージソートよりも優れていません。O(n ^ 2)(めったに起こらない最悪のケース)では、クイックソートはマージソートのO(nlogn)よりもはるかに遅くなる可能性があります。Quicksortはオーバーヘッドが少ないので、nが小さくて遅いコンピューターの場合は、より優れています。しかし、今日のコンピューターは非常に高速であるため、マージソートの追加のオーバーヘッドは無視でき、非常に遅いクイックソートのリスクは、ほとんどの場合、マージソートの取るに足らないオーバーヘッドをはるかに上回ります。
さらに、mergesortは、同じキーを持つアイテムを元の順序のままにします。これは便利な属性です。
<=
がではなく比較に使用されていることが保証されており、使用し<
ない理由はありません。
答えは、プリミティブ値のDualPivotQuickSortによってもたらされた変更に対して、クイックソートのwrtに少し傾くでしょう。Java 7でjava.util.Arraysをソートするために使用されます
It is proved that for the Dual-Pivot Quicksort the average number of
comparisons is 2*n*ln(n), the average number of swaps is 0.8*n*ln(n),
whereas classical Quicksort algorithm has 2*n*ln(n) and 1*n*ln(n)
respectively. Full mathematical proof see in attached proof.txt
and proof_add.txt files. Theoretical results are also confirmed
by experimental counting of the operations.
-あなたはここでJAVA7のimplmentationを見つけることができますhttp://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/Arrays.java
DualPivotQuickSortのさらにすばらしい読み物-http : //permalink.gmane.org/gmane.comp.java.openjdk.core-libs.devel/2628
マージソートの一般的なアルゴリズムは次のとおりです。
トップレベルでは、2つのソートされたサブ配列をマージするには、N個の要素を処理する必要があります。
その1レベル下、ステップ3の各反復ではN / 2要素の処理が必要になりますが、このプロセスを2回繰り返す必要があります。したがって、まだ2 * N / 2 == N要素を処理しています。
その1つ下のレベルでは、4 * N / 4 == N個の要素をマージしています。再帰スタックのすべての深さには、その深さのすべての呼び出しにわたって、同じ数の要素をマージすることが含まれます。
代わりに、クイックソートアルゴリズムを検討してください。
トップレベルでは、サイズNの配列を処理しています。次に、ピボットポイントを1つ選択し、正しい位置に配置して、残りのアルゴリズムでは完全に無視できます。
その1つ下のレベルでは、N-1の合計サイズを持つ2つのサブ配列を扱います(つまり、以前のピボットポイントを減算します)。各サブ配列のピボットポイントを選択すると、最大2つのピボットポイントが追加されます。
その1レベル下では、上記と同じ理由で、サイズN-3を組み合わせた4つのサブ配列を処理しています。
それからN-7 ...そしてN-15 ...そしてN-32 ...
再帰スタックの深さはほぼ同じままです(logN)。マージソートでは、再帰スタックの各レベルにわたって、常にN要素のマージを処理しています。ただし、クイックソートを使用すると、スタックを下に行くにつれて、処理する要素の数が減少します。たとえば、再帰スタックの途中で深さを見る場合、処理している要素の数はN-2 ^((logN)/ 2))== N-sqrt(N)です。
免責事項:マージソートでは、毎回配列を2つのまったく同じチャンクに分割するため、再帰的な深さはちょうどlogNです。クイックソートでは、ピボットポイントが正確に配列の中央にある可能性が低いため、再帰スタックの深さはlogNよりわずかに大きくなる可能性があります。この要素と上記の要素がアルゴリズムの複雑さで実際にどの程度の役割を果たすかを確認するための計算は行っていません。
クイックソートの方が平均的なケースの複雑さは優れていますが、一部のアプリケーションでは間違った選択です。クイックソートは、サービス拒否攻撃に対して脆弱です。攻撃者がソートする入力を選択できる場合、攻撃者はo(n ^ 2)の最悪の場合の時間の複雑さを取るセットを簡単に構築できます。
Mergesortの平均的なケースの複雑さと最悪のケースの複雑さは同じであり、そのため同じ問題に悩まされることはありません。マージソートのこのプロパティは、リアルタイムシステムにとっても優れた選択肢になります。これは、実行速度を大幅に低下させる病理的なケースがないためです。
これらの理由で、私はQuicksortよりもMergesortの方が好きです。
それは言うのは難しいです.MergeSortの最悪はn(log2n)-n + 1で、nが2 ^ kに等しい場合に正確です(これはすでに証明済みです)、任意のnの場合、(n lg n-n + 1)と(n lg n + n + O(lg n))ですが、quickSortの場合、nlog2n(nも2 ^ kに等しい)が最適です。MergesortをquickSortで除算すると、nが無限大の場合は1になります。So MergeSortのワーストケースがQuickSortのベストケースよりも優れているように見えますが、なぜクイックソートを使用するのですか?ただし、MergeSortが適切に配置されていないため、2nのmemeroyスペースが必要です。アルゴリズムの分析には含めないでください。つまり、MergeSortはtheroyでのクイックソートよりも優れていますが、実際には、メモリ空間を考慮する必要があります。配列コピーのコストは、マージがクイックソートよりも遅くなります。私はランダムクラスによってJavaで1000000桁を与えられた実験、マージソートでは2610ミリ秒、クイックソートでは1370ミリ秒かかりました。
クイックソートは最悪の場合O(n ^ 2)ですが、平均的なケースでは一貫してマージソートが実行されます。各アルゴリズムはO(nlogn)ですが、Big Oについて話すときは、複雑度の低い要素は省略していることを覚えておく必要があります。クイックソートは、一定の要素に関してマージソートよりも大幅に改善されています。
マージソートにはO(2n)メモリも必要ですが、クイックソートを実行できます(O(n)のみが必要です)。これは、一般にマージソートよりもクイックソートが優先されるもう1つの理由です。
追加情報:
ピボットの選択が不十分な場合、最悪の場合のクイックソートが発生します。次の例について考えてみます。
[5、4、3、2、1]
ピボットがグループの最小値または最大値として選択されている場合、クイックソートはO(n ^ 2)で実行されます。リストの最大25%または最小25%にある要素を選択する確率は0.5です。これにより、アルゴリズムは適切なピボットになる確率が0.5になります。典型的なピボット選択アルゴリズム(ランダム要素を選択するなど)を使用する場合、ピボットのすべての選択に対して適切なピボットを選択する可能性は0.5です。サイズが大きいコレクションの場合、常に不適切なピボットを選択する確率は0.5 * nです。この確率に基づいて、クイックソートは平均的な(そして典型的な)ケースに対して効率的です。
これはかなり古い質問ですが、最近両方を扱ったので、ここに私の2cがあります。
マージソートの必要性は平均でNログN比較です。すでに(ほぼ)ソート済みのソート済み配列の場合、これは1/2 N log Nに減少します。マージする間、(ほぼ)常に「左」の部分を1/2 N回選択し、次に右の1/2 N要素をコピーするだけだからです。さらに、すでに並べ替えられた入力によってプロセッサの分岐予測子が輝き、ほとんどすべての分岐が正しく推測されるため、パイプラインのストールが防止されると推測できます。
クイックソートでは、平均で1.38 N log Nの比較が必要です。比較の観点からは既にソートされた配列から大きなメリットはありません(ただし、スワップの観点から、おそらくCPU内の分岐予測の観点からはメリットがあります)。
かなり最近のプロセッサでの私のベンチマークは以下を示しています:
比較関数が(qsort()libc実装のように)コールバック関数である場合、クイックソートはマージソートよりランダム入力で15%、64ビット整数のソート済み配列で30%遅くなります。
一方、比較がコールバックでない場合、私の経験では、クイックソートはマージソートより最大25%優れています。
ただし、(大規模な)配列に一意の値がほとんどない場合は、マージソートがどのような場合でもクイックソートよりも優先されます。
だから多分結論は:比較が高価な場合(例:コールバック関数、文字列の比較、構造の多くの部分を比較して、主に2分の3から3の "if"で違いが出る)-可能性はあなたが良くなることですマージソート付き。単純なタスクの場合、クイックソートはより高速になります。
つまり、クイックソートはN ^ 2である可能性がありますが、Sedgewickは、ランダム化された適切な実装では、N ^ 2を実行するよりも、コンピューターがソートを実行する可能性が高いと考えています-マージソートには追加のスペースが必要です
両方の並べ替えアルゴリズムを試してみたところ、再帰呼び出しの数を数えることで、クイックソートは一貫してマージソートよりも再帰呼び出しが少なくなっています。これは、クイックソートにピボットがあり、ピボットが次の再帰呼び出しに含まれないためです。このようにして、クイックソートはマージソートよりも速く再帰ベースケースに到達できます。
これはインタビューでよく寄せられる質問です。マージソートのワーストケースのパフォーマンスは優れていますが、特に大規模な入力の場合、クイックソートはマージソートよりも優れていると考えられています。クイックソートの方が優れているため、いくつかの理由があります。
1-補助スペース:クイックソートはインプレースソートアルゴリズムです。インプレースソートとは、ソートを実行するために追加のストレージスペースが必要ないことを意味します。一方、マージソートは、ソートされた配列をマージするために一時的な配列を必要とするため、インプレースではありません。
2-最悪のケース:O(n^2)
ランダムなクイックソートを使用することで、クイックソートの最悪のケースを回避できます。正しいピボットを選択することで、高い確率で簡単に回避できます。適切なピボット要素を選択してケースの平均的な動作を取得すると、パフォーマンスが向上し、マージソートと同じくらい効率的になります。
3-参照の局所性:クイックソートは、特にキャッシュの局所性が優れているため、仮想メモリ環境などの多くの場合、マージソートよりも高速になります。
4-末尾再帰:マージソートはそうではありませんが、QuickSortは末尾再帰です。末尾再帰関数は、再帰呼び出しが関数によって実行される最後のものである関数です。テール再帰はコンパイラーによって最適化できるため、テール再帰関数は非テール再帰関数よりも優れていると見なされています。
どちらも同じ複雑性クラスに属していますが、両方が同じランタイムを持っているという意味ではありません。Quicksortは通常、Mightsortよりも高速です。これは、タイトな実装のコーディングが容易であり、その操作が高速になるためです。これは、そのクイックソートの方が一般的にマージソートの代わりに使用するより速いためです。
しかしながら!私は個人的に、クイックソートがうまくいかない場合にマージソートまたはマージソートに低下するクイックソートバリアントを頻繁に使用します。覚えておいてください。クイックソートは平均で O(n log n)のみです。最悪のケースはO(n ^ 2)です!Mergesortは常にO(n log n)です。リアルタイムのパフォーマンスまたは応答性が必須であり、入力データが悪意のあるソースからのものである可能性がある場合は、単純なクイックソートを使用しないでください。
すべてが同じであるため、ほとんどの人が最も便利に利用できるものを使用することを期待しています。それ以外は、配列で一般的な選択肢であるマージソートと同様に、配列でクイックソートが非常に高速であることが知られています。
どうして基数やバケットのソートが表示されるのがめったにないのかと思います。それらはO(n)であり、少なくともリンクリストでは、キーを序数に変換するいくつかの方法があります。(文字列とフロートは問題なく動作します。)
その理由は、コンピューターサイエンスの教え方に関係していると思います。アルゴリズム分析の講師に、O(n log(n))よりも速くソートできることを実際に示す必要さえありました。(彼はあなたがO(n log(n))よりも速くソートを比較できないという証拠を持っていました、それは真実です。)
他のニュースでは、浮動小数点数は整数として並べ替えることができますが、後で負の数を反転させる必要があります。
編集:実際、floats-as-integersをソートするさらに悪質な方法があります:http : //www.stereopsis.com/radix.html。ビットフリッピングトリックは、実際に使用するソートアルゴリズムに関係なく使用できることに注意してください...
qsort
は、マージソートであることに注意してください。
クイックソートはインプレースソートアルゴリズムであるため、配列に適しています。一方、マージソートはO(N)の追加ストレージを必要とし、リンクされたリストにより適しています。
配列とは異なり、いいねリストでは、O(1)スペースとO(1)時間で途中にアイテムを挿入できるため、余分なスペースなしでマージソートのマージ操作を実装できます。ただし、配列に追加のスペースを割り当てたり割り当て解除したりすると、マージソートの実行時間に悪影響が及びます。マージソートでは、ランダムメモリアクセスをほとんど行わずにデータに順次アクセスするため、リンクリストも優先されます。
一方、クイックソートには大量のランダムメモリアクセスが必要です。配列を使用すると、リンクリストのように走査せずに直接メモリにアクセスできます。また、配列はメモリに連続して格納されるため、配列に使用した場合のクイックソートは参照の局所性が良好です。
両方の並べ替えアルゴリズムの平均的な複雑さはO(NlogN)ですが、通常のタスクでは通常、配列をストレージとして使用します。そのため、クイックソートが最適なアルゴリズムです。
編集:私はマージソートの最悪/最高/平均のケースが常にnlognであることを発見しましたが、クイックソートはn2(要素がすでにソートされている最悪のケース)からnlogn(ピボットが常に配列を2つに分割するときの平均/ベストケース)に変わる可能性があります半分)。
c / c ++ランドでは、stlコンテナーを使用しない場合、Quicksortを使用する傾向があります。これは、ランタイムに組み込まれているのに対し、mergesortはそうではないためです。
したがって、多くの場合、それは単に最小の抵抗の道であると私は信じています。
さらに、データセット全体がワーキングセットに収まらない場合は、クイックソートを使用するとパフォーマンスが大幅に向上します。
qsort
要素の数が本当に巨大であるか、一時メモリが割り当てられない限り、GNU libc はマージソートです。cvs.savannah.gnu.org/viewvc/libc/stdlib/…–
その理由の1つは、より哲学的です。クイックソートはトップ→ダウンの哲学です。並べ替える要素がn個ある場合、n個あります。可能性。相互に排他的なmとnmの2つのパーティションにより、可能性の数は数桁下がります。m!*(nm)!nよりも数桁小さい!一人で。想像してみてください!対3!* 2!。5!2と3の2つのパーティションの10倍の可能性があります。100万の階乗と900Kに外挿します!* 100K!vs.したがって、範囲またはパーティション内の順序を確立することを心配する代わりに、パーティションのより広いレベルで順序を確立し、パーティション内の可能性を減らします。パーティション自体が相互に排他的でない場合、範囲内で先に確立された順序は後で妨害されます。
マージソートやヒープソートなどのボトムアップ方式のアプローチは、ミクロレベルで早期に比較を開始するワーカーまたは従業員のアプローチに似ています。しかし、これらの間にある要素が後で見つかるとすぐに、この順序は失われます。これらのアプローチは非常に安定しており、非常に予測可能ですが、ある程度の余計な作業を行います。
クイックソートは、最初は注文を気にせず、注文を考慮せずに幅広い基準を満たすことのみを考慮した管理アプローチに似ています。次に、ソートされたセットが得られるまで、パーティションが狭められます。クイックソートの本当の課題は、ソートする要素について何も知らないときに、暗闇の中でパーティションまたは基準を見つけることです。そのため、中央値を見つけるために努力するか、ランダムに1つまたは任意の「管理」アプローチを選択する必要があります。完璧な中央値を見つけるにはかなりの労力が必要であり、また愚かなボトムアップアプローチにつながります。したがって、Quicksortはランダムなピボットを選択するだけであり、それが中間のどこかになるか、中央値3、5、またはそれ以上の中央値を見つけるために何らかの作業を行って、より良い中央値を見つけることを期待しますが、完全であるとは考えていません。最初の注文で時間を無駄にしない。これは、運が良かったり、中央値を取得せずにチャンスをとるだけでn ^ 2に低下したりする場合にうまくいくようです。どのような方法でもデータはランダムです。正しい。だから私はクイックソートのトップ→ダウン論理アプローチにもっと同意します、そしてピボットの選択とそれが以前に保存する比較について取る可能性は、どんな細心の徹底した安定したボトム→アップのアプローチよりもうまく機能するようですマージソート。だが 以前に保存された比較は、マージソートなどの詳細で安定したボトムからアップへのアプローチよりも多くの時間で機能するようです。だが 以前に保存された比較は、マージソートなどの詳細で安定したボトムからアップへのアプローチよりも多くの時間で機能するようです。だが
qsort
、Pythonlist.sort
、およびArray.prototype.sort
FirefoxのJavaScriptは、すべてマージされたマージです。(GNU STLsort
は代わりにIntrosortを使用しますが、C ++ではスワップがコピーよりも優先される可能性があるためと考えられます。)