クイックソートとヒープソートの両方がインプレースソートを実行します。どちらが良いですか?どちらが望ましいアプリケーションとケースは何ですか?
このペーパー にはいくつかの分析があります。
また、ウィキペディアから:
クイックソートの最も直接的な競争相手はヒープソートです。ヒープソートは通常、クイックソートよりもやや遅いですが、最悪の場合の実行時間は常にΘ(nlogn)です。通常、クイックソートは高速ですが、最悪のケースが検出されるとヒープソートに切り替わるイントロソートバリアントを除き、最悪の場合のパフォーマンスの可能性が残ります。ヒープソートが必要になることが事前にわかっている場合、それを直接使用する方が、イントロソートがヒープに切り替わるのを待つよりも速くなります。
HeapsortはO(N log N)が保証されており、Quicksortの最悪の場合よりもはるかに優れています。 Heapsortは、Mergesortが必要とするように、別の配列が順序付けられたデータを置くためにこれ以上メモリを必要としません。では、なぜ商用アプリケーションはQuicksortに固執するのでしょうか?他の実装よりも特別なQuicksortの特徴は何ですか?
私は自分でアルゴリズムをテストしましたが、Quicksortには本当に特別なものがあることがわかりました。ヒープおよびマージアルゴリズムよりもはるかに高速に実行されます。
Quicksortの秘密は次のとおりです。不要な要素のスワップはほとんど行いません。スワップには時間がかかります。
Heapsortを使用すると、すべてのデータがすでに順序付けられている場合でも、要素を100%交換して配列を順序付けします。
Mergesortでは、さらに悪化します。データがすでに順序付けられていても、100%の要素を別の配列に書き込み、元の配列に書き戻します。
Quicksortを使用すると、すでに注文されているものを交換する必要はありません。データが完全に注文されていれば、ほとんど何も交換しません!最悪のケースについては多くの混乱がありますが、配列の最初または最後の要素を取得する以外に、ピボットの選択を少し改善することで回避できます。最初の要素、最後の要素、および中間の要素の間の中間要素からピボットを取得する場合、最悪のケースを回避するのに十分です。
Quicksortで優れているのは最悪のケースではなく、最良のケースです!最良の場合、同じ数の比較を行いますが、大丈夫ですが、ほとんど何も交換しません。 HeapsortやMergesortのように、平均的な場合、要素の一部を交換しますが、すべての要素ではありません。それがQuicksortに最高の時間を与えるものです。少ないスワップ、より高速。
リリースモードで実行している私のコンピューターのC#での以下の実装は、Array.Sortを中間のピボットで3秒、改善されたピボットで2秒倒します(はい、良いピボットを取得するためのオーバーヘッドがあります)。
static void Main(string[] args)
{
int[] arrToSort = new int[100000000];
var r = new Random();
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
Console.WriteLine("Press q to quick sort, s to Array.Sort");
while (true)
{
var k = Console.ReadKey(true);
if (k.KeyChar == 'q')
{
// quick sort
Console.WriteLine("Beg quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
QuickSort(arrToSort, 0, arrToSort.Length - 1);
Console.WriteLine("End quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
else if (k.KeyChar == 's')
{
Console.WriteLine("Beg Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
Array.Sort(arrToSort);
Console.WriteLine("End Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
}
}
static public void QuickSort(int[] arr, int left, int right)
{
int begin = left
, end = right
, pivot
// get middle element pivot
//= arr[(left + right) / 2]
;
//improved pivot
int middle = (left + right) / 2;
int
LM = arr[left].CompareTo(arr[middle])
, MR = arr[middle].CompareTo(arr[right])
, LR = arr[left].CompareTo(arr[right])
;
if (-1 * LM == LR)
pivot = arr[left];
else
if (MR == -1 * LR)
pivot = arr[right];
else
pivot = arr[middle];
do
{
while (arr[left] < pivot) left++;
while (arr[right] > pivot) right--;
if(left <= right)
{
int temp = arr[right];
arr[right] = arr[left];
arr[left] = temp;
left++;
right--;
}
} while (left <= right);
if (left < end) QuickSort(arr, left, end);
if (begin < right) QuickSort(arr, begin, right);
}
たいていの場合、速いか少し速いかは関係ありません...たまに動きが遅くなることを望みません。 QuickSortを微調整して低速な状況を回避できますが、基本的なQuickSortの優雅さは失われます。そのため、ほとんどの場合、実際にはHeapSortを好みます。完全にシンプルなエレガンスで実装でき、スローソートを使用することはできません。
ほとんどの場合、最高速度が必要な状況では、QuickSortがHeapSortより優先される場合がありますが、どちらも正しい答えではない場合があります。速度が重要な状況では、状況の詳細を詳しく調べる価値があります。たとえば、速度が重要なコードの一部では、データが既に並べ替えられているか、ほぼ並べ替えられていることがよくあります(多くの場合、一緒に上下に移動する複数の関連フィールドにインデックスを付けていますOR =互いに反対に上下に移動するので、1つでソートすると、他はソートまたは逆ソートまたはクローズされます...どちらもQuickSortを殺すことができます。その場合、代わりにどちらも実装しませんでした... DijkstraのSmoothSortを実装しました。O(N)であるソート済みまたは準ソート済みのHeapSortバリアントです...それほどエレガントではなく、理解するのも簡単ではありませんが、高速です。 .. read http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF コーディングが少し難しいものが必要な場合。
Quicksort-Heapsortインプレースハイブリッドも非常に興味深いものです。なぜなら、それらのほとんどは最悪の場合にn * log nの比較しか必要としないからです(これらは漸近性の最初の項に関して最適であり、最悪のシナリオを回避しますクイックソートの)、O(log n)余分なスペースで、すでに並べ替えられたデータのセットに関して、クイックソートの良好な動作の少なくとも「半分」を保持します。非常に興味深いアルゴリズムが、DikertとWeissによって http://arxiv.org/pdf/1209.4214v1.pdf に提示されています。
あなたがアーキテクチャレベルに行くと...キャッシュメモリのキューデータ構造を使用するので、キューで利用可能なものはソートされます。クイックソートのように、配列を任意の長さに分割することは問題ありません...しかし、ヒープでは並べ替え(配列を使用して)、キャッシュで使用可能なサブ配列に親が存在しない可能性があり、キャッシュメモリに入れる必要があります...時間がかかります。それはクイックソートが最高です!! ????
比較_quick sort
_と_merge sort
_の間は両方ともインプレースソートのタイプであるため、クイックソートの場合の最悪ケースの実行時間とO(n^2)
のヒープソートの実行時間には違いがあります。まだO(n*log(n))
であり、平均的なデータ量のクイックソートはより便利です。ランダム化されたアルゴリズムなので、正しいansを取得する確率が高くなります。短時間で選択するピボット要素の位置に依存します。
だから
グッドコール: LとGのサイズはそれぞれ3秒/ 4未満
悪い呼び出し: LとGのいずれかのサイズが3s/4より大きい
少量の場合は挿入ソートに、非常に大量のデータにはヒープソートに使用できます。
ヒープソートは、非常に大きな入力を処理する場合の安全な方法です。漸近解析により、最悪の場合のヒープソートの成長順序はBig-O(n logn)
であり、QuicksortのBig-O(n^2)
が最悪の場合よりも優れていることがわかります。ただし、 Heapsort は、適切に実装されたクイックソートよりも、ほとんどのマシンで実際には多少遅くなります。ヒープソートも安定したソートアルゴリズムではありません。
ヒープソートが実際にはクイックソートよりも遅い理由は、データ要素が比較的内にあるクイックソートの参照の局所性が優れているためです( " https://en.wikipedia.org/wiki/Locality_of_reference ")保管場所を閉じます。参照の局所性が強いシステムは、パフォーマンスの最適化の優れた候補です。ただし、ヒープのソートは、より大きな飛躍に対処します。これにより、小さな入力に対してクイックソートがより有利になります。
Heapsort ヒープを構築してから、最大アイテムを繰り返し抽出します。その最悪のケースはO(n log n)です。
しかし、最悪のケースである quick sort (O(n2))が表示された場合、クイックソートは大きなデータにはあまり適していません。
したがって、これによりソートが興味深いものになります。今日、非常に多くの分類アルゴリズムが存在する理由は、それらのすべてが最高の場所で「最高」だからだと思います。たとえば、データがソートされている場合、バブルソートはクイックソートを実行できます。または、ソートするアイテムについて何か知っていれば、おそらくもっとうまくいくでしょう。
これはあなたの質問に直接答えないかもしれません、私は私の2セントを追加すると思いました。
ヒープソートにはO(n * log(n))の最悪の実行ケースがあるという利点があるため、クイックソートのパフォーマンスが低下する可能性が高い場合(一般的にはソートされたデータセットがほとんど)、ヒープソートがより好まれます。
私にとって、ヒープソートとクイックソートには基本的な違いがあります。後者は再帰を使用します。再帰アルゴリズムでは、ヒープは再帰の数とともに増加します。これは、nが小さい場合は関係ありませんが、今はn= 10 ^ 9 !!で2つの行列をソートしています。プログラムはほぼ10 GBのRAMを必要とし、追加のメモリがあると、コンピューターは仮想ディスクメモリへのスワップを開始します。私のディスクはRAMディスクですが、それと交換しても速度の大きな差になります。したがって、サイズの調整可能な次元行列を含むC++でコーディングされたstatpackではプログラマーには事前に不明であり、ノンパラメトリックな統計的な並べ替えのソートは、非常に大きなデータ行列での使用の遅延を回避するためにヒープソートを好む。