ループ不変条件については少し経験がありますが、はっきりとはわかりません。私はそれらをPythonの例で学習しようとしています。誰かが指摘したり、理解を助けたりできますか?
私は、programmers.SXとWebの両方を検索しましたが、見つけることができる唯一のものは、不変条件と契約による設計でした。ループ不変条件では何もありませんでした。
ループ不変量は、ループのすべての反復で真になる単純なものです。たとえば、非常に簡単なwhile
ループを見てみましょう。
while x <= 5:
x = x + 1
ここでは、ループ不変式はx ≤ 6
になります。明らかに、実際には、ループ不変量はより複雑になります-一般的にループ不変量を見つけることは芸術の一種であり、アルゴリズム的に(私が知る限り)簡単に行うことはできません。
では、なぜこれが便利なのでしょうか。さて、おおまかなレベルでは、デバッグに適しています。重要な不変条件を特定した場合、コードを変更した場合でも、それが保持されていることを簡単に確認できます。あなたはちょうどある種のassertステートメントを追加することができます:
while x <= 5:
x = x + 1
assert x <= 6
より具体的には、これらの不変条件は、ループの動作を推論するのに役立ちます。これは、公理セマンティクスとHoareロジックが出てくるところです(回答のこの部分は少し高度で難解なので、あまり心配しないでください)。意味する」、,は「および」を意味し、¬は「しない」を意味します。
基本的な考え方は、コードのプロパティを証明する体系的な方法が欲しいということです。これに取り組む方法は、コードの前提条件と事後条件を調べることです。つまり、ある条件A
がbeforeを保持する場合にコードを実行し、いくつかの条件B
が保持する場合afterを実行することを証明したいそれ。通常、次のように書きます。
{A} code {B}
一般に、これはかなり単純です。 {x = 0} x = x + 1 {x = 1}
のようなものを証明する方法を直感的に理解できます。これを行うには、事後条件のx
をx + 1
に置き換え、x = 0 ⇒ x + 1 = 1
の論理式が明らかにtrueになるようにします。これが一般的な割り当ての扱い方です。事後条件の変数を新しい値に置き換えるだけです。
行内の複数のステートメントやifステートメントも非常に直感的であるような他の構成体。
しかし、どのようにforループを行うのですか?与えられたループが何回繰り返されるかは(一般的に)わからないので、これは難しい質問です。これがループ不変式の出番です。次のようなループを見ています。
while cond: code
ここには2つの可能性があります。 cond
がFalse
の場合、それは取るに足らないことです。ループは何も実行しないため、A ⇒ B
を取得します。しかし、ループが実際に実行されるとどうなりますか?ここで不変式が必要です。
不変式の背後にある考え方は、常にループ内で保持されるということです。 whileループの内側では、cond
は常にtrueです。したがって、次のようなアサーションを取得します。
{A ∧ cond} code {A}
これは、必要なものを正式に書き出すだけです。A
(ループ不変)とcond
は、ループ本体の最初に保持され、A
は最後に保持する必要があります。ループ本体でこれを証明できれば、ループが何回実行されてもA
が保持されることがわかります。したがって、上記の説明が当てはまる場合、次のように推測できます。
{A} while cond: code {A}
追加のボーナスとして、while
ループが終了したばかりなので、cond
がfalseである必要があります。したがって、結果全体を実際に次のように書き出すことができます。
{A} while cond: code {A ∧ ¬cond}
これらのルールを使用して、上記の例について何かを証明しましょう。証明したいのは:
{x ≤ 0} while x <= 5: x = x + 1 {x = 6}
つまり、小さなx
で始めると、ループの最後のx
は常に6になることを示したいと思います。これはかなり簡単ですが、良い例です。したがって、最初のステップはループ不変式を見つけることです。この場合、不変条件はx ≤ 6
になります。これが実際にはループ不変であることを示す必要があります。
{x ≤ 5 ∧ x ≤ 6} x = x + 1 {x ≤ 6}
つまり、x
が5以下の場合、x = x + 1
の実行後、x
は6以下になります。上で概説した置換ルールを使用してこれを行うことができますが、それはとにかくかなり明白です。
したがって、これを知っていれば、ループ全体のルールを推測できます。
{x ≤ 6} while x <= 5: x = x + 1 {x ≤ 6 ∧ ¬(x ≤ 5)}
したがって、これはループの終わりにx
が5より大きく6以下であることを示しています。これはx = 6
に簡略化されます。 x ≤ 6
は常にx ≤ 0
以来、最初のステートメントを証明しました。
さて、これは非常に明白な何かを証明するための多くの誇張と思われるかもしれません。結局のところ、このループの最後で、プログラマはx
の値を教えてくれたかもしれません。ただし、重要なアイデアは、この方法はすぐには明らかにならないかもしれないより複雑なループにスケーリングするということです。しかし、そのようなループの不変式を思い付くことができれば、それを使用して、より興味深い特性を証明することができます。
とにかく、それがあまり混乱しないこと、そしてループ不変式がより基本的なレベルで重要である理由についての良い考えがあなたに与えられたことを願っています。
私はここに使用例を含む非常に良い説明を見つけました:
http://www.cs.uofs.edu/~mccloske/courses/cmps144/invariants_lec.html
瓶に赤と青のビー玉を入れた例は、トリックを完全に説明しています。答えがスタックルールに準拠するように要約します(一部は元のコピーと貼り付けの場合があります)。
-瓶があり、そこに一定量の赤または青の大理石(N> = 1)が含まれているとします。
-また、側面に無制限の量のREDビー玉があります。
PROCEDURE:
while (N > 1):
pick any two marbles from the jar
if (marbles have same colour):
remove marbles
put 1 RED marble in the jar
else: // marble have different colour
remove picked RED marble
put picked BLUE marble back
この手順を調べると、Nが各反復で1ずつ減少していることがわかります。したがって、最初にジャーにN個のビー玉が含まれていることがわかっている場合、N-1ループの後は1つしか含まれません。これは、「ループが有限回数の反復後に終了する」という直感的で非公式な引数です。
瓶の中の赤と青のビー玉の量が最初にわかっていると仮定します。手順の最後に、瓶の最後の大理石の色を予測してみましょう。
正式には、関数を見つけようとしています
f: N × N --> {BLUE, RED},
domain: set of ordered pairs of natural numbers,
range: is the set {BLUE, RED})
次の条件を満たす:
For all K and M (such that at least one of them is non-zero),
if we begin with K RED marbles and M BLUE marbles in the jar,
then the last marble remaining in the jar is necessarily of color f(K,M).
この関数を特定する方法は、瓶の中の青いビー玉の量に作用するループの不変条件を最初に見つけることです。
ループの1つの反復と、青のビー玉の数への影響を考慮します。
Case 1: both marbles have same colour:
subcase 1.1: marbles are BLUE (number of BLUE marble decreases by 2)
subcase 1.2: marbles are RED (number of BLUE marble stays the same)
Case 2: marbles have different colour: (number of BLUE marble stays the same)
単一のループの繰り返しが、青のビー玉の数のパリティに影響を与えないことがわかります。 Mは、反復の結果として(開始状態に応じて)奇数または偶数のままになります。
このプロパティは、任意の数の反復に対してtrueを保持します。
Let us denote:
Big_K the number of BLUE marbles in the jar at the beginning
small_k the number of BLUE marbles currently in the jar
then an invariant of the loop is:
small_k is odd if and only if Big_K is odd
This can be also expressed in a different way:
(both Big_K and small_k are odd) or (both Big_K and small_k are even)
瓶の最初のBig_Kにある青いビー玉の数が奇数だったとします。 「ループ不変式はすべての反復の終わりに保持されるため、特に最後の反復の終わりに保持されることを思い出してください。」
次に、jarの最後の大理石は青でなければなりません。それ以外の場合はsmall_k = 0(偶数)であるためです。同様に、Big_Kが偶数の場合、k = 1(奇数)なので、最後の大理石は赤でなければなりません。
関数fは次のとおりです。
f(Big_K,M) = { RED if Big_K is even
{ BLUE if Big_K is odd
すべての功績は、Robert McCloskey http://www.cs.uofs.edu/~mccloske/ がループ不変式に関する非常に役立つ説明を提供しているためです。