ループインバリアントとは何ですか?


268

CLRSの「Introduction to Algorithm」を読んでいます。第2章では、著者は「ループ不変量」に言及しています。ループインバリアントとは何ですか?


4
これは説明に非常に優れているようです:cs.miami.edu/~burt/learning/Math120.1/Notes/LoopInvar.html
Tom


誰かがループ不変の概念に基づいて実際のアルゴリズムのコーディング問題を解決したい場合に備えて、HackerRankでこの問題を参照しください。彼らはまた、概念を詳述するためだけに挿入ソート問題を言及しました。
RBT 2018

理論的な理解のために、ここの注記を参照することもできます。
RBT 2018

回答:


345

簡単に言うと、ループ不変量とは、ループのすべての反復で成立する述語(条件)です。たとえばfor、次のような単純なループを見てみましょう。

int j = 9;
for(int i=0; i<10; i++)  
  j--;

この例では、(すべての反復で)trueですi + j == 9。また、真の弱い不変条件は、 i >= 0 && i <= 10です。


29
これは優れた例です。インストラクターがループ不変量について説明していると何度も聞いたことがありますが、それは単に「ループ条件」またはそれに類似したものでした。あなたの例は、不変条件がはるかに大きくなる可能性があることを示しています。
ブライアンS

77
ループの不変条件がループの目標であるはずなので、これは良い例ではありません... CLRSはこれを使用して、ソートアルゴリズムの正確さを証明します。挿入ソートの場合、ループがiで反復しているとすると、各ループの終わりに、配列はi番目の要素まで順序付けられます。
衝突

5
ええ、この例は間違っていませんが、十分ではありません。@Clashをバックアップします。ループの不変条件は、それ自体だけでなく、目標を提示する必要があるためです。
ジャック

7
@Tomas Petricek-ループが終了すると、i = 10およびj = -1; したがって、あなたが与えたより弱い不変の例は正しくない可能性があります(?)
Raja

7
上記のコメントには同意しますが、ここでは目標が定義されていないため、この回答に賛成しました。収まる目標を定義します。例はすばらしいものです。
2013年

119

私はこの非常に単純な定義が好きです:(ソース

ループ不変量は、[プログラム変数の中で]条件であり、ループの各反復の直前と直後に必ずtrueになります。(これは、反復の途中でその真実または虚偽について何も言わないことに注意してください。)

それ自体では、ループ不変量はあまり機能しません。ただし、適切な不変条件が与えられれば、アルゴリズムの正確性を証明するために使用できます。CLRSの簡単な例は、おそらくソートに関係しています。たとえば、ループ不変を次のようにすると、ループの開始時に、iこの配列の最初のエントリがソートされます。これが実際にループ不変であることを証明できる場合(つまり、ループのすべての反復の前後で保持される場合)、これを使用して、ソートアルゴリズムの正確さを証明できます。ループの終了時でも、ループ不変は満たされます。 、そしてカウンターiは配列の長さです。したがって、最初のiエントリがソートされるとは、配列全体がソートされることを意味します。

さらに簡単な例:ループ不変式、正当性、プログラム導出

ループ不変式を理解する方法は、プログラムについて推論するための体系的で正式なツールとしてです。真であることを証明することに焦点を当てた単一のステートメントを作成し、それをループ不変と呼びます。これでロジックが整理されます。アルゴリズムの正確性について非公式に議論することもできますが、ループ不変式を使用すると、慎重に考える必要があり、推論が気密になります。


10
「各反復の直後」には、ループの終了方法に関係なく、ループの終了後も含まれることを指摘しておく必要があります。
ロバートS.バーンズ

この回答を本当にありがとう!それからの最大の要点は、このループを不変にする目的は、アルゴリズムの正確さを証明するのを助けることです。他の答えは、ループ不変のものに焦点を当てるだけです!
Neekey

39

ループや不変式を処理するときに、多くの人がすぐに気付かないことが1つあります。ループ不変条件と条件付きループ(ループの終了を制御する条件)の間で混乱します。

人々が指摘するように、ループ不変量は真でなければなりません

  1. ループが始まる前
  2. ループの各反復の前
  3. ループが終了した後

(ただし、ループの本体で一時的にfalseになる場合があります)。 一方、ループ 終了後、ループ条件 falseでなければなりません。そうでない場合、ループは決して終了しません。

したがって、ループ不変条件とループ条件異なる条件でなければなりません

複雑なループ不変の良い例は、二分探索です。

bsearch(type A[], type a) {
start = 1, end = length(A)

    while ( start <= end ) {
        mid = floor(start + end / 2)

        if ( A[mid] == a ) return mid
        if ( A[mid] > a ) end = mid - 1
        if ( A[mid] < a ) start = mid + 1

    }
    return -1

}

したがって、ループの条件非常に単純明快です。開始>終了すると、ループが終了します。しかし、なぜループが正しいのですか?それが正しいことを証明するループ不変量は何ですか?

不変式は論理ステートメントです。

if ( A[mid] == a ) then ( start <= mid <= end )

このステートメントは論理的なトートロジーです。これは、証明しようとしている特定のループ/アルゴリズムのコンテキストでは常に真です。また、終了後のループの正確さに関する有用な情報を提供します。

配列内の要素が見つかったために戻った場合、ステートメントは明らかに真です。A[mid] == aそれaは、配列内にある場合、mid開始と終了の間でなければならないためです。そして、ループ終了した場合ので、start > endそのようなことを何も多数あり得ないstart <= mid mid <= end、したがって、我々は文があることを知っているA[mid] == a偽でなければなりません。ただし、結果として、全体的な論理ステートメントは依然としてnullの意味で真です。(論理的には、if(false)then(something)は常にtrueです。)

では、ループが終了したときに、ループ条件が必ずfalseになるということについてはどうでしょうか。要素が配列内で見つかると、ループが終了するとループ条件が真になります!?実際にはそうではありません。暗黙のループ条件は実際にはそうですwhile ( A[mid] != a && start <= end )が、最初の部分が暗黙であるため、実際のテストを短くしています。この条件は、ループの終了方法に関係なく、ループの後は明らかに偽です。


論理ステートメントをループ不変として使用することは重要です。なぜなら、すべての論理ステートメントは、どのような条件であっても常に真になる可能性があるためです。
2013年

保証はありませんので、それほど奇妙な私は、考えるべきaですA。非公式には、「キーaが配列に存在する場合、それはその間startで発生する必要がありますend」です。場合は、その次のA[start..end]空である、すなわち、aAに存在しない
scanny

33

以前の回答では、ループ不変式を非常に良い方法で定義しています。

以下は、CLRSの作成者がループ不変式を使用して挿入ソートの正確さを証明した方法です。

挿入ソートアルゴリズム(Bookで指定):

INSERTION-SORT(A)
    for j ← 2 to length[A]
        do key ← A[j]
        // Insert A[j] into the sorted sequence A[1..j-1].
        i ← j - 1
        while i > 0 and A[i] > key
            do A[i + 1] ← A[i]
            i ← i - 1
        A[i + 1] ← key

この場合、ループ不変: サブ配列[1からj-1]は常にソートされます。

これをチェックして、アルゴリズムが正しいことを証明しましょう。

初期化:最初の反復の前にj = 2。したがって、サブアレイ[1:1]はテストされるアレイです。要素が1つしかないため、ソートされます。したがって、不変条件は満たされます。

メンテナンス:これは、各反復後に不変条件をチェックすることで簡単に確認できます。この場合は満足です。

終了これは、アルゴリズムの正確性を証明するステップです。

ループが終了すると、j = n + 1の値になります。ここでもループ不変条件が満たされています。つまり、Sub-array [1 to n]をソートする必要があります。

これは、アルゴリズムで実行したいことです。したがって、アルゴリズムは正しいです。


1
同意します...終了ステートメントはここでとても重要です。
Gaurav Aradhye 2015

18

すべての良い答えに加えて、Jeff EdmondsによるHow to Think About Algorithmsの優れた例 は、コンセプトを非常によく説明できると思います。

例1.2.1「Find-Max 2本指アルゴリズム」

1)仕様:入力インスタンスは、要素のリストL(1..n)で構成されます。出力は、L(i)が最大値を持つようなインデックスiで構成されます。この同じ値のエントリが複数ある場合は、それらのいずれか1つが返されます。

2)基本的な手順:2本指の方法を決定します。右の指がリストを実行します。

3)進行状況の測定:進行状況の測定は、右指がリストに沿ってどれだけ離れているかです。

4)ループ不変:ループ不変は、左指が右指がこれまでに遭遇した最大のエントリの1つを指していることを示します。

5)主な手順:反復ごとに、右指をリストの1つのエントリの下に移動します。右の指が左の指のエントリよりも大きいエントリを指している場合は、左の指を右の指と同じ位置に移動します。

6)進行する:右の指が1つのエントリを移動するため、進行します。

7)ループ不変量の維持:ループ不変量は次のように維持されています。各ステップで、新しい左の指の要素はMax(古い左の指の要素、新しい要素)です。ループ不変により、これはMax(Max(shorter list)、new element)です。数学的には、これはMax(長いリスト)です。

8)ループ不変式の確立最初に、両方の指を最初の要素に向けてループ不変式を確立します。

9)終了条件:右指がリストの移動を終了すると、終了です。

10)終了:最終的に、問題は次のように解決されます。終了条件では、右の指がすべてのエントリに遭遇しました。ループ不変により、左指はこれらの最大値を指します。このエントリを返します。

11)終了および実行時間:必要な時間は、リストの長さの定数倍です。

12)特殊なケース:同じ値のエントリが複数ある場合、またはn = 0またはn = 1の場合にどうなるかを確認します。

13)コーディングと実装の詳細:...

14)正式な証明:アルゴリズムの正確さは、上記の手順に従います。


この答えは、不変式の直観的な要点に本当に「当てはまる」と思います。
スキャンニー

6

ループの不変式は、各反復の開始時とループの終了時に真でなければならない変数間の重要な関係を表すアサーションと見なされると、反復アルゴリズムの設計に役立ちます。これが当てはまる場合、計算は有効性に向かっています。falseの場合、アルゴリズムは失敗しています。


5

この場合の不変とは、すべてのループ反復の特定の時点で真でなければならない条件を意味します。

コントラクトプログラミングでは、不変条件は、パブリックメソッドが呼び出される前後に(コントラクトによって)trueでなければならない条件です。


4

不変の意味は決して変わらない

ここで、ループ不変とは、「ループ内の変数に発生する変化(増分または減分)がループ条件を変更していない、つまり条件が満たされている」ことを意味するため、ループ不変の概念が生まれました。


2

ループ不変プロパティは、ループ実行のすべてのステップ(つまり、ループ、whileループなど)を保持する条件です。

これは、ループ不変式の証明に不可欠であり、実行の各ステップでこのループ不変式のプロパティが保持されている場合に、アルゴリズムが正しく実行されることを示すことができます。

アルゴリズムが正しいためには、ループ不変式は次の条件を満たしている必要があります。

初期化(初め)

メンテナンス(後の各ステップ)

終了終了時)

これは、一連のものを評価するために使用されますが、最も良い例は、重み付きグラフトラバーサルの貪欲なアルゴリズムです。貪欲なアルゴリズムが最適なソリューション(グラフ全体のパス)を生成するには、可能な限り最小の重みのパスにあるすべてのノードに接続する必要があります。

したがって、ループ不変のプロパティは、経路の重みが最小であることです。始まる、私たちはすべてのエッジを追加していないので、このプロパティには、(それが、この場合には、偽ではない)が真です。で、各ステップは、我々はそうもう一度、我々は最低重量パスを取っている、最低重量エッジ(貪欲ステップ)に従います。終わり、我々は最低の加重パスを発見したので、私たちの財産も同様です。

アルゴリズムがこれを行わない場合、最適ではないことを証明できます。


1

ループで何が起こっているかを追跡することは困難です。目的の動作を達成せずに終了または終了しないループは、コンピュータープログラミングの一般的な問題です。ループ不変式が役立ちます。ループ不変式は、プログラム内の変数間の関係についての正式なステートメントであり、ループが実行される直前に不変であり(不変条件を確立)、ループを通過するたびに(不変条件を維持し)ループの最下部で再び真になります。 )。以下は、コードでループ不変式を使用する一般的なパターンです。

... //ループ不変式はここで真でなければならない
(TEST CONDITION){
//ループの先頭
...
// ループの最下部
//ここでループ不変式はtrueでなければなりません
}
//終了+ループ不変式=目標
...
ループの上部と下部の間で、ループの目標に到達するための前進が行われていると考えられます。これは不変式を乱す(falseにする)場合があります。ループ不変量のポイントは、ループ本体を毎回繰り返す前に不変量が復元されるという約束です。これには2つの利点があります。

作業は、複雑でデータに依存する方法で次のパスに繰り越されることはありません。各パスは他のループとは独立してループを通過します。不変条件はパスを結合して作業全体に結合します。ループが機能する理由は、ループを通過するたびにループ不変式が復元されるという理由に限定されます。これにより、ループの複雑な全体的な動作が小さな単純なステップに分割されます。各ステップは個別に考えることができます。ループのテスト条件は不変条件の一部ではありません。それがループを終了させるものです。ループが終了する理由と、ループが終了したときにループが目的を達成する理由の2つを個別に検討します。ループが終了するたびに、ループを通過するたびにループが終了します。これを保証することはしばしば簡単です:例えば 一定の上限に達するまで、カウンター変数を1つずつ増やします。場合によっては、終了の理由がより難しいことがあります。

ループの不変条件は、終了の条件が達成され、不変条件がtrueの場合に目標が達成されるように作成する必要があります。

インバリアント+​​終了=>目標
終了以外のすべての目標達成を捕捉する単純で関連性のあるインバリアントを作成するには、練習が必要です。数学的な記号を使用してループ不変条件を表現するのが最善ですが、これが過度に複雑な状況につながる場合は、明確な散文と常識に依存します。


1

コメント権限がありません。

@Tomas Petricekあなたが言ったように

また、trueである弱い不変条件は、i> = 0 && i <10です(これは継続条件であるためです)。 "

どのようにループ不変ですか?

[1]を理解している限り、私は間違っていないといいのですが、ループの不変式はループの開始(初期化)でtrueになり、各反復(メンテナンス)の前後でtrueになり、その後もtrueになりますループの終了(Termination)。しかし、最後の反復の後、iは10になります。したがって、条件i> = 0 && i <10はfalseになり、ループを終了します。ループ不変式の3番目のプロパティ(終了)に違反しています。

[1] http://www.win.tue.nl/~kbuchin/teaching/JBP030/notebooks/loop-invariants.html


ループはこれらの条件下では実際には実行されないため、これは正しいと思います。
muiiu 2017

0

ループ不変式は、などの数式(x=y+1)です。その例では、ループ内の2つの変数xy表します。コードの実行を通して、これらの変数の変更の挙動を考えると、すべての可能性をテストすることはほぼ不可能であるxyの値を、彼らはすべてのバグを生み出すかどうかを確認します。x整数としましょう。整数は、メモリ内に32ビットのスペースを保持できます。その数を超えると、バッファオーバーフローが発生します。したがって、コードの実行全体を通して、そのスペースを超えないようにする必要があります。そのためには、変数間の関係を示す一般的な式を理解する必要があります。結局のところ、プログラムの動作を理解しようとするだけです。


0

簡単に言うと、それはすべてのループ反復で真になるLOOP条件です。

for(int i=0; i<10; i++)
{ }

これでは、私は i<10 and i>=0



-1

線形探索(本で与えられた演習に従って)では、与えられた配列で値Vを見つける必要があります。

配列を0 <= k <長さからスキャンし、各要素を比較するだけの簡単な方法です。Vが見つかった場合、またはスキャンが配列の長さに達した場合は、ループを終了します。

上記の問題についての私の理解に従って

ループ不変式(初期化): Vはk-1回の反復で見つかりません。非常に最初の反復、これは-1になるので、位置-1にVが見つからないと言えます

保守: 次の反復では、k-1にないVが成り立つ

終了: kの位置にあるVまたはkが配列の長さに達した場合は、ループを終了します。

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