web-dev-qa-db-ja.com

Collections.sortがクイックソートではなくマージソートを使用する理由

クイックソートが最速のソートアルゴリズムであることはわかっています。

Collections.sortは、クイックソートではなくマージソートアルゴリズムを使用しました。ただし、Arrays.sortはクイックソートを使用します。

Collections.sortがクイックソートではなくマージソートを使用する理由は何ですか?

90
MayurB

ジョシュ・ブロックからの可能性が高い § ​​:

これらのメソッドを書いたので、答える資格があると思います。単一の最適なソートアルゴリズムは存在しないのは事実です。マージソートと比較した場合、QuickSortには2つの大きな欠陥があります。

  1. (parsifalが指摘したように)安定していません。

  2. それは保証しませんn log nパフォーマンス;病理学的入力で二次性能に低下する可能性があります。

(値)の等価性とは異なるアイデンティティの概念がないため、プリミティブ型では安定性は問題ではありません。そして、二次挙動の可能性は、BentlylyとMcIlroyの実装(またはその後 Dual Pivot Quicksort )の実際には問題ではないと見なされたため、これらのQuickSortバリアントはプリミティブソートに使用されました。

任意のオブジェクトを並べ替える場合、安定性は重要です。たとえば、電子メールメッセージを表すオブジェクトがあり、最初に日付で、次に送信者で並べ替えるとします。各送信者内で日付順にソートされることが期待されますが、ソートが安定している場合にのみそうなります。そのため、オブジェクト参照をソートするために安定したソート(マージソート)を提供することにしました。 (技術的に言えば、複数の連続した安定したソートにより、ソート順と逆の順序でキーが辞書式順序になります。最終ソートが最も重要なサブキーを決定します。)

マージの並べ替えが保証されますn log n(時間)パフォーマンスがどのような入力であっても、それは素晴らしい副次的な利点です。もちろん、欠点もあります。クイックソートは「インプレース」ソートです。これは、(コールスタックを維持するために)log n外部スペースのみを必要とします。一方、マージ、ソートには、O(n)外部スペースが必要です。入力配列がほぼソートされている場合、TimSortバリアント(Java SE 6で導入された)は、必要なスペース(O(k))が大幅に少なくなります。

また、 後続 が関連しています:

Java.util.Arrays.sortおよび(間接的に)Java.util.Collections.sortがオブジェクト参照をソートするために使用するアルゴリズムは、「修正マージソート(下位サブリストの最上位要素が以下の場合、マージは省略されます)上位サブリストの最下位要素)。」これは、O(n log n)のパフォーマンスを保証し、O(n)の追加スペースを必要とする、適度に高速で安定したソートです。その日(1997年にJoshua Blochによって書かれました)、それは素晴らしい選択でしたが、今日はもっと良いことができます。

2003年以来、Pythonのリストの並べ替えでは、(それを書いたTim Petersの後の)timsortと呼ばれるアルゴリズムが使用されています。部分的にソートされた配列で実行する場合、n log(n)比較よりもはるかに少ない安定性、適応性、反復マージソートであり、ランダム配列で実行する場合は従来のマージソートに匹敵するパフォーマンスを提供します。すべての適切なマージソートと同様に、timsortは安定しており、O(n log n)時間で実行されます(最悪の場合)。最悪の場合、timsortはn/2個のオブジェクト参照のために一時的なストレージスペースを必要とします。最良の場合、必要なスペースはごくわずかです。これを現在の実装と比較してください。現在の実装では、n個のオブジェクト参照に常に余分なスペースが必要であり、ほぼソートされたリストでのみn log nを超えます。

Timsortの詳細については、 http://svn.python.org/projects/python/trunk/Objects/listsort.txt を参照してください。

Tim Petersの元の実装はCで記述されています。JoshuaBlochはCからJavaに移植し、最終的なコードのテスト、ベンチマーク、調整を広範囲にわたって行いました。結果のコードは、Java.util.Arrays.sortのドロップイン置換です。高度に順序付けられたデータでは、このコードは現在の実装(HotSpotサーバーVM上)の最大25倍の速度で実行できます。ランダムデータでは、古い実装と新しい実装の速度は同等です。非常に短いリストの場合、新しい実装は、ランダムデータであっても古いものよりもかなり高速です(不必要なデータコピーが回避されるため)。

また、 メソッドArrays.Sort?にTim Sortを使用しているJava 7です を参照してください。

単一の「最良」の選択肢はありません。他の多くのものと同様に、トレードオフについてです。

175
NPE