実行可能なアプローチには非常に多くの種類があります。どちらが最適かは、
- あなたが見せようとしているもの、
- どれだけの詳細が必要または必要か。
アルゴリズムがサブルーチンとして使用する広く知られているものである場合、多くの場合、より高いレベルに留まります。アルゴリズムが調査中の主要なオブジェクトである場合、おそらくより詳細にしたいでしょう。分析についても同じことが言えます。大まかなランタイムの上限が必要な場合は、ステートメントの正確なカウントが必要な場合とは異なります。
よく知られているMergesortアルゴリズムの3つの例を紹介します。
高いレベル
アルゴリズムMergesortはリストを取得し、それを2つの(ほぼ)等しい長さの部分に分割し、それらの部分リストを再帰し、(ソートされた)結果をマージして、最終結果がソートされるようにします。シングルトンまたは空のリストでは、入力を返します。
このアルゴリズムは明らかに正しいソートアルゴリズムです。リストの分割とマージは、それぞれで実装できます。これにより、最悪の場合のランタイム。マスター定理により、これは評価され。T (n )= 2 T (nΘ(n)T(N)∈Θ(NログN)T(n)=2T(n2)+Θ(n)T(n)∈Θ(nlogn)
中レベル
アルゴリズムMergesortは、次の擬似コードで提供されます。
procedure mergesort(l : List) {
if ( l.length < 2 ) {
return l
}
left = mergesort(l.take(l.length / 2)
right = mergesort(l.drop(l.length / 2)
result = []
while ( left.length > 0 || right.length > 0 ) {
if ( right.length == 0 || (left.length > 0 && left.head <= right.head) ) {
result = left.head :: result
left = left.tail
}
else {
result = right.head :: result
right = right.tail
}
}
return result.reverse
}
帰納法により正しさを証明します。長さが0または1のリストの場合、アルゴリズムは非常に正確です。帰納仮説として、任意の、ただし固定された自然なmergesort
の長さが最大リストで正しく実行されると仮定します。ここで、を長さリストとします。帰納法の仮定により、およびホールド(非漸減)最初RESPのバージョンを並べ替えます。再帰呼び出し後の後半。したがって、ループはすべての反復で、まだ調査されていない最小の要素を選択し、それをに追加します。従ってからすべての要素を含む非ますますソートされたリストであり、そしてn > 1 L n + 1 L Lnn>1Ln+1left
right
Lwhile
result
result
left
right
。その逆は、非減少的にソートされたバージョンであり、返された結果であり、望ましい結果です。L
ランタイムに関しては、要素の比較とリスト操作(ランタイムを漸近的に支配する)をカウントしましょう。長さが2未満のリストはどちらも引き起こしません。長さがリストの場合、再帰呼び出しの入力、再帰呼び出し自体からの入力、およびloopとoneを準備することによって引き起こされる操作があります。両方の再帰パラメーターは、それぞれ最大リスト操作で計算できます。ループが正確に実行された多くても1つの要素を比較して、正確に2つのリストの操作で時間とすべての反復原因を。最終版はを使用するように実装できますn n 2 nn>1while
reverse
nwhile
nreverse
2nリスト操作-すべての要素が入力から削除され、出力リストに入れられます。したがって、操作カウントは次の繰り返しを満たします。
T(0)=T(1)T(n)=0≤T(⌈n2⌉)+T(⌊n2⌋)+7n
明らかに非減少であり、考慮するのに十分である漸近的な成長のために。この場合、繰り返しは単純化されますn = 2 kTn=2k
T(0)=T(1)T(n)=0≤2T(n2)+7n
マスター定理により、のランタイムに拡張されるが得られます。T∈Θ(nlogn)mergesort
超低レベル
Isabelle / HOLでのMergesortのこの(一般化された)実装を検討してください。
types dataset = "nat * string"
fun leq :: "dataset \<Rightarrow> dataset \<Rightarrow> bool" where
"leq (kx::nat, dx) (ky, dy) = (kx \<le> ky)"
fun merge :: "dataset list \<Rightarrow> dataset list \<Rightarrow> dataset list" where
"merge [] b = b" |
"merge a [] = a" |
"merge (a # as) (b # bs) = (if leq a b then a # merge as (b # bs) else b # merge (a # as) bs)"
function (sequential) msort :: "dataset list \<Rightarrow> dataset list" where
"msort [] = []" |
"msort [x] = [x]" |
"msort l = (let mid = length l div 2 in merge (msort (take mid l)) (msort (drop mid l)))"
by pat_completeness auto
termination
apply (relation "measure length")
by simp+
これには、明確な定義と終了の証明がすでに含まれています。ここで(ほぼ)完全な正当性の証拠を見つけます
「実行時」、つまり比較の回数については、前のセクションの繰り返しと同様の繰り返しを設定できます。マスター定理を使用して定数を忘れる代わりに、それを分析して、真の量に漸近的に等しい近似値を取得することもできます。完全な分析は[1]にあります。以下に大まかな概要を示します(Isabelle / HOLコードに必ずしも適合するとは限りません)。
上記のように、比較回数の繰り返しは
f0=f1fn=0=f⌈n2⌉+f⌊n2⌋+en
ここで、は部分的な結果をマージするために必要な比較の数です²。床と天井を取り除くために、が偶数であるかどうかを区別するケースを実行します。 nenn
{f2mf2m+1=2fm+e2m=fm+fm+1+e2m+1
とネストされた前方/後方差分を使用すると、e nfnen
∑k=1n−1(n−k)⋅Δ∇fk=fn−nf1。
合計は、ペロンの式の右側に一致します。私たちは、定義ディリクレ生成シリーズのとしてΔ∇fk
W(s)=∑k≥1Δ∇fkk−s=11−2−s⋅∑k≥1Δ∇ekks=: ⊟(s)
ペロンの式と一緒に
fn=nf1+n2πi∫3−i∞3+i∞⊟(s)ns(1−2−s)s(s+1)ds。
評価は、分析されるケースによって異なります。それ以外に、いくつかのトリックの後、剰余定理を適用して⊟(s)
fn∼n⋅log2(n)+n⋅A(log2(n))+1
ここで、は値を持つ周期関数です。[ − 1 、− 0.9 ]A[−1,−0.9]
- Mellin変換と漸近: FlajoletとGolinによるマージソートの再発(1992)
- 最適なケース:
最悪のケース:
平均的なケース:en=⌊n2⌋
en=n−1
en=n−⌊n2⌋⌈n2⌉+1−⌈n2⌉⌊n2⌋+1