Example: systematic construction of an integer square root program

advertisement
Example: systematic construction of an integer square
root program
W. Drabent
May 2008
Version 3
We want to construct a program computing the integer square root of a
non-negative integer n. In other words, the program has to be totally correct
w.r.t. precondition P = n ≥ 0 and postcondition Q = (x2 ≤ n < (x + 1)2 ).
(The latter also says that x will contain the result). We present the construction with all the details.
My intuition tells me that we should try to use a loop. So a skeleton of
our program is
{ P } ?? ; while b do ?? { ⇓ Q }.
(I will not write ⇓ below, but remember – we are dealing with total correctness).
The most important thing in a loop is its invariant. In the previous
example we constructed the invariant by weakening the postcondition: we
removed n < (x + 1)2 from Q. It should be easy to obtain the invariant
from the precondition and the postcondition from the invariant. Here we
introduce a new variable y and take
I = ( x2 ≤ n < y 2 ∧ 0 ≤ x < y, )
as the invariant.1 The intuition behind I is that the number we are going
to compute is somewhere between x and y.
It is good to remember the proof rule for while which we use:
{b ∧ I ∧ t=z} S {⇓I ∧ t<z}
{ I } while b do S { ⇓ ¬b ∧ I }
if I ⇒ t ≥ 0
(where t – the bound function – is an integer expression and z is a variable
that does not appear in P, b, t or S).
1
The details of constructing the invariant may be explained as follows. First notice
that Q ≡ ∃y (x2 ≤ n < y 2 ∧ y = x + 1). Now remove the quantifier; this introduces y as a
new free variable. The obtained formula is Q1 = (x2 ≤ n < y 2 ∧ y = x + 1). It is stronger
than Q (i.e. Q1 implies Q). The invariant I is obtained by weakening Q1 .
If we had only removed y = x + 1 from Q then the resulting formula would have allowed
negative values for x, y. To avoid this, we put 0 ≤ x < y.
Notice that indeed Q1 implies I, as y = x + 1 implies x < y and Q1 implies 0 ≤ x.
(Assume 0 > x; then x + 1 = y ≤ 0 and x2 > y 2 , hence Q1 does not hold.)
1
Q, I and the proof rule for while suggest the choice of b, as it is necessary
that ¬b ∧ I implies Q. We achieve this by taking
b = (y 6= x+1).
P does non imply I, so the loop requires initialization. We are looking
for a statement S0 satisfying { P } S0 { I }. It is easy to check that S0 =
(x := 0; y := n+1) does the work. (What is wrong with n instead of n + 1?)
This is the current version of our program
(1)
{ P }(x := 0; y := n+1); { I } while b do
({ b| ∧ I {z
∧ t = z} } S { I ∧ t < z }) { Q }.
P0
Now we are looking for a suitable bound function t. It should be ≥ 0 and
it should decrease at each repetition of the loop. Our intention is that the
loop shrinks the interval [x..y) containing the solution until the solution is
found. So a good candidate for t is the size of this interval:
t = y − x.
Note that the invariant indeed implies t ≥ 0.
It remains to construct the loop body S satisfying the stated pre- and
postcondition. S has to decrease t. The future solution is somewhere between
x and y. We divide the interval [x..y) into two parts [x..A) and [A..y) (where
x < A < y), and choose this part that preserves the invariant (in other words,
contains the solution). We do not bother yet about the actual choice of A,
and postpone a proof that such A always exists.
Replacing [x..y) by [x..A) can be done by y := A, assignment x := A
replaces [x..y) by [A..y). As there are two cases, S is likely to be a conditional
statement. Together we have
S = if b0 then y := A else x
| :=
{z A}
| {z }
S2
S1
The postcondition for both S1 and S2 is I ∧ t<z. The axiom [ass] gives2
2
{x
≤ n < A2 ∧ 0 ≤{zx < A ∧ A−x < z} } y := A { I ∧ t<z }
|
(2)
{ A2 ≤ n < y 2 ∧ 0 ≤ A < y ∧ y−A < z } x := A { I ∧ t<z }
(3)
R1
|
{z
}
R2
2
Notice that from t < z we obtained A−x < z and y−A < z. The inequalities say
that both parts [x..A) and [A..y), into which we divide the interval [x..y), are smaller than
[x..y), whose length is z.)
2
We want to apply the rule [if], but this requires the preconditions to be of
the form b0 ∧ R and ¬b0 ∧ R. So we are looking for such conditions implying,
respectively, R1 and R2 . A good candidate for b0 is n < A2 , as n < A2 occurs
in R1 and A2 ≤ n occurs in R2 ). Taking b0 = n < A2 and
R = x2 ≤ n < y 2 ∧ 0 ≤ x < A < y ∧ A−x < z ∧ y−A < z
we see that indeed b0 ∧ R implies R1 and ¬b0 ∧ R implies R2 .
From (2), (3) by rule [cons] we obtain
{ b0 ∧ R } y := A { I ∧ t<z }
(4)
{ ¬b0 ∧ R } x := A { I ∧ t<z }
(5)
and then
{ R } S { I ∧ t<z }
We are almost ready, but the required precondition for S is P0 (see (1)). We
have to show that P0 implies R; then applying [cons] gives
{ P0 } S { I ∧ t<z }
and completes the correctness proof for the program. Also it remains to
decide what is A.
Remember that
P0 = y 6= x+1 ∧ x2 ≤ n < y 2 ∧ 0 ≤ x < y ∧ y−x = z.
Notice first that P0 implies x + 1 < y (we get it from x < y and y 6= x+1).
Hence A, which has to satisfy x < A < y, exists.
Back to the unproven implication P0 ⇒ R. Compare P0 with R. You see
that it remains to show that P0 implies A−x < z ∧ y−A < z. This is easy:
from A < y we have A−x < y−x = z; from x < A we have y−A < y−x = z.
So we constructed a family of programs (parameterized by the choice
of A) and proved their total correctness. Our work can be summarized by
the following proof outline.
3
{n ≥ 0}
x := 0; y := n+1;
{ invariant: I }
{ boundfunction: y − x }
while y 6= x + 1 do ( { b ∧ I ∧ y−x = z }
|
{z
b
}
|
{z
P0
}
{R}
if n < A2 then { n<A2 ∧ R }
{ R1 }
y := A
else { ¬(n<A2 ) ∧ R }
{ R2 }
x := A
{ I ∧ y−x < z } )
{ x2 ≤ n < (x + 1)2 }
Let us choose A. To come to y = x + 1 quickly, it is good to have A close
to the middle of the interval [x..y). So A = (x+y)÷2 is a candidate (where
÷ is the integer division, e.g. 7 ÷ 2 = 3). We have to check3 that P0 implies
x < A < y. We already know that P0 implies x + 1 < y, hence x + 2 ≤ y.
From this we have x + x + 2 ≤ x + y, hence x + 1 ≤ (x + y) ÷ 2 = A; and
x + y ≤ y − 2 + y, hence A = (x + y) ÷ 2 ≤ y − 1. Done.
3
Notice that x < (x+y)÷2 < y does not hold for any x < y, e.g. (3 + 4) ÷ 2 = 3
4
Download