web-dev-qa-db-ja.com

さまざまなSTL実装のC ++ 11 std :: sortで使用されているアルゴリズムは何ですか?

C++ 11標準では、std::sortO(n logn)複雑度になることを保証しています(最悪の場合)。これは、C++ 98/03のaverage-case保証とは異なります。ここで、std::sortはQuicksortで実装できます(小さなnの挿入ソートと組み合わせることができます) 、最悪の場合はO(n ^ 2)になります(ソートされた入力などの特定の入力の場合)。

異なるSTLライブラリのstd::sort実装に変更はありましたか? C++ 11のstd::sortは、さまざまなSTLにどのように実装されますか?

46
Alexey Voytenko

libstdc ++ および libc ++ のオンラインソースを参照すると、両方のライブラリが、イントロソートのメインループからのよく知られたソーティングアルゴリズムの全範囲を使用すること:

_std::sort_の場合、_insertion_sort_のヘルパールーチン(O(N^2)アルゴリズムですが、小さなシーケンスで競合できるように適切なスケーリング定数を使用)と、サブ0、1、2、3要素のシーケンス。

_std::partial_sort_の場合、どちらのライブラリも_heap_sort_(一般的にはO(N log N))のバージョンを使用します。これは、このメソッドに、ソートされたサブシーケンスを保持するNice不変式があるためです(通常、スケーリングが大きくなります)。完全なソートのためにそれをより高価にするために定数)。

_std::nth_element_の場合、_selection_sort_のヘルパールーチンがあります(これも、小さなシーケンスで競合できるようにするための適切なスクラリング定数を持つO(N ^ 2)アルゴリズムです)。通常の並べ替えでは、_insertion_sort_は通常_selection_sort_より優先されますが、_nth_element_の場合、最小の要素を持つことの不変量は_selection_sort_の動作と完全に一致します。

19
TemplateRex

問題は、どうすればよいか[〜#〜] stl [〜#〜]が_std::sort_と言うことです最悪の場合はO(N log(N))、本質的にはQuickSortですが。 STLのソートはIntroSortです。 IntroSortは本質的にQuickSortです。導入された違いにより、最悪のケースの複雑さが変わります。


QuickSortの最悪のケースはO(N ^ 2)です

どのパーティション設定を選択しても、QuickSortが実行されるシーケンスO(N ^ 2)が存在します。選択したパーティショニングは、最悪のケースが発生する確率を下げるだけです。 ( ランダムピボット選択 、3つの中央値、 etc。

編集: @ maxim1000の訂正に感謝します。ピボット選択アルゴリズムを使用したクイックソート Median of MediansO(N log(N))最悪の場合の複雑さですが、オーバーヘッドがあるため、実際には使用されません。これは、理論的には、ピボット選択により、優れた選択アルゴリズムがワーストケースの複雑さをどのように変更できるかを示しています。


IntroSortは何をしますか?

IntroSortは、QuickSortの分岐を制限します。これが最も重要なポイントであり、その制限は2 * (log N)です。制限に達すると、IntroSortはO(N log(N))の最悪の場合の複雑さを持つ任意の並べ替えアルゴリズムを使用できます。

O(log N)サブ問題があると分岐が停止します。すべての部分問題O(n log n)を解くことができます。 (小文字のnはサブ問題のサイズを表します)。

(n log n)の合計は、現在、最悪の場合の複雑さです。

QuickSortの最悪の場合。既に並べ替えられた配列があり、常にこの配列の最初の要素をピボットとして選択するとします。すべての反復で、最初の要素のみを削除します。この方法で最後まで行けば、明らかにO(N ^ 2)になります。 IntroSortを使用してQuickSortを停止し、深さlog(N)に達したら、残りのソートされていない配列にHeapSortを使用します。

_16 -> 1  /**N**/
   \
    > 15 -> 1 /**N - 1**/
         \
          > 14 -> 1 /**N - 2**/
               \
                > 13 -> 1 /**N - log(N)**/  
                     \
                      > 12 /**(HeapSort Now) (N - log(N)) log (N - log(N))**/
_

それらを合計します。

分岐が停止するまで、N + (N - 1) + ... + (N - log(N))操作が実行されます。ガウスを使用して合計する代わりに、単にN + (N - 1) + ... + (N - log(N)) < N log(N)と言うことができます。

HeapSortパートは_(N - log(N)) log(N - log(N)) < N log(N)_です

全体的な複雑さ< 2 N log(N)

定数は省略できるため、IntroSortの最悪の場合の複雑さはO(N log(N))です。


追加情報:[〜#〜] gcc [〜#〜] STL実装のソースコードは ここ です。 Sort関数は行5461にあります。

修正: *Microsoft .NET* sort実装は2012年からIntroSortです。関連情報は こちら です。

25
Cahit Gungor