4

CLRS says that

We must show three things about a loop invariant:

  • Initialization: It is true prior to the first iteration of the loop.
  • Maintenance: If it is true before an iteration of the loop, it remains true before the next iteration.
  • Termination: When the loop terminates, the invariant gives us a useful property that helps show that the algorithm is correct.

My question is can I edit the steps and make them these instead:

  • Initialization: It is true after the first iteration of the loop.
  • Maintenance: If it is true after an iteration of the loop, it remains true after the next iteration.
  • Termination: When the loop terminates, the invariant gives us a useful property that helps show that the algorithm is correct.

Explanation: Basically we use Principle Of Mathematical Induction to prove correctness. Using "Initialization" we prove that the property holds after the 1st iteration. Using "Maintenance" we can show that the property holds for all iterations since "Initialization" and "Maintenance" together create a chain. And hence the property holds after the last iteration too.

Example:

RANDOMIZE-IN-PLACE(A) 
1 n ← length[A] 
2 for i ← to n
3 do swap A[i] ↔ A[RANDOM(i, n)]

For this algorithm, there is already a proof given in the textbook using the standard procedure (I didn't include it here since it is too long).

My suggestion:

  • Loop Invariant: Just after the ith iteration of the for loop of lines 2-3, for each possible (i)-permutation, the subarray A[1 .. i] contains this (i)-permutation with probability (n - i)!/n!.
  • Initialization: After 1st iteration A[1] contains this permutation with probability (n-1)!/n!=1/n which is true.
  • Maintenance: Let it be true after (i-1)th iteration. After (i)th iteration, A[1...i] contains this permutation with probability [(n-i+1)!/n!]*[1/(n-i+1)]=(n-i)!/n! which is what we want.
  • Termination: At termination, i = n, and we have that the subarray A[1 .. n] is a given n-permutation with probability (n - n)!/n! = 1/n!. Thus, RANDOMIZE-IN-PLACE produces a uniform random permutation.

Is my explanation logically sound?

Any help would be greatly appreciated. Thanks.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
Garima
  • 53
  • 5
  • 1
    You have to then add one additional step in the proof: that there exists a first iteration. (Some loops don't ever execute the body.) – Ted Hopp Jun 24 '14 at 20:18
  • Yes you're right, thanks :) What about the rest of the proof? – Garima Jun 24 '14 at 20:23
  • The rest of the proof looks okay to me. I'm not sure why the original formulation wouldn't work equally well for you. The initialization step would be trivially satisfied since the subarray of processed elements would be empty. – Ted Hopp Jun 24 '14 at 21:58
  • I'm not sure how you'd make these *probabilistic* loop *invariants* work. I sold my copy of CLRS a while ago, so I can't check, but it sounds a bit fishy. –  Jun 24 '14 at 22:12

1 Answers1

1

Apart from the fact, that you have to make an additional step, which covers the loop never running at all, you can of course also prove that the invariant is true after the first iteration instead of proving that it is true before the first iteration.

However I doubt that this will often make much sense

  • First, as already stated you have already a special case (what happens if the loop isn't executed at all) which probably results in a proof, which is similar to the Initialization, that you wanted to skip in the first place
  • Secondly, the proof that the invariant is true after the first iteration, is very likely to be very similar to the Maintanance proof, so you are likely to write two very similar proofs, just to skip the Initialization, which is likely to be quite an easy proof.

Analogy

Although this is not directly related, this question sounds a lot like trying to optimize the following (pseudo)code:

int product(int[] values)
    product = 1
    for i = 0 to values.size - 1
        product *= values[i]
    return product

To this:

int product(int[] values)
    product = values[0] // to make the code quicker, start with the first item already
    for i = 1 to values.size - 1 // start the loop at the second item
        product *= values[i]
    return product

just, that you now have to include the additional check, whether the values array is empty (which I did not in the above code)

Misch
  • 10,350
  • 4
  • 35
  • 49