最悪の場合、このソートアルゴリズムはΘ(n³)であり、Θ(n²)ではありませんか?


52

データ構造とアルゴリズムの講座を始めたばかりで、ティーチングアシスタントは整数の配列を並べ替えるための次の擬似コードを提供してくれました。

void F3() {
    for (int i = 1; i < n; i++) {
        if (A[i-1] > A[i]) {
            swap(i-1, i)
            i = 0
        }
    }
}

明確ではないかもしれませんが、ここではソートしようとしている配列のサイズです。nA

いずれにしても、ティーチングアシスタントはクラスにこのアルゴリズムは時間(最悪の場合、私は信じている)であると説明しましたが、逆に並べ替えられた配列で何度も調べても、私には、ではなくであるように思われます。Θ n 2Θ n 3Θ(n3)Θ(n2)Θ(n3)

誰かがこれがΘ(n3)ではなくΘ(n ^ 3)である理由を説明できΘ(n2)ますか?


構造化された分析アプローチに興味があるかもしれません。自分で証拠を見つけよう!
ラファエル

それを実装して、自分を納得させるために測定するだけです。逆の順序で10,000個の要素を持つ配列には数分かかり、逆の順序で20,000個の要素を持つ配列には約8倍の時間がかかります。
gnasher729

@ gnasher729あなたは間違っていないですが、私の解決策は異なっている:あなたがしようとする証明あなたのあなたは常にあなた代間違っを教えてくれますどの、失敗するバインド。(もちろん、両方を行うことができます。仮説を棄却する場合、プロット/フィッティングは間違いなく高速ですが、信頼性は低くなります。形式的/構造化分析を行う限り、害はありません。プロットに頼ることが問題の始まりです。)O(n2)
ラファエル

1
i = 0声明のため
njzk2

回答:


60

このアルゴリズムは次のように書き直すことができます

  1. 反転Aが見つかるまでスキャンします。
  2. 見つかったら、交換してやり直してください。
  3. 存在しない場合は終了します。

現在、最大で反転があり、それぞれを見つけるために線形時間スキャンが必要です-最悪の場合の実行時間は。多くの人が負けているパターンマッチングアプローチをトリップする美しい教育の例!(n2)Θ(n2)Θ(n3)

ベーネ注意:一つは、少し注意する必要があります。(下限のために)記載のコストがアップ追加すること自体は簡単ではありませんので、いくつかの反転は、いくつかの後半、早期に表示されます。また、スワップによって新しい反転が発生することはありません。逆に並べ替えられた配列を使用したケースのより詳細な分析では、ガウスの式の2次のケースのようなものが得られます。

@ gnasher729が適切にコメントしているように、入力ソートする際の実行時間を分析することにより、最悪の実行時間がであることが簡単にわかります。(この入力はおそらくありませんが、最悪の場合)。Ω(n3)[1,2,,n,2n,2n1,,n+1]

注意してください:逆に並べ替えられた配列が、すべての並べ替えアルゴリズムの最悪の入力であると必ずしも仮定しないでください。それはアルゴリズムに依存します。逆ソートされた配列が最悪のケースではなく、最良のケースに近い場合もあるソートアルゴリズムがいくつかあります。


14
前半が昇順の1〜n / 2の数字で構成され、後半が逆順のn〜n / 2 + 1の配列をとる場合、少なくともn / 2が必要であることは明らかです。各反転を見つけるための手順、およびそれらの約(n / 2)^ 2/2があります。そして、それはおそらく最悪のケースではありません。
gnasher729

@AnthonyRosselloこれは標準の結果です(順列の組み合わせで)。要するに、逆にソートされた配列内の反転の数を数えます(それが最悪のケースであることは明らかですか?)。ガウス和です。
ラファエル

何であれ、部分和は常に、急速に低下するのは係数だけであることを覚えておく必要があり(非常に大きな係数注意してください)。問題は、が係数を気にしないことです。Θ(nα)Θ(nα+1)k=0nkα1α+1nα+11α+1Θ
よ」

2
@yo 'そして、これは答え(または質問)に関連していますか?
ラファエル

7

これについての別の考え方iは、リセットされる前の最大値が何になるかということです。これにより、結果として、前のソート順がAアルゴリズムの実行時間にどのように影響するかをより簡単に推論できます。

特に、i新しい最大値を設定するときにNと呼び、配列[A[0], ..., A[N-1]]が昇順で並べ替えられることに注意してください。

では、要素A[N]をミックスに追加するとどうなりますか?

数学:

さて、位置に収まるとし。次に、を配置するためにループの反復()が必要を配置するために反復を配置します。pNNstepsN1N+(N1)N2

stepsN(pN)=N+(N1)+(N2)++(pN+1)=12(N(N+1)pN(pN+1))

ランダムに並べ替えられた配列の場合、は、各に対しての一様分布を取ります。pN{0,1,,N}N

E(stepsN(pN))=a=1NP(pN=a)stepsN(a)=a=1N1N12(N(N+1)a(a+1))=12(N(N+1)13(N+1)(N+2))=13(N21)=Θ(N2)

合計は、Faulhaberの式または下部のWolfram Alphaリンクを使用して表示できます。

逆にソートされた配列の場合、すべてのに対してになり、次のようになります。pN=0N

stepsN(pN)=12N(N+1)

正確に、他の値よりも厳密に長くます。pN

すでにソートされた配列の場合、およびで、下位の用語が関連します。pN=NstepsN(pN)=0

合計時間:

合計時間を取得するために、すべてのステップを合計します。(私たちが細心の注意を払っていた場合、スワップとループの反復を合計し、開始条件と終了条件を処理しますが、ほとんどの場合、それらが複雑さに寄与しないことは比較的簡単にわかります) 。N

繰り返しになりますが、期待の線形性とフォーハーバーの公式を使用します。

Expected Total Steps=E(N=1nstepsN(pN))=N=1nE(stepsN(pN))=Θ(n3)

もちろん、何らかの理由でがはない場合(たとえば、私たちが見ている配列の分布がソートされていることに既に近い場合)、これは必ずしも必要ではありませんそうです。しかし、これを実現するには非常に特殊な分布がです!stepsN(pN)Θ(N2)pN

関連する読書:


@Raphael-提案された改善のおかげで、もう少し詳細を追加しました。さて、ランダム変数は(の順序のセットから)なので、期待は介して技術的に行われますpiΩAΩ
デビッドE

異なる ; 私はランダウを意味しました。Ω
ラファエル

3

免責事項:

これは証拠ではありません(一部の人々は、私がそれをあたかもそれがあったかのように投稿したと思うようです)これは、OPが割り当てに関する疑問を解決するために実行できる小さな実験です。

逆に並べ替えられた配列を何度使用しても、ではなくであるように思われます。Θ(n2)Θ(n3)

このような単純なコードでは、との違いを見つけるのは難しくありません。多くの実際の場合、これは予測を調整したり、期待を調整したりするのに便利な方法です。Θ(n2)Θ(n3)


@Raphaelは既にあなたの質問に答えましたが、キックのためだけに、このgnuplotスクリプトを使用してこのプログラムの出力をにと、指数値およびが報告され、次のプロットが生成されました( 1つ目は通常のスケールで、2つ目は対数目盛です):f(x)=axb+cx2.997961668332222.99223727692339

普通の ログログ

これが役立つことを願っています¨


2
これらの値に任意の関数を適合させることができます。こちらもご覧ください
ラファエル

3
@Raphaelこの方法で選択することを望まない場合、いいえ、どの関数にも適合できません(たとえば、一定の関数を妥当な精度に適合させることはできません)。これは証明ではありませんが、スケッチを提供する答えがすでにあります。有用性に関しては、あなたがリンクしたあなた自身の投稿を引用することができます:「これは非常に有用なアプローチであり、時には十分に活用されていないことに同意しなければなりません」。さらに、OP は、ではなくにすべきだと考えていると言ったので、実験して、彼の予言が正しいかどうかを見てみませんか?続き Θ(n2)Θ(n3)
dtldarek

2
これは、アルゴリズムがという証拠を提供しますが、質問は理由を尋ねます。現象の説明ではなく、現象の説明を求めています。Θ(n3)
デビッドリチャービー

2
@DavidRicherbyこれは、この答えが役に立たないということですか?
dtldarek

3
@Magicsowonこれは、フォーラムではなく質疑応答サイトです。質問への回答を探しています。それについての議論は探していません。
デビッドリチャービー

3

配列があると仮定します。

array a[10] = {10,8,9,6,7,4,5,2,3,0,1}

アルゴリズムは次のことを行います

Scan(1) - Swap (10,8) => {8,10,9,6,7,4,5,2,3,0,1}  //keep looking at "10"
Scan(2) - Swap (10,9) => {8,9,10,6,7,4,5,2,3,0,1}
...
Scan(10) - Swap(10,1) => {8,9,6,7,4,5,2,3,0,1,10}

基本的には、最も高い要素の配列の最後にO(n^2)移動します。その際、各スキャンで最初からやり直します。ただし、n個の要素があるため、このn回数を繰り返す必要があります。これは正式な証拠ではありませんが、実行時間がなぜであるかを「非公式」な方法で理解するのに役立ちますO(n^3)


4
これは他の回答に何を追加しますか?アルゴリズムの機能の説明は既に与えられており、ランタイムの理由はせいぜい大ざっぱです。(ワーストケースは直線的に動作しませ!)
ラファエル

2
同じ形式を複数の方法で説明する価値がある場合があります(形式主義、「直感をポンピングする」簡単な例)、特に質問をする人が新しい分野の場合。それで、これが追加するのは、直感を助けるかもしれない方法で表示されるということです。
DW

フラグにコメントに対する返信があったので(そうしないでください!):「ワーストケースは直線的に動作しません!」-最悪の場合の演算子の代数的性質を意味します。大まかに言うと、WorstCase(1 + ... + n) "=" WorstCase(1)+ ... + WorstCase(n)を使用していますが、このIDは保持されません。
ラファエル

1
私はこの分野に不慣れであり、具体的で綴られた例で説明を提供することは、間違いなく問題についての直観を得るのに役立ちました。今、受け入れられた解決策は私にとってより意味があります。
vaer-k

0

ロジックは、配列内の要素を昇順で並べ替えているようです。

最小数が配列の末尾にあると仮定します(a [n])。それが正しい場所に来るためには-(n +(n-1)+(n-2)+ ... 3 + 2 + 1)操作が必要です。= O(n2)。

配列内の単一の要素には、O(n2)opsが必要です。したがって、要素の数はO(n3)です。


5
これは他の回答に何を追加しますか?アルゴリズムの機能の説明は既に与えられており、ランタイムの理由はせいぜい大ざっぱです。(最悪の場合は線形に動作しませ!)
ラファエル

素晴らしい説明。これにより、他の回答では説明されていない、問題に関する別のより直感的な視点が提供されます。(言うまでもなく、非常に短く理解しやすい。)
2501年

1
@ 2501いいえ、間違っています。ダイクストラのアルゴリズムでこの「直感」を使用すると、2次ランタイム(ノード数)が得られますが、これは間違っています。
ラファエル

@Raphaelいいえ、答えで説明されているとおりです。この説明は、他のアルゴリズムではなく、このアルゴリズムで機能します。彼らにとっては間違っているかもしれませんが、この主張はこの主張が間違っていることを証明するものではありません。
2501

@Raphael受け入れられた答えの説明を理解できませんでした。だから、私はこれを解決し、専門用語なしで簡単な用語で説明しようとしました..ので、これは受け入れられた答えを理解できなかった私のようなメンバーのためです。
mk ..
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.