We know that a loop variant is defined as a statement that is true before and after every iteration of the loop. But is that definition too loose? Let us look at a concrete example: linear search.
Input: A sequence of n numbers A = (a1, a2, a3, ..., an) and a value v
Output: An index i such that v = A[i] or NIL if v is not found in A
Here is my pseudocode:
linear_search(A, v)
1 for i ∈ {1, 2, ..., n}
2 if A[i] = v
3 return i
4 return NIL
So a typical loop invariant would be that (since we are searching from left to right) v is not on the left side of the current index, or mathematically, P: ∀p ∈ {1, 2, ..., i - 1}, A[p] ≠ v.
This is clearly true, even at the start because p ∈ Ø is false, which makes P true, remembering that every universally quantified statement can be thought of as a conditional statement. (But it is easier to think of it informally: at the start, there is nothing on the left side of v.)
We could also use a conditional statement that is less restricting. In this case, Q: If A[i] = v, then 1 ≤ i ≤ n.
Obviously, this is true if v is found. If v is not found, A[i] = v is false and Q becomes true regardless of the value of i. Q is also true if we change our algorithm to search from right to left.
Maybe we want to be less restricting than that. What about using a statement that is always true? R: Either A[i] = v or A[i] ≠ v.
R holds before and after every iteration of the loop.
Which of the statements P, Q and R are valid to use as a loop invariant?