web-dev-qa-db-ja.com

平均的な場合に挿入ソートsort(n ^ 2)になるのはなぜですか?

挿入ソート ランタイムはΩ(n)(入力がソートされている場合)およびO(n2)(入力が逆ソートされている場合)。平均して、it(n2)時間。

どうしてこれなの?たとえば、平均的なケースがO(n log n)に近くないのはなぜですか?

25
templatetypedef

この質問に答えるために、まず挿入ソートの実行時間を評価する方法を決定しましょう。ランタイムの素敵な数式が見つかれば、その式を操作して平均ランタイムを決定できます。

必要な重要な観察は、挿入ソートの実行時間は入力配列内の inversions の数に密接に関連しているということです。配列の反転は、相対順序が間違っている要素A [i]とA [j]のペアです。つまり、i <jですが、A [j] <A [i]です。たとえば、次の配列では:

0 1 3 2 4 5

反転が1つあります。3と2を入れ替える必要があります。この配列では:

4 1 0 3 2

6つの反転があります。

  • 4と1
  • 4と0
  • 4と3
  • 4と2
  • 1と0
  • 3と2

反転の重要な特性の1つは、ソートされた配列に反転がないことです。これは、すべての要素は、その後ろに来るすべての要素より小さく、その前に来るすべての要素より大きい必要があるためです。

これが重要な理由は、挿入ソートで実行された作業量と元の配列の反転数の間に直接的なリンクがあるためです。これを確認するために、挿入ソートの簡単な疑似コードを見てみましょう。

  • For i = 2 .. n:(1-indexingと仮定)
    • J = i-1に設定します。
    • A [j]> A [j + 1]の間:
      • A [j]とA [j + 1]を入れ替えます。
      • J = j-1に設定します。

通常、このような関数によって行われる作業の総量を決定する場合、内側のループによって行われる作業の最大量を決定し、それを外側のループの反復回数で乗算することができます。これは上限を与えますが、必ずしもタイトな限界ではありません。行われた作業全体を説明するより良い方法は、2つの異なる作業ソースがあることを認識することです。

  • 2、3、...、nを数える外側のループ
  • スワップを実行する内部ループ。

その外側のループは常にΘ(n)が機能します。ただし、内部ループは、アルゴリズムのランタイム全体で行われたスワップの総数に比例する量の処理を実行します。そのループがどの程度の作業を行うかを確認するには、アルゴリズムのすべての反復で行われる合計スワップの数を決定する必要があります。

ここで反転が発生します。挿入ソートを実行すると、常に配列内の隣接する要素が交換され、反転を形成する場合にのみ2つの要素が交換されることに注意してください。では、スワップを実行した後、配列内の反転の総数はどうなりますか?さて、グラフィカルに、これがあります:

 [---- X ----] A[j] A[j+1] [---- Y ----]

ここで、Xは交換されたペアの前に来る配列の部分であり、Yは交換されたペアの後に来る配列の部分です。

A [j]とA [j + 1]を交換するとします。反転の数はどうなりますか?さて、2つの要素間の任意の反転について考えてみましょう。 6つの可能性があります。

  • 両方の要素がXにあるか、両方の要素がYにあるか、または1つの要素がXにあり、1つの要素がYにあります。これらの要素のいずれも移動しなかったため、反転がまだあります。
  • 1つの要素はXまたはYにあり、もう1つの要素はA [j]またはA [j + 1]です。次に、要素の絶対位置が変更されていても、要素の相対的な順序は変更されていないため、逆転はまだそこにあります。
  • 1つの要素はA [j]で、もう1つの要素はA [j + 1]です。次に、スワップ後に反転が削除されます。

これは、スワップを実行した後、隣接するペアの反転のみが消えたため、反転の数を1つだけ減らすことを意味します。これは、次の理由で非常に重要です。Iの反転から始めると、スワップごとに数が1つ減ります。反転がなくなると、それ以上のスワップは実行されません。したがって、スワップの数は反転の数と同じです

これにより、挿入ソートの実行時間をΘ(n + I)として正確に表すことができます。ここで、Iは元の配列の反転の数です。これは、元のランタイム境界と一致します-並べ替えられた配列では、0の反転があり、ランタイムはΘ(n + 0)=Θ(n)です。逆に並べ替えられた配列では、n(n-1)/ 2つの反転、および実行時間はΘ(n + n(n-1)/2) =Θ(n2)。気の利いた!

これで、特定の配列を指定した挿入ソートの実行時間を分析する非常に正確な方法が手に入りました。平均実行時間を分析する方法を見てみましょう。これを行うには、入力の分布について仮定を行う必要があります。挿入ソートは比較ベースのソートアルゴリズムであるため、入力配列の実際の値は実際には重要ではありません。それらの相対的な順序のみが実際に重要です。以下では、すべての配列要素が別個であると仮定しますが、そうでない場合、分析はそれほど変化しません。私たちがそこに着いたときに、物事が台無しになるところを指摘します。

この問題を解決するために、X形式のインジケーター変数の束を導入しますij、ここでXij A [i]とA [j]が反転を形成する場合は1、それ以外の場合は0の確率変数です。これらの変数のn(n-1)/ 2は、要素の個別のペアごとに1つです。これらの変数は、配列内の可能な反転のそれぞれを説明することに注意してください。

これらのXが与えられると、配列内の反転の総数に等しい新しいランダム変数Iを定義できます。これは、Xの合計によって与えられます。

I =ΣXij

配列の予想される反転数であるE [I]に興味があります。期待の線形性を使用して、これは

E [I] = E [ΣXij] =ΣE [Xij]

したがって、E [Xij]、予想される反転数、したがって予想される実行時間を決定できます!

幸い、すべてのXijはバイナリ指標変数であり、

E [Xij] = Pr [Xij = 1] = Pr [A [i]とA [j]は反転です]

では、重複のないランダムな入力配列が与えられた場合、A [i]とA [j]が逆になる確率はどのくらいでしょうか。まあ、半分の時間、A [i]はA [j]より小さく、残りの半分の時間A [i]はA [j]より大きくなります。 (重複が許可されている場合、重複を処理するための卑劣な余分な用語がありますが、ここでは無視します)。したがって、A [i]とA [j]の間に反転がある確率は1/2です。したがって、次のようになります。

E [I] =ΣE[Xij] =Σ(1/2)

合計にn(n-1)/ 2項があるため、これは

E [I] = n(n-1)/ 4 =Θ(n2

そして、期待して、on(n2)反転なので、期待どおりにランタイムはΘ(n2 + n)=Θ(n2。これにより、挿入ソートの平均ケースの動作がΘ(n2)。

お役に立てれば!

37
templatetypedef

楽しみのために、サイズnのカウント比較のベクトルのすべてのデータの組み合わせを実行するプログラムを作成し、最良のケースはn-1(すべてソート)で、最悪のケースは(n *(n-1))/ 2であることがわかりました。

異なるnに対するいくつかの結果:

  n min     ave     max ave/(min+max) ave/max

  2   1     1         1        0.5000
  3   2     2.667     3        0.5334
  4   3     4.917     6        0.5463
  5   4     7.717    10        0.5512
  6   5    11.050    15        0.5525
  7   6    14.907    21        0.5521
  8   7    19.282    28        0.5509
  9   8    24.171    36        0.5493
 10   9    29.571    45        0.5476
 11  10    35.480    55        0.5458
 12  11    41.897    66        0.5441

平均値は、最大値よりも最小値に近いようです。

編集:追加の値

 13  12    48.820    78        0.5424        
 14  13    56.248    91        0.5408

編集: 15の値

 15  14    64.182   105        0.5393

編集:選択された高い値

 16  15    72.619   120        -       0.6052
 32  31   275.942   496        -       0.5563
 64  63  1034.772  1953        -       0.5294
128 127  4186.567  8128        -       0.5151
256 255 16569.876 32640        -       0.5077

私は最近、nのより高い値の挿入ソートの比較の平均数を計算するプログラムを書きました。これらから、nが無限大に近づくにつれて、平均ケースは2で割った最悪のケースに近づくという結論を導き出しました。

2
Olof Forshell

ほとんどのアルゴリズムの平均ケースは、最悪のケースと同じです。これがなぜかを確認するために、Oを最悪のケース、Ωを最高のケースと呼びましょう。おそらく、nが無限大になるとO> =Ωになります。ほとんどの分布では、平均ケースは、最良ケースと最悪ケースの平均に近くなります。つまり、(O +Ω)/ 2 = O/2 +Ω/ 2です。係数とO> =Ωは関係ないので、これはOと同じです。

明らかに、これは単純化しすぎです。平均ケースが最悪のケースと最良のケースの平均であるという仮定が無効になるように歪んでいる実行時間分布があります*。しかし、これはなぜこれがそうであるかについてあなたにまともな直感を与えるはずです。

* templatetypedefでコメントに述べられているように、いくつかの例は、クイックソート/クイック選択、BSTルックアップ(ツリーのバランスをとっていない場合)、ハッシュテーブルルックアップ、シンプレックスメソッドです。

0
Aaron Dufour