アルゴリズムを記述し、それらを証明し、分析する方法は?


20

The Art of Computer Programming(TAOCP)を読む前に、これらの質問を深く考えたことはありません。擬似コードを使用してアルゴリズムを記述し、それらを理解し、成長の順序についてのみ実行時間を推定します。TAOCPは徹底的に私の心を変更します。

TAOCPは、ステップとgoto組み合わせた英語を使用してアルゴリズムを説明し、フローチャートを使用してアルゴリズムをより簡単に描写します。低レベルのように見えますが、特にフローチャートにはいくつかの利点があることに気付きました。計算がその矢印をたどるときの現在の状態についてのアサーションで各矢印にラベルを付け、アルゴリズムの帰納的証明を行うことができます。著者は言う:

著者の主張は、図4で行われたように、すべてのアサーションが暗黙的に満たされた点に到達した場合にのみアルゴリズムが有効である理由を本当に理解しているということです。

私はそのようなことを経験していません。別の利点は、各ステップが実行される回数をカウントできることです。キルヒホッフの最初の法則で確認するのは簡単です。実行時間を正確に分析していないため、実行時間を推定するときに一部が省略された可能性があります。±1

成長の順序の分析は役に立たない場合があります。たとえば、クイックソートとヒープソートはすべてであるため区別できません。ここで、はランダム変数予想数です。したがって、定数を分析する必要があります。およびしたがって、と良い。また、場合によっては、分散などの他の量を比較する必要があります。実行時間の増加順序の大まかな分析だけでは十分ではありません。TAOCPとしてETn=ΘnログnEバツバツET1n=A1nlgn+B1n+OログnE(T2(n))=A2lgn+B2n+O(logn)T1T2 アルゴリズムをアセンブリ言語に変換し、実行時間を計算します。私にとっては難しすぎるので、実行時間をもう少し大まかに分析するためのテクニックを知りたいと思います。これは、C、C ++または擬似コード。

そして、主に研究活動で使用されている記述スタイルと、これらの問題の処理方法を知りたいです。


6
アルゴリズムの実行時間をこれと厳密に比較するときは、非常に注意する必要があります。実際のコンピューターにはキャッシュ、レジスター、パイプラインがあり、実行時間を大幅に変更できます。どのアルゴリズムが実際に高速であるかを知りたい場合は、実際にコンピューターで実行する必要があります。
svick

1
実際には、このようクヌースの用途としてアセンブラの分析は、ある方法で何も隠さず、制御フローが簡単ですので、実際のコードを分析するよりも簡単。あなたは練習を求めています。デイブのコメントが当てはまると思います。開業医は、厳密な分析を行うよりも、ランタイム測定を使用してアルゴリズムを設計する可能性が高くなります。しかし、その後、私は開業医ではないので、私が言ったことを一粒の塩で取ります。
ラファエル

1
@Raphael My プログラミングではなく、研究活動の実践を意味します
Yai0Phah

@フランク、分散とはどういう意味ですか?私のパフォーマンステストでは、タイミングの違いがわかります。
edA-qa mort-ora-y

@Raphael、あなたの最初のポイント、これはもはや真実ではありません。最新のチップはアセンブリの順序を変更し、順不同でストア/ロードを実行し、実行とロードを予測します。並行性および以前の問題については、実際には徹底的な分析が必要ですが、正式な形式では行いません。
edA-qa mort-ora-y

回答:


18

実行可能なアプローチには非常に多くの種類があります。どちらが最適かは、

  • あなたが見せようとしているもの
  • どれだけの詳細が必要または必要か。

アルゴリズムがサブルーチンとして使用する広く知られているものである場合、多くの場合、より高いレベルに留まります。アルゴリズムが調査中の主要なオブジェクトである場合、おそらくより詳細にしたいでしょう。分析についても同じことが言えます。大まかなランタイムの上限が必要な場合は、ステートメントの正確なカウントが必要な場合とは異なります。

よく知られているMergesortアルゴリズムの3つの例を紹介します。

高いレベル

アルゴリズムMergesortはリストを取得し、それを2つの(ほぼ)等しい長さの部分に分割し、それらの部分リストを再帰し、(ソートされた)結果をマージして、最終結果がソートされるようにします。シングルトンまたは空のリストでは、入力を返します。

このアルゴリズムは明らかに正しいソートアルゴリズムです。リストの分割とマージは、それぞれで実装できます。これにより、最悪の場合のランタイム。マスター定理により、これは評価され。T n = 2 T nΘ(n)TNΘNログNT(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+1leftrightLwhileresultresultleftright。その逆は、非減少的にソートされたバージョンであり、返された結果であり、望ましい結果です。L

ランタイムに関しては、要素の比較とリスト操作(ランタイムを漸近的に支配する)をカウントしましょう。長さが2未満のリストはどちらも引き起こしません。長さがリストの場合、再帰呼び出しの入力、再帰呼び出し自体からの入力、およびloopとoneを準備することによって引き起こされる操作があります。両方の再帰パラメーターは、それぞれ最大リスト操作で計算できます。ループが正確に実行された多くても1つの要素を比較して、正確に2つのリストの操作で時間とすべての反復原因を。最終版はを使用するように実装できますn n 2 nn>1whilereversenwhilenreverse2nリスト操作-すべての要素が入力から削除され、出力リストに入れられます。したがって、操作カウントは次の繰り返しを満たします。

T(0)=T(1)=0T(n)T(n2)+T(n2)+7n

明らかに非減少であり、考慮するのに十分である漸近的な成長のために。この場合、繰り返しは単純化されますn = 2 kTn=2k

T(0)=T(1)=0T(n)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=f1=0fn=fn2+fn2+en

ここで、は部分的な結果をマージするために必要な比較の数です²。床と天井を取り除くために、が偶数であるかどうかを区別するケースを実行します。 nenn

{f2m=2fm+e2mf2m+1=fm+fm+1+e2m+1

とネストされた前方/後方差分を使用すると、e nfnen

k=1n1(nk)Δfk=fnnf1

合計は、ペロンの式の右側に一致します。私たちは、定義ディリクレ生成シリーズのとしてΔfk

W(s)=k1Δfkks=112sk1Δekks=: (s)

ペロンの式と一緒に

fn=nf1+n2πi3i3+i(s)ns(12s)s(s+1)ds

評価は、分析されるケースによって異なります。それ以外に、いくつかのトリックの後、剰余定理を適用して(s)

fnnlog2(n)+nA(log2(n))+1

ここで、は値を持つ周期関数です。[ 1 0.9 ]A[1,0.9]


  1. Mellin変換と漸近: FlajoletとGolinによるマージソートの再発(1992)
  2. 最適なケース: 最悪のケース: 平均的なケース:en=n2
    en=n1
    en=nn2n2+1n2n2+1

実行時間分析の私の質問は、および正確に決定する方法です、これは実践に近い(たとえば、merge-sortとqsortを比較するのに利用可能です)。αβT(n)=T(n/2)+T(n/2)+αn+β
Yai0Phah

@フランク:簡単な答えはあなたができない ; 定数は、基盤となるアルゴリズムにとって重要ではない実装の詳細(マシンアーキテクチャ、言語、コンパイラなど)に依存します。
-JeffE

@JeffEとは比較を行うのに十分なだけ正確であるべきだと主張しなければなりません。もっと簡単に言えば、定数を決定するために、機械語なしで多くの作業を行うことができる数学モデル。αβ
Yai0Phah

たとえば、@ JeffEはtaocpのMIX / MMIXですが、アルゴリズムをそのような機械語に翻訳するのは難しすぎます。
Yai0Phah

@FrankScience:練習に近づくには、すべての操作をカウントする必要があります(Knuthのように)。その後、マシン固有の操作コストで結果をインスタンス化して、実際のランタイムを取得できます(操作の順序、キャッシュ、パイプラインなどの影響を無視します)。通常、人々はいくつかの操作のみを数えます。その場合、と修正してもあまりません。αβ
ラファエル

3

ダイクストラの「プログラミングの規律」は、アルゴリズムの分析と証明、および証明可能性の設計に関するものです。その本の序文で、ダイクストラは、分析するために適切に設計された非常に単純な構成されたミニ言語が、多くのアルゴリズムを形式的に説明するのに十分であることを説明します。

このような本を始めると、「どのプログラミング言語を使用するのか」という質問にすぐに直面しますが、これはそうではありません単なるプレゼンテーションの質問です!あらゆるツールの最も重要な、しかし最もとらえどころのない側面は、その使用法を自分で訓練する人々の習慣への影響です。ツールがプログラミング言語である場合、この影響は、私たちが好むと好まざるとにかかわらず、私たちの思考習慣への影響です。私の知る限りではその影響を分析した結果、既存のプログラミング言語もそのサブセットも私の目的に合わないという結論に達しました。一方で、新しいプログラミング言語の設計にはあまりにも準備ができていないので、次の5年間はそうしないと誓いました。(その前に、他の多くのものの中で、このモノグラフを書かなければなりませんでした。

後に彼は、自分がどれだけ小さな言語を手に入れることができたかを説明します。

読者に、ミニ言語を非常に小さくして、手続きや再帰すら含まないようにした理由を説明する必要があります。... 要点は、私のメッセージを伝えるためにそれらの必要性を感じなかった、ということです。慎重に選択された関心の分離が、あらゆる点で高品質のプログラムの設計に不可欠である方法。ミニ言語の控えめなツールは、自明ではないが非常に満足のいく設計を行うための十分な自由度をすでに与えてくれました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.