アルゴリズムのループ不変式


7

ペアの合計の問題の次の疑似コードを開発しました。

与えられた配列 A 整数と整数の b、位置があり、場合はYESを返し、それ以外の場合はNOを返します。i,jAA[i]+A[j]=b

次に、私のアルゴリズムが正しいことを示すループ不変条件を記述します。誰かが有効なループ不変のヒントを教えてくれますか?

PAIRSUM(A,b):
YES := true;
NO := false;
n := length(A);
if n<2 then
  return NO;

SORT(A);
i := 1;
j := n;
while i < j do  // Here I should state my invariant
   currentSum := A[i] + A[j];
   if currentSum = b  then
      return YES;
   else 
    if currentSum < b then
      i := i + 1;
    else
      j := j – 1;
return NO;

3
ようこそ!ループ不変量だけでは、アルゴリズムが正しいことを証明しないことに注意してください。これは、必要な事後条件を証明するのに適しているという意味で、正しい不変条件である必要があります。また、ループを途中で終了すると、このような証明が困難になることにも注意してください。ループ全体が常に処理されるようにアルゴリズムを書き直すことをお勧めします。
ラファエル

1
賢いことの代償がわかります。証明は難しくなります。1)正確さはAソートに大きく依存します。2)i及びj入力に応じて、ループの終わりに役割を切り替えます。3)ループ実行の数は入力に依存します。
ラファエル

1
このアルゴリズムを正しくしたい場合は、配列を徐々にソートする必要があることを忘れていたと思います。
Gopi

(おっと、Raphaelは既に:(と言っていましたが、まだ変更していません。)
Gopi

回答:


5

最初の簡略化:このアルゴリズムは、検査により、本来の位置での合計が b

2番目の単純化:常に終了します。 i そして jそれぞれ単調に上下に移動し、常に移動します。したがって、最終的にそれらは等しくなり、ループが終了します。

その答えがYESであると仮定しましょう。 x そして y ソート後に関心のあるインデックス x できるだけ小さくしてから y可能な限り大きくします(正しい合計でいくつかの可能なペアがある場合があります)。これは不変です:

  • ループの開始時に、 ix そして jy

もし i=x そして j=y、その後、アルゴリズムは停止し、YESと表示されます。ループ不変式は最初は取るに足らないものであり、i<x そして j>y。しかし、もしi=x、および j>y、次に、配列がソートされているため A[i]+A[j]>b、理由のため y選ばれました。そうj 到達するまで、将来のすべての反復で減少します y。他のケースでは、j 得る y 最初は対称です。

上記のように、アルゴリズムは常に終了します。これは、アルゴリズムが実際にYESを示すことを意味するため、これで完了です。

編集:この質問は証明の執筆に関するものなので、ここでは別のオプションを紹介しますが、これはより混乱しやすく、よりザラザラしていると思います。Tony Hoareが承認するかどうかはわかりません。

これが不変です:Aループのトップ、

  • もし s<i、それから、 x[n]A[s]+A[x]b
  • もし t>j、それから、 x[n]A[x]+A[t]b

繰り返しになりますが、これは当初は空虚に続きます。次に、いくつかのケースを確認します。

  • もし i=j、次に任意のインデックスのペア x そして y 少なくとも1つ x または y より小さい i またはより大きい j。これは、入力がYESであることを除外します。ループが終了し、NOと表示されるので、状態は良好です。
  • ループが終了していないと想定します。もしA[i]+A[j]=b、答えはYESで、アルゴリズムは機能します。
  • 想定する A[i]+A[j]b
  • サブケース:がある場合 j、仮説によって j<j、 そのような A[i]+A[j]=b、その後 A[j]<A[j] 配列がソートされたため A[i]+A[j]>b、 その原因 j減少する。これは、不変式がi。それを見るためにjがあることに注意してください x そのような A[x]+A[j]=b、その後 x<i、これは帰納的仮説によって除外されます。
  • サブケース:がある場合 i、仮説によって i>i、 そのような A[i]+A[j]=b、その後 A[i]>A[i] 配列がソートされたため A[i]+A[j]<b、 その原因 iを増やす。これは、不変式がj。それを見るためにiがあることに注意してください x そのような A[i]+A[x]=b、その後 x>j、これは帰納的仮説によって除外されます。
  • サブケース:他の2つのケースのどちらも成立しない場合、不変条件は自明に維持されます。

最後に、私たちは、 i そして j 常に小さくなるため、最終的には等しくなる必要があり、アルゴリズムは停止します。

備考:これはまったく同じ議論ですが、理解するのがより困難です。あなたもいくつかの専門的なテクニックを使ってこのような証明を書くことができます:

  • アルゴリズムが何をするかは言わないでください。ここで、入力がYESの場合、アルゴリズムは常に両端にできるだけ近い証明インデックスを提供します。2番目の証明は、ケース分析でこれを巧みに隠しています。
  • 物事に名前を付けないでください。アルゴリズムの機能を隠しているので、停止点を指定しないでください。
  • 簡単なケースを除外しないでください。このアルゴリズムは明らかにインスタンスなしで機能します。これを前に書き留めておかないと、より複雑な不変式になります。

これは優れた高レベルの証明ですが、ザラザラしたホアレスタイルの証明でこのアイデアをどのように使用しますか?特に、どこで入手できますかx,yから(存在しない可能性があるため)?
ラファエル

@Louis皮肉は簡単に誤解される可能性があるため、皮肉は悪い考えだと私は思います。また、2番目のバージョンも高レベルであるため、ここで失敗します。Hoareスタイルの証明はローカル引数を使用して、個々のコードステートメント間を移動します。OPが実際にその方向に進みたかったかどうかはわかりません。私はそれを想定しました。
ラファエル

3

おそらくこのようなものですか?

後のkループの反復、A[i...j]サイズのソートされたサブアレイを含んでいるn - kと、k元の要素に加算のいずれのb他の任意の要素にA除去されました。

おそらくこれを述べるより明確な方法は単純です:

kのためにループ本体の反復、0 <= k < nA[i...j]のソート(連続)サブアレイであるA[1...n](その要素が同じ順序であり、最初の反復前と同じ値を有する)を含むループ体k(すなわち、要素はi + j = k + 1)、その結果がA[1...n]含まれています必要な場合のみ、A[i...j]必要な要素のペア。

このループ不変式は便利です。ループが終了した場合(つまり、終了条件に達した場合)Aは、要素が1つある並べ替えられた配列であり、には要素のペアがないと結論付けることができるためですA

これは、この不変条件の証明スケッチ/アイデアです:

  • ループの前にA[1...n]は、ソートされた配列とがi = 1ありj = nます。したがって、ループ不変式はk = 0、ループ本体の反復後に保証されます。
  • ループ不変式がkまで、およびを含む場合に当てはまると仮定しますk'
  • ここで、ループ不変式がに当てはまることを示しk' + 1ます。に対応する反復中のループ本体の動作には3つのケースがありますk = k' + 1
    • A[i] + A[j] = b、その場合、ループは正しく戻りYES、このループの反復の後に反復はありません。
    • A[i] + A[j] < bその場合、私たちは安全の最小の要素を破棄することができAA[i]。これを確認するには、A[j]がの最大の要素でAあるため、との別の要素の合計Aがより小さい場合b、その他の要素との要素の合計Aも未満であることが保証されていることに注意してくださいb
    • A[i] + A[j] > bその場合、私たちは安全の最大の要素を破棄することができAA[j]。推論はケースのそれと同様A[i] + A[j] < bです。
  • ループ本体の終了後、i = j及びそのA一つだけの要素が含まれています。以来A、今だけ1つの要素が含まれます、それは自明である場合Aに加算する二つの要素が含まれていませんb

ループ不変の十分性を実証して、ループの終了後に解決策がないことを示す

これは、Raphaelのコメントで提起された質問に対処します。ここでは、このループ不変条件で十分である理由を説明します。

ループを終了するには、が必要i = jです。また、このループは常にYES、一致が見つかった場合に正しく出力するか、1つだけになるまで配列要素を削除する(インクリメントiまたはデクリメントのいずれかにより)ことで終了することにも注意してくださいj。この終了の引数は上のスケッチにはありませんが、簡単なはずです。

以来i = jAサイズ1のソートされた部分配列です。サイズ1の配列は存在するという要件を満たさないためx,y(私はこれをユニークを意味すると解釈していることに注意してください x,yそれ以外の場合は、提供されたアルゴリズムが正しくないため。検討A = [1]してb = 2A[x]+A[y]=b、およびループ不変で、私たちは何の要素ということを知っているのでA[k]k != i = j任意の要素に追加することができA[k']k' != k合計を達成するためには、bこれは証明のアイデアを完了していません。

終了条件に使用される仮定/事実:

  • 質問は正しく解釈されました、すなわち、 x そして y明確です。元の配列の異なる位置に要素を追加する必要があります。
  • i = j ループ本体の(通常の)終了後、ループは常に終了します。
  • 2つはありません x そして yij場合i = j; サイズ1のサブ配列には2つのインデックスはありません。
  • ループ不変式では、のためにk != i = j、追加する方法はありませんA[k]いずれにもA[k']k != k'取得するには、b。元の配列の要素と合計できない可能性のある要素をインクリメントiおよびデクリメントすることによってのみスローしました。jb

私が微妙なことを見逃し続けているなら、遠慮なく助けてください。


私はあなたの証明が正しいと思います(しかし、私はそれをすくい取っただけであることに注意してください)。どうやってそれに思いついたのですか?これは学習課題なので、問題を解決する方法をどのように考案したかを示していただければ幸いです。
Gilles「SO-悪をやめなさい」

3

有効なループの不変条件は常に無限にあります。秘訣は、アルゴリズムについて証明したいことを証明するのに十分であり、証明できることを見つけることです(通常、ループの反復回数の帰納によって)。

このアルゴリズムの正確性を証明するには、3つの部分があります。

  • アルゴリズムが誤ったステップを実行することはありません。ここで、潜在的な不正な手順は、配列の境界外の配列要素にアクセスすることです。
  • アルゴリズムがYESorを返す場合NO、この出力は正しいです。
  • アルゴリズムは入力ごとに終了します。

正確さのために、あなたはそれを証明する必要があります 1in そして 1jn。これはあなたの不変条件の一部である方がいいです。ループ条件を考えるi<j、これを凝縮して 1i<jnループ本体への入り口。ループテストが最後に到達した場合、この条件は当てはまりませんが、次のことに気付くと役立つ場合があります。ij (ループ本体の内部で、 i<ji そして j 変更するだけ 1、これは最悪の場合、この厳密な不等式を等式に変える可能性があります)。

ときにreturn YES実行され、A[i]+A[j]=b明らかです。したがって、この部分は証明するために特別なものを必要としません。

最後のreturn NOステートメントが実行されると、ループは正常に終了した(つまりi<j は偽です)、あなたはそれを証明する必要があります i,j,A[i]+A[j]b。このプロパティは明らかに一般的には当てはまりませんYES。答えがである場合、このプロパティは成り立ちません。あなたは強化する必要がありますこの特性あります。一般化する必要がある特別なケースがあります。これは、既にトラバースされている配列の一部にのみ適用されるプロパティの典型的なケースです。ループは、(x,y) 以前の値です (i,j) (すなわち 1xi そして jyn そして (x,y)(i,j))その後 A[x]+A[y]b。これは、ループ不変式で表現した方がよいでしょう。

私たちはそこで終わりましたか?結構です。通常のループ終端でわかっているのは、xi,yj,A[x]+A[y]b。もしあったらx>i そして yj、または xi そして y>j:できますか A[x]+A[y]b?それ以上の情報なしではそれを言うのは難しいです。実際、次のような場合を区別したほうがよいでしょう。A[x]+A[y]>b と場合 A[x]+A[y]<b。これらのプロパティを使用すると、配列がソートされているという事実を使用して、配列内の他の位置に関する事実を推測できます。だけでb、私たちが取り組むことは何もありません。どっちかわからないA[x]+A[y] いくつかのランダムのためにあります x<i そして y>j; しかし、境界で何が起こるかはわかっています。i 増加します、それは A[i]+A[j] 小さすぎる、と j 減少している、それは A[i]+A[j]大きすぎます。どのループ不変式がこれを表現できるか考えてください。以下に可能性をあげます。

このプロパティは、必要な条件を直接提供しないことに注意してください return NOステートメントにください。ループの最後の実行で何が起こったのかを調べる必要があります。または、いつかを詳しく調べるより強いループ不変式を証明する必要があります。A[x]+A[y]<b そしていつ A[x]+A[y]>b

最後に、終了のために、あなたは関係する必要があります i そして jループの反復回数。ここでは、これは単純です。i または j 各ターンで移動するので、 ji 減少する 1各ループ反復で; これを証明するためにループ不変式を使用する必要はありません。

次の不変式を取得しました。

1ijn(x<i,y<j,A[x]+A[y]b)(x<i,A[x]+A[j]<b)(y>j,A[i]+A[y]>b)

そして、不変条件を満たした後、あなたはその部分を見逃しています。そして、あなたが言うように、「Aソートされた」不変の一部である必要があります。
ラファエル

ああ、そして小さなバグ: i<jは不変ではありません。
ラファエル

1
@Raphael良い点 i<j。それを帰納法で実際に実行する必要はありませんが、方法論のポイントとして、それは重要です。私はそれを考慮しますAループ中に変更されない(したがって、ソートされたままですが、変更されないという事実も重要です)ここで明らかです(技術的にはそれも証明する必要がありますが、個別に実行して取得します)最初に邪魔にならないようにします)。不変条件を設定した後に何が起こるかは問題の一部ではありません。それはかなり機械的に行うことができる運動の一部です。
Gilles「SO-邪悪なことをやめよ」

その後に続く部分は、不変条件が十分である理由を示しています。しかし、その後、あなた(の一種)はあなたの派生でそれをカバーしました。
ラファエル

2

有用な不変条件を導出するための絶対的な方法はありません。問題は計算できないことに注意してください(そうしないと、停止の問題を解決できます)。したがって、経験に基づいて訓練されたヒューリスティックを使用して、試行錯誤を繰り返します。模範的な思考プロセスについては、Gillesの回答を参照してください。一般に、最初に必要なポスト条件(つまり、出力仕様)が必要であり、それを意味する不変条件を(否定されたループ条件と共に)見つけます。

これが私がうまくいくと思うものです:

I= A is sorted and has not been changed 1i,jn ji i<i. j>j. A[i]+A[j]b i<i. b>A[i]+A[j] j>j. b<A[i]+A[j]

後者の3つの句が実際にループによって維持されていることは-YESで終了しないのは当然のことですが-かなり相互依存しており、 i,j 役割を切り替えることができます。

最後に、あなたは得る i=j これにより、最後の2つの句を、最後の要素が A[i] 合計しない b他の要素と。左からの要素と右からの要素の組み合わせは、3項で除外されています。最後に、左の2つの要素i 合計することはできません b 第4節と第1節のため、そして同様に右の2つの要素について

証明を行う場合は、Hoareロジックのルールに厳密に従ってください。


私があなたの答えで見逃しているのは、この不変条件をどのように決定したかです。(つまり、あなたはそれを示さなかった そして j常に配列の境界内にありますが、私はソフトウェアの正しさを知っています。)
Gilles 'SO- stop be evil'

インデックスについての良い点。このような条件は、事後条件を示すために必要ではなく、そこに到達したことを示すためにのみ必要なため、しばしば忘れられます。アレイアクセスを保護するアレイアクセスのHoareルールが必要です。{1iA.lengthP(X)}A[i]=X;{P(A[i])}, forcing you to keep track of indices.
Raphael
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.