web-dev-qa-db-ja.com

クイックソートの悪い例は何ですか?

私はクイックソートについて学び、クイックソートが困難なさまざまな配列を説明したいと思います。私が念頭に置いているクイックソートには、初期ランダムシャッフルはなく、2つのパーティションがあり、中央値を計算しません。

これまでに3つの例を考えました。

[1,2,3,4,5,6,7,8,9,10] - when the array is sorted
[10,9,8,7,6,5,4,3,2,1] - when the array is reversed
[1,1,1,1,1,1,1,1,1,1] - when the array is the same values
[1,1,1,2,2,2,3,3,3,3] - when there are few and unique keys

たとえば、これについてはよくわかりません。

[1,3,5,7,9,10,8,6,4,2]

では、クイックソートが(ほぼ)理想的である配列と比較して困難な配列は何になるのでしょうか。

10
mrQWERTY

すべてのソートアルゴリズムには最悪のケースがあり、多くの場合最悪のケースは本当に悪いので、テストする価値があります。問題は、基本的なアルゴリズムを知っているからといって、最悪のケースが1つもないということです。

一般的な最悪のケースは次のとおりです。逆順にソートされます。ほぼソートされた、1つの順不同要素。すべての値は同じです。最初(または最後)が高い(または低い)以外はすべて同じです。私たちはかつて、最悪のケースが特定の鋸歯状パターンである種類を持っていました。これは予測が非常に困難でしたが、実際には非常に一般的でした。

クイックソートの最悪のケースは、可能な限り最悪のピボットを常に選択して、パーティションの1つに要素が1つしかないケースです。ピボットが最初の要素である場合(悪い選択)、既に並べ替えられたデータまたは逆に並べ替えられたデータが最悪のケースです。中央値が3のピボットデータがすべて同じか、最初と最後だけが異なる場合、トリックは異なります。


クイックソートの場合、平均的な複雑度はnlognで、最悪のケースはn ^ 2です。最悪のケースの動作をトリガーする価値があるのは、これが最大の再帰深度を生成するケースでもあるためです。単純な実装の場合、再帰の深さはnになる可能性があり、スタックオーバーフローをトリガーする可能性があります。同様の理由で、他の極端な状況(最良のケースを含む)をテストすることは価値があります。

8
david.pfx

アルゴリズムは、ランダム化されたピボットを使用してほとんどの悪いケースから脱出し、ピボットに等しい連続要素をパーティション分割および非対称検索から除外します。要素を前方に検索しピボット以上、後方に要素を検索します未満ピボット。
MichaelTに感謝します。非対称検索は[2,1,2,1,2,1,2,1]を解決するために考案されました。

次の結果は、関数qsort_random()によって生成されます。 N = 100,000

usec    call   compare   copy    pattern
80132   62946  1971278   877143  random
47326   57578  1606067   215155  sorted : 0,1,2,3,...,n-1
49927   63578  1628883   338715  sorted in reverse : n-1,n-2,...,2,1,0
55619   63781  1596934   377330  nearly reverse : n-2,n-1,n-4,n-3,...,2,3,0,1
54714   66667  1611454   290392  median-3-killer : n-1,0,1,2,...,n-2
1491    1      99999     4       all values the same : n,n,n,...
1577    1      99999     4       first is higher : n,1,1,1,...
2778    2      156159    10      last is lower : n,n,n,...,n,1
2994    3      199996    100009  a few data : n,...,n,1,...,1
3196    3      199996    50012   zigzag : n,1,n,1,...,n,1
917796  56284  67721985  673356  valley(sawtooth?) : n-1,n-3,...,0,...,n-4,n-2

ほとんどの場合、ランダムパターンよりも高速です。谷のパターンは、ほとんどのピボット選択の悪いケースです。

qsort(3)       usec = 14523   call = 0      compare = 884463    copy = 0
qsort_head()   usec = 138609  call = 99999  compare = 8120991   copy = 1214397
qsort_middle() usec = 664325  call = 99999  compare = 52928111  copy = 1036047
qsort_trad()   usec = 118122  call = 99999  compare = 6476025   copy = 1337523
qsort_random() usec = 295699  call = 58806  compare = 19439952  copy = 732962
qsort_log2()   usec = 66411   call = 63987  compare = 1597455   copy = 944821

qsort_log2()は、log2(N)要素のピボットを選択することにより、不正なケースからエスケープします。
qsort(3)使用GNUライブラリは、インデックスソートのマージソートです。
qsort_trad()は、最初、中間、最後の要素でピボットを選択します。
qsort_random()およびqsort_log2()はスワッピングを使用しません。
ソースCプログラムとスクリプトは github に投稿されます。

0
Leorge Takeuchi