QuickSort – ウィキペディア

before-content-x4

QuickSort 英語 素早い 、速い’ 分類する 「ソート」)は、原則に基づいた、より速く、再帰的で安定していないソートアルゴリズムです 分割統治 は働いている。 1960年頃にC. Antony R. Hoareによって基本的な形で開発されました [初め] それ以来、多くの研究者によって改善されました。アルゴリズムには、非常に短い内側ループ(実行速度が大幅に向上する)があり、再帰に必要な追加スペースを除いて、追加のストレージスペースを必要としないという利点があります。

after-content-x4

平均して、QuickSortアルゴリズムがリードします

o n de ログ n )) )) {displaystyle {mathcal {o}}(ncdot log(n))}

比較してください。最悪の場合

o n 2 )) {displaystyle {mathcal {o}}(n^{2})}

比較が行われましたが、これは実際には非常にまれです。 [2]

1から1から n ランダムな順序では、クイックスの場所でソートされます。

まず、ソートするリストは2つのサブリスト( “左”および「右」のサブリスト)に分けられます。この目的のために、QuickSortはリストからSO -CALLEDピボット要素を選択します。ピボット要素よりも小さいすべての要素は、左サブリストに入り、右の部分リストで大きいすべての要素が入ります。ピボット要素に等しい要素は、必要に応じてサブリストに広がる可能性があります。部門によると、左のリストの要素は、右のリストの要素が小さく、または等しく等しくあります。

次に、ソートを完了するには、すべてのサブリストを並べ替える必要があります。この目的のために、QuickSortアルゴリズムは左側と右の部分リストで実行されます。次に、各サブリストは2つのサブリストに分割され、クイックソートアルゴリズムが再度適用されます。これらのセルフコールコールは、再帰と呼ばれます。長さの部分的なリストが1つまたはゼロに表示される場合、それはすでにソートされており、再帰が破壊されています。

ピボット要素に等しい要素の位置は、使用される分割アルゴリズムに依存します。好きなようにサブリストを広めることができます。同等の要素の順序は変化する可能性があるため、クイックソートは一般に安定していません。

after-content-x4

この手順では、各サブリストがリスト全体より少なくとも1つ短いことを確認する必要があります。その後、再帰は多くのステップの後に最終的に終了することが保証されます。それはzです。 B.は、元々選択された要素をサブリストの間の場所にピボットとして選択することで実現できます。したがって、部分的なリストに属していません。

部門はインプレースアルゴリズムとして実装されます。要素は追加のメモリにコピーされるのではなく、リスト内でのみ交換されます。これには手順が使用されます スプリット また パーティション と呼ばれます。その後、2つのサブリストが正しい位置にあります。サブリストがソートされるとすぐに、リスト全体のソートが終了しました。

次の擬似コードは、アルゴリズムの仕組みを示しています データ ソートするリスト n 要素はです。あなたが電話するたびに QuickSort() 与えます リンク サブリストの最初の要素のインデックスと 右の方へ 最後のそれ。初めての呼び出し(上部再帰レベル)は リンク= 0 右= n-1 。合格したリストは、1つの値のみが含まれるまで再帰的に繰り返し共有されます。

 関数 QuickSort(左、右) 左<右 それから 部分:=パーツ(左、右)
         QuickSort(左、分割-1)
         QuickSort(仕切り + 1、右) 終わり  終わり  

関数の以下の実装 あなたへ フィールドは、ピボット要素が最終的な位置にあり、すべての小さな要素がその前にあるように分裂し、その後すべての大きな要素が来ます。

 関数 部品(左、右)
     i:=左 //ピボテレメントの左から始めます J:=右-1
     ピボット:=データ[右] 
繰り返す i //私がjを過ぎて走らなかった限り //左から検索ピボテルメントよりも大きい要素 繰り返す i と daten [i] <= pivot i:= i + 1 終わり
//右からの検索ピボテレメントに小さくまたは等しい要素 繰り返す J> i daten [j]> pivot J:= J -1 終わり
data [i]> data [j] それから データ[i]をデータ[J]と交換する 終わり
終わり
// Tausche Pivotelement(data [right])新しい最終位置(データ[i])を備えた) //そしてピボテレメントの新しい位置を返し、終了して実行されます データ[i]> pivot それから データ[i]をデータと交換[右] さもないと i:=右 終わり 応答 終わり

ピボット要素を選択した後、リストの先頭(インデックスI)から要素が最初に検索されます。これは、ピボテルメントよりも大きくなります。したがって、リストの最後からピボテルメント(インデックスJ)よりも小さい要素が求められます。次に、2つの要素が交換され、最終的に正しいサブリストになります。その後、2つの検索プロセスは、下下検索が出会うまで達成された位置から継続されます。 2つのサブリストの境界線があります。

部品(左、右) [ 編集 | ソーステキストを編集します ]

文字シーケンス「例」は、アルファベット順にソートされることです。

の初期状況 j 、右側の要素( l )ピボット要素は次のとおりです。

e i n b e i s p i e l
  ^ ^
  I j 

内側の研削での最初の検索の後 要素に > l j 要素に <= l 開催:

e i n b e i s p i e l
      ^ ^
      I j 

要素を交換した後 j

e i e b e i s p i n l
      ^ ^
      I j 

次の検索と交換の後:

e i e b e i i p s n l
              ^ ^
              I j 

別の検索の後、インデックスはお互いを通り過ぎました:

e i e b e i i p s n l
              ^ ^
              J i 

交換後 ピボットを参照してください 部分リストの分離点。で ピボット要素は、その左側にある要素のみが要素≤ピボットであり、右側にはそのような>ピボットのみがあります。

e i e b e i i l s n p
                ^
                私 

アルファベット順の並べ替えの完全な例 [ 編集 | ソーステキストを編集します ]

この例では、QuickSortalgorithmは文字シーケンス「QuickSort」を並べ替える必要があります。最初に正しい要素 p-> ピボテルメントとして定義されています。次に、左から右に「大きく」実行され、右から左に「小さい」とカウンターgが「大きく」kを実行します。

QuickSort
^ ^^
g kp 

gがピボットエレメントよりも大きい要素を満たし、小さいか等しくピボットリングを等しくkまでの要素を満たすまで。

QuickSort
  ^ ^^
  g kp 

これらの2つの要素RとUが見つかったuは、次のステップで交換されます。

QRICKSOUT
  ^ ^^
  g kp 

次のステップでは、インデックスkとgは通常と同じ方向に続き、ピボット要素よりも大きい、またはkでピボテルメントよりも大きい要素を探しています。

QRICKSOUT
       ^^^
       KGP 

今、KとGは互いに通過しました。このイベントは終了条件です。これで、ピボット要素はGで示される要素と交換されます。

qricksotu
       ^^^^
       kpg 

次の2つのステートメントが真実です。「ピボテレメントの左側には、すべての要素が小さいか、ピボテルメントが等しくあります。ピボテレメントの右側には、すべてが大きい要素が大きい、または等しくピボットレメントがあります。」

リンク|:| rechts
 qrickso | t | u
       ^|^|^
       k | p | g 

ピボテレメントは、左半分の左半分のピボテレメントのポイントでのデータの量を「共有」します。
これで、アルゴリズムは前のアルゴリズムと同じ方法で左と右の部分を引き続き処理する必要があります。これにより、再帰が発生しました。正しい部分(文字u)は単一の要素のみであるため、定義によりソートされます。したがって、左の部分は現在扱われています。適切な要素は再びピボテルメントであり、カウンターは適切に設定されています。

qrickso | t | u
^ ^^
g kp 

QはOよりも大きく、KはOよりも小さいです。

qrickso | t | u
 ^ ^ ^
 G K p 

したがって、Qとkが交換されます。

kricqso | t | u
 ^ ^ ^
 G K p 

挿入gとkは走り続けます...

kricqso | t | u
  ^ ^ ^
  G K p 

RとCが交換されます。

kcirqso | t | u
  ^ ^ ^
  G K p 

次のステップでは、インデックスは再び互いに通り過ぎました...

kcirqso | t | u
   ^^ ^
   kg p 

...そして、ピボテレメント(文字o)は、より大きな要素(文字R)と交換されます。

kcioqsr | t | u
   ^^ ^
   KP g 

今、別の左と右の部分があります。

リンク:Rechts
 kci | o | qsr | t | u
   ^|^| ^ | |
   k | p | g | | 

最初に左部が扱われます。

kn | | qsr | t | u
^ ^ | | | ^^ | | "
G kl | | g kp | | " 
cki | o | qsr | t | u
 ^^ ocus | | ^^^ | |
 GKP | | g kp | | 

文字CとKが交換されます。

cki | o | qsr | t | u
 ^^ ocus | | ^^^ | |
 KGP | | g kp | | 

インデックスは互いに通り過ぎており、インデックスgの要素はインデックスpの要素と交換されます。

cik | o | qsr | t | u
 ^^^ | |^^^ | |
 kpg | | g kp | | 

現在発生している新しい左右の部分は、単一の要素のみで構成され、ソートされていると見なされます。

cik | o | qsr | t | u
    | | ^^^ | |
    | | KGP | | 

前者の右の部分(文字QSR)では、インデックスは互いに直接実行され、Gの要素はピボット要素と交換されます。

cik | o | qrs | t | u
    | | ^^^ | |
    | | kpg | | 

すべての文字がソートされます。

cik | o | qrs | t | u 

結果:

CICOQSTU 

間隔 [ 編集 | ソーステキストを編集します ]

アルゴリズムの持続時間は、本質的にピボット要素の選択に依存します。

最悪の場合(最悪の場合)、ピボット要素は常に選択されているため、リスト内の最大または最小の要素になります。たとえば、この場合、リストの最後に要素が常にピボット要素として選択され、ソートされるリストが既にソートされている場合です。検討すべきリストは、すべての再帰ステップで1つの小さいもののみであり、時間の複雑さはによって説明されます

o n 2 )) {displaystyle {mathcal {o}}(n^{2})}

。この場合の比較の数はあります

n(n+1)2 - 初め = n22+ n2 - 初め {displaystyle {tfrac {ncdot left(n+1 right)} {2}} - 1 = {tfrac {n^{2}} {2}}+{tfrac {n} {2}} - 1}

最良のケース(ベストケース)では、2つの部分的なリストがほぼ同じサイズになるように、ピボット要素が常に選択されます。この場合、アルゴリズムの漸近項に適用されます

o n de ログ n )) )) {displaystyle {mathcal {o}}(ncdot log(n))}

。今回の複雑さは、平均的なケース(平均ケース)にも適用されます。再帰通話の長い部品リストの長さは平均してです

2ni=n2n1= 34n - 24{displaystyle textStyle {frac {2} {n}}合計制限_ {i = {frac {n} {2}}}}^{n-1} i = {frac {3} {4}} n- {frac {2} {4}}}} {4}}}

そして、その中の再帰の深さ

o ログ n )) )) {displaystyle {mathcal {o}}(log(n))}

。平均的な場合、比較の数は約です

2 de ログ 2 )) de n + 初め )) de ログ 2 n )) 初め 39 de n + 初め )) de ログ 2 n )) {displaystyle 2cdot log(2)cdot(n+1)cdot log _ {2}(n)約1,39cdot(n+1)cdot log _ {2}(n)}

[3]

ピボット要素を選択するための文献では、さまざまなアプローチが説明されています。最悪のケースが到着する確率は、サイズが異なります。

可能なアプローチは、常にリストの最後または中央の要素を選択する最初のアプローチです。ただし、この素朴なアプローチは比較的非効率的です。別のオプションは、これら3つの要素の中央値を決定し、それをピボテレメントとして使用することです。
別のアプローチは、ピボット要素としてランダム要素を選択することです。これで ランダム化されたQuickSeport 最悪の期間が非常に低い結果として、ピボット要素が各部門ステップで選択される可能性があります。彼は実際には決して現れないと想定することができます。

たとえば、Hepssondなどのアルゴリズムがありますが、最悪の場合にも条件があります

o n de ログ n )) )) {displaystyle {mathcal {o}}(ncdot log(n))}

限られています。
ただし、実際には、最悪の場合のクイックソートが非常にまれに発生し、中間ケースでは機関車よりも高速であると想定されるため、クイックスの場所がまだ使用されています。ただし、これは依然として研究の主題であり、一部の分析とシミュレーションは、情報の理論的な考慮事項と実装の考慮事項の両方から、正面にHapssontバリアントを見ることができます。 [4] [5]

今日、QuickSortは、幅広い実用的なアプリケーションに適したソートアルゴリズムです。これは、高速であり、再帰が利用可能な場合、簡単に実装できます。すでに多くの標準ライブラリで利用可能です。ただし、それまでの間、代替手段もイントロソートで利用できます。これは、同等の中期で、最悪の場合の上部障壁もあります。

o n de ログ n )) )) {displaystyle {mathcal {o}}(ncdot log(n))}

保証されています。

中央メディアンアルゴリズムを使用して、ピボテレメントを選択します。これはの配列の中央値です

o n )) {displaystyle {mathcal {o}}(n)}

確かに、のランタイム

o n de ログ n )) )) {displaystyle {mathcal {o}}(ncdot log(n))}

クイックソートの最悪の場合は保証されます。

収納スペース [ 編集 | ソーステキストを編集します ]

QuickSortはインプレースプロセスです。リストの要素をリスト内に並べ替えるだけで、追加のストレージスペースにコピーしませんが、スタック上の再帰レベルごとに追加のスペースが必要です。

この用語と同様に、メモリ消費はピボテレメントの選択と利用可能なデータの種類にも依存します。最も安価で平均的なケースでは、再帰の深さがあります

o ログ n )) )) {displaystyle {mathcal {o}}(log(n))}

、スタックサイズのみです

o ログ n )) )) {displaystyle {mathcal {o}}(log(n))}

必要です。

最悪の場合、再帰の数はリストの長さによるものです

n {displaystyle n}

限られた、なんてサイズのスタック

o n )) {displaystyle {mathcal {o}}(n)}

必要。これにより、長いリストにスタックオーバーフローが発生する可能性があります。この問題を解決するか、少なくともその発生の可能性を減らすために、アルゴリズムに多様な変更があります。 [6]

  • 再送信の処分(後続のpseudocodeを参照)
  • よりバランスのとれたピボットエレメントの選択(例:3の中央値)
  • ランダムなピボテルメントを選択します。これは、ピボットを選択する方法との相互作用における特定の要素の前ソートから生じる可能性のある体系的な問題を防ぐことを防ぎます
  • 2つ未満の要素を持つ部分リストの回避(結果、ピボット要素がサブリストから取り出された場合でも、再帰の最大深度

次の擬似コードは、より短いサブリストが再帰的にソートされるように、残りのサブリストが再び分割される(および短いサブリストのソート)を再帰的にソートするように、最終規制(2番目のサブリストの並べ替え)を反復に置き換えます。再回帰の深さはより大きくありません

ログ n )) {displaystyle log(n)}

 関数 QuickSort(左、右) に限って 右>左 繰り返す 部分:=パーツ(左、右) リーガルディバイダー>部分リンク
            QuickSort(左、分割-1) // SMALL SUB -LIST RECURSIVE .. 左:=仕切り + 1 // ..繰り返し大きく並べ替えます  さもないと QuickSort(仕切り + 1、右)
            右:=仕切り-1 終わり  終わり  終わり  

別の可能性

n {displaystyle n}

線形の追加ストレージスペースを避けることは、2つの部分的な成功のうち少ないことが最初に再帰的にソートされることです(もう1つは後でソートされますが、再帰的)。したがって、まだソートされていない部分シーケンスの数は残っています

o ログ n )) )) {displaystyle {mathcal {o}}(log(n))}

限定。各パーツの部分的な部分シーケンスに2つのインデックス制限が保存され、それぞれがそれぞれ

o ログ n )) )) {displaystyle {mathcal {o}}(log(n))}

追加の保管スペースが必要です。全体として、このバリアントのニーズを備えたクイックソート

o ログ 2 n )) )) {displaystyle {mathcal {o}}(log ^{2}(n))}

追加のストレージスペース。

チェーンズリストのクイックソート [ 編集 | ソーステキストを編集します ]

ChainedリストのQuickSortバリアントの場合、分割される最初のエピソードがピボット要素として選択されます。ポインターポインターは、ピボットよりも小さい要素を満たすまで前方に押し上げられます。したがって、Zeiger2が通過した要素は、ピボットが大きくなっています。これらのより大きな要素の最初をPOINTER2と交換すると、問題の要素が正しいセクションになることが保証されます。 Zeger1は、ピボットよりも小さい要素のセクションの現在の端をマークします。 Zeger2がエピソードの右端に到達して分割すると、ピボット要素は部分シーケンス間の正しい位置に交換されます。

//左、右はここにポインターです QuickSort(左、右): 左<>右 それから  //(ローカル)ポインターの初期化  // Zeiger0はZeger1の前身としてのみ必要です Zeger0:=左
    Zeiger1:=左
    Zeger2:=左 
ピボット:=左番号 
 繰り返す Zeger2:= pointer2。後継者; 
 Zeiger2.spahl それから
        Zeger0:= Zeger1;
        Zeger1:= pointer1。後継者;
        Exchange(Zeiger1、Zeiger2) そうです pointer2 =右; 
Exchange(左、pointer1); 
 Zeger1 <>右 それから Zeger1:= pointer1。後継者; 
QuickSort(左、pointer0);
    QuickSort(Zeiger1、右); 終わり  

この手順の欠点は、主にソートされたエピソードまたは多くの同様の重要な価値が最悪の場合の動作につながることです。したがって、チェーンズリストのメルゲシュマンなどのソートアルゴリズムを選択するのが好きです。これは、最悪の場合の時間の複雑さでもあります。

o n ログ n )) )) {displaystyle {mathcal {o}}(nlog(n))}

自分の。バランスの取れた木(たとえば、Bツリー、AVLツリーなど)などの他の動的なデータ構造は、挿入手術にソートコストを分配しているため、その後のソートは必要ありません。

反復液クイックソート [ 編集 | ソーステキストを編集します ]

クイックソートは、小さなスタックまたはアレイの助けを借りて実装することもできます。
これは、ピボテルメントのランダムな選択を備えた単純なバリアントです。

関数 QuickSort_iterative(左、右)
   偶然:=ランダム() //ランダム開始値  繰り返す  //エクステリアループ  に限って 左<右 繰り返す ピボット:=データ[ ランダム (左右)] //左から右の間にあるランダムな要素の選択 プッシュ(右) //右部を後で並べ替えます NOT:=リンク 繰り返す  //内側ループ、部品フィールド  に限って データ[センター] <ピボット 繰り返す  //左から誤ってソートされた要素を検索します NOT:= NOT + 1 終わり  に限って データ[右]>ピボット 繰り返す  //右から誤ってソートされた要素を検索します 右:=右-1 終わり  中間> =右 それから 内部ループを中止します
            データ[中央]をデータと交換[右] 終わり  //フィールドを分割し、左部分をさらに並べ替えます  終わり  スタックレザー それから 外部ループの解体 //左の別の部分? 左:=右 + 1
      ポップ(右) //今すぐソートします  終わり  終わり  

指示 押す スタックに要素を置きます ポップ 持ち帰られます。
ピボテルメントのランダムな選択は、平均的なケースを確保する可能性が非常に高いです。
必要なスタッキングストアのサイズは、十分な確実性で2.ログが小さくなります 2 (s)。限られたスタックがオーバーフローする場合は、前から簡単に開始して残りの部分を並べ替えることができます。

クイックソートの効率は、遠くから要素を交換することです。ソートするリストが短いほど、より複雑であるため、より非効率的にクイックソートが機能します

o n 2 )) {displaystyle {mathcal {o}}(n^{2})}

近づいた。ただし、QuickSortによって解体されたリストには、要素とそのソートされた位置の間の距離が制限されているというプロパティがあります。このようなリストは、線形時間に挿入位置を並べ替えるため、挿入位置で並べ替えを続けるために、定義されたサブリストの長さ以下の実装でQuicksの場所がキャンセルされることがよくあります。

機能言語のクイックソート [ 編集 | ソーステキストを編集します ]

HaskellやErlangなどの機能的言語では、より強力なリスト処理により、クイックソートを非常に簡単に実装できます。

QuickSort  ::  言葉  a  =>  [ a ]   - >  [ a ]  QuickSort  []  =  []  QuickSort  そうです ))  =  QuickSort  [ バツ  |  バツ  < -    バツ  <= =  そうです ]  ++  [ そうです ]  ++  QuickSort  [ バツ  |  バツ  < -    バツ  >  そうです ]  

ある ++ リストのチェーンオペレーター。パターン (e:es) リストの最初の要素を分離します [x | x <-ES、x <= e] リストのすべての要素が得られます それはピボテレメントよりも小さいです そうです

並列クイックソート [ 編集 | ソーステキストを編集します ]

いくつかのプロセッサ/コアが利用可能な場合は、クイックソートを並列化することもできます。これは、より良い条件に達することができることを意味します。

  • ロバート・セッジウィック: アルゴリズム。 Pearson Studies、2002、ISBN 3-8273-7032-9。
  • トーマス・H・コルメン、チャールズ・E・レイロン、ロナルド・L・リベスト、クリフォード・スタイン: アルゴリズムの概要 。第2版​​。 Press、Cambridge MA 2001、ISBN 0-262-03293-7(アルゴリズムの標準作業)。
  • トーマス・H・コルメン、チャールズ・E・レイロン、ロナルド・L・リベスト、クリフォード・スタイン: アルゴリズム。はじめに 。 2.、corr。版。オルデンブール、ミュンヘン /ウィーン2007、ISBN 978-3-486-58262-8(英語: アルゴリズムの概要 。 Karen Lippert、Micaela Krieger-Hauwedeによる翻訳)。
  1. C. A. R.ホア: QuickSort 。の: コンピュータージャーナル バンド 5(1) 、1962年、 S. 10–15 、doi: 10.1093/comjnl/5.1.10
  2. スティーブンS.スキーナ: アルゴリズム設計マニュアル 。 Springs、2011、2011、ISBN 978-1-84800-069-8、 S. 129 Google COM [2013年7月18日にアクセス])。
  3. QuickSortのパフォーマンス (PDF)イリノイ大学システム、数学、統計学部、コンピューターサイエンス。
  4. Paul Hsieh: 再検討されたソート。 の: azilionmonkeys.com。 2004年、 2010年4月26日に取得 (英語)。
  5. デビッド・マッカイ: Heapsort、QuickSort、およびエントロピー。 (オンラインで利用できなくなりました。)www.aims.ac.za/~mackay、2005年12月1日、アーカイブ オリジナル 午前 7. 2007年6月 ; 2010年4月26日に取得 (英語)。
  6. エリス・ホロウィッツ、サルタジ・サニ、スーザン・アンダーソン・フリード: cのデータ構造の基本 。第1版。 International Thomson Publishing、1994、ISBN 3-929821-00-1、 S. 342
after-content-x4