Download presentation
Presentation is loading. Please wait.
Published byKaren Nicholson Modified over 6 years ago
1
Some heuristics for deriving invariant LN sections 6.7 , 6.8
2
Overview Examples of “Replace constant with counter” heuristic
Fibbonaci Longest “X” segment Tail invariant Revisiting array reverse GCD Other examples 2
3
Loop Reduction rule [Init] [IC] [EC] [TC1] [TC2]
P I // setting up I {* g /\ I *} S {* I *} // invariance I /\ g Q // exit cond {* I /\ g *} C:=m; S {* m<C *} // m decreasing I /\ g m > // m bounded below {* P *} while g do S {* Q *} 3
4
Heuristic “replace constant with counter”
i := 0 ; r := true ; while i<n do { r := r /\ (a[i]=0) ; i++ } {* r = (k : 0k<n : a[k]=0) *} 1. Indentify the loop counter The loop iterates on the counter up to some upperbound/lowerbound. 2. Identify this upper/lower bound. If the post-cond is of the form Q(n). Try Q(i) /\ ... as then invariant. Notice that: Q(i) /\ i=n Q(n) 3. Takes Q(i) as inv, + a range expr over the counter, e.g. i 4
5
Fibonacci sequence The Fibonacci “sequence” can be generated by: fib 0 = 0 fib 1 = 1 fib (t + 2) = fib (t+1) fib t Nice (but bad execution time)
6
Let’s take a multi-vars example: iterative Fibonacci
Fibonacci’s original problem: rabbit growth problem. If we start if n pairs of rabits, how many rabits we’ll have at the end of the year? Simplified model: Start with one new pair A new pair needs 1 month to become adult Each month each adults pair produce a new pair Our rabbits don’t die :)
7
Iterative Fib O(n) RabitSim (n:int) : int
{ nNow,nAdult,newPairs,t : int ; t:=1 ; nAdult:=0 ; nNow:=1 ; while t < n do { newPairs := nAdult ; nAdult := nNow ; nNow := nNow + newPairs ; t := t + 1 } ; return nNow }
8
Let’s prove that the two verions are the same
t:=1 ; nAdult:=0 ; nNow:=1 ; while t < n do { newPairs := nAdult ; nAdult := nNow ; nNow := nNow + newPairs ; t := t + 1 } ; return := nNow {* I *} Inv : nNow = fib t /\ 1 t n term. metric : n-t {* nNow = fib n *} {* return = fib n *} (note multiple variables to accumulate results)
9
Verifying Invariance /\ nAdult = fib (t-1)
{* nNow = fib t /\ 1 t n /\ t<n *} Not enough info in inv to prove this wp : nNow + nAdult = fib (t+1) /\ t+1 n newPairs := nAdult ; nAdult := nNow ; nNow := nNow + newPairs ; t := t + 1 /\ nNow = fib t /\ nAdult = fib (t-1) the wp of {* nNow = fib t /\ 1 t n *}
10
A different example: converting tail recursions
Typical recursion: g(x) = if x≤ 0 then x else x + g(x-1) Tail recursion: f (x) = if x≤0 then x else f(x-1) f(2) = f(1) = f(0) = 0 g(2) = 2 + g(1) g(0)
11
Examples last s = #s=1 hd s | last (tail s)
lsum (z,s) = s=[] z | lsum (z + hd s, tail s) You can calculate sum via lsum: sum s = lsum (0,s)
12
Tail Recursion, general form
A function F of this general form: F x = g x base x // base case | F ( x) // recursion This is called tail recursion. Can be optimized by: Last recursion returns directly to the first/root call. Use less stack space With iteration cost no stack space for passing params. Terminates if … ? F : AB. Find a term. metric m : AInt such that: g x m ( x) < m x g x m x > 0 12
13
Implementing tail recursive function
F x = g x base x | F ( x) Compute F With this simple loop x := ; while g x do x := x ; r := base x ; Inv: F x = F {* true *} Term. Metric : m x bsearch(x,a,N) = bsworker(x,a,0,N) bsworker (x,a,left,right) = // not optimized ... could break if a[left] = x, but let’s not do that let d = right – left m = left + (right – left) /2 in if d <= 1 then left<right /\ a[left]=x else if x<=a[m-1] then bsworker(x,a,left,m) else bsworker(x,a,m,right) base x = F {* r = F *} 13
14
Example : GCD Given X,Y > 0, compute gcd X Y.
Straight forward solution: O(X min Y). We can do better than that (Euclides, 320 BC) Approach: try to cast the problem into tail recursion. We will need to explore some properties of common divisors.
15
Some properties of gcd We’ll assume positive integers!
Define : d divides x = k : k>0 : x = k*d d comdiv x,y = d divides x /\ d divides y Theorem : \\ hmm … looks like a “split” If x>y then d comdiv x,y d comdiv (x-y),y if d comdiv x,y then: (1) x = k1*d, for some k1>0 (2) y = k2*d, for some k2>0 If y>x, then x-y = (k1 – k2)*d ... so d is also a divisor of x-y, so d is comdiv of x-y as well. 15
16
Some properties of gcd Define gcd via this equation : = gcd x y = comdiv x,y /\ (e : e comdiv x,y : e ) Theorem : if x>y then gcd x y = gcd (x-y) y Theorem : gcd x x = x 16
17
Gcd in tail recursion So we can infer this recursive relation for gcd : gcd x y = x = y x | x > y gcd (x-y) y | x < y gcd x (y – x) Implemented by this loop: x,y := X,Y ; while x y do if x>y then x:=x-y else y:=y-x inv : gcd x y = gcd X Y {* X>0 /\ Y>0 *} term. metric : x+y {* x = gcd X Y *} 17
18
Another example Consider an array a[0..n) of non-negative integers representing a number. Write a program that computes the “value” of the number represented by such an array. a [0..3) 3 1 2 value a 3 = 312 value
19
Possible solutions: Iterative: :i=0 ; r:=0 ; while i<n do {
Recursive: Both are less efficient in computing 10^... :i=0 ; r:=0 ; while i<n do { r := r + a[i] * 10^(n-i) i++ } value [] = 0 value (x:s) = x*10^(length s) + value s
20
Tail recursive definition of “value”
Or, using tail recursion (more efficient): val x s = s=[] x | /* else */ val (10*x + hd s) (tail s) Note that value s = val 0 s Now you can convert it to an iterative solution
21
Implementation Recall:
val x s = s=[] x | /* else */ val (10*x + hd s) (tail s) Inv: val x s = val 0 (a[0..n)) /\ 0 i n {* 0≤n *} x , s := 0 , a[0..n) ; while s[] do { x := 10*x + hd s ; s := tail s } result = x You can further refine this to get rid of the lists. {* result = val 0 (a[0..n)) *} 21
22
Data refinement inv: {* ... /\ s = a[i..n) *}
x , s := 0 , a[0..n) ; while s[] do { x := 10*x + hd s ; s := tail s } result = x i = 0 Introduce new variables Add invariant to express their intention Add statements in order to maintain the new inv. Use the new vars to optimize Drop what you don’t need Repeat i < n a[i] i++ {* result = val 0 (a[0..n)) *} 22
23
Oh, note that foldl is tail recursive...
Well, foldl is tail recursive: Roll out your implementation... Compare it with : foldl f e [] = e foldl f e (x:s) = foldl f (f e x) s F(z) = F(z) SUM [] = 0 SUM (x:s) = x + SUM s This is not TR
24
Tail invariant Previously,our invariants express “what has been computed” : nNow = fib t s = SUM (a[0..i)) Tail invariant has this forms : still to-do = goal The loop shrinks the “todo” part, so that in the end we have: remainder = goal If remainder can be easily calculated and the result is put in a variable r, then in r we would have goal.
25
Tail invariant Or more generally, this form : x todo = goal When the loop terminates, we again hope that x todo can be easily calculated, and hence we get the goal. An instance of the above scheme is to reduce the todo part to the unit of , so that in the end we have: x = goal
26
A bit more complicated: Longest Zero Segment
Given an array a (of lenght n), find the longest segment in a containing of only 0’s. We’ll simplify this a bit: just find the length of that longest segment. Naive solution is O(n2); but we can also do it in O(n). 1 3 2
27
Analogous problems Longest “flat” segments Longest increasing segments
Longest segment satisfying predicate X : [T] bool It will be important that X has a “nice” split property, e.g. : X (x:s) = x X s // for some 27
28
Some helping concepts TAILS gives all tails of an array : TAILS a i = [ a[p i) | p from [ i] ] SEGS gives the segments; we’ll define it in terms of TAILS: SEGS a = [[]] SEGS a (i+1) = SEGS a i TAILS a (i+1)
29
Specification of “Longest Zero Segment”
This gives the length of the logest zero-segment: LZS a i = MAX [# s | s from SEGS a i, ALLZ s ] ALLZ s = … // list s consists of only zeros # s // length of s (COUNT s) We’ll need a split theorem a[0..i+1) a[0..i) 1 3 2 a[i] LZS a (i+1) = LZS a i max LZTail a (i+1)
30
LZS Split The LZS split theorem: // require i0 LZS a (i+1) = LZS a i max LZTail a (i+1) Longest “zero tail” : LZTail a i = MAX [ # t | t from TAILS a i, ALLZ t ]
31
Specifying and Deriving the program
Inv: lz = LZS a i /\ i n term. metric : n - i <initialization> while i<n do { <some statement> ; i := i+1 } {* lz = LZS a n *}
32
Deriving the body {* lz = LZS a i /\ 0in /\ i<n *}
{* lz max LZTail a(i+1) = LZS a i max LZTail a(i+1) *} // wp lz := lz max LZTail a (i+1) // intro solution {* lz = LZS a i max LZTail a(i+1) *} // split {* lz = LZS a (i+1) /\ 0i+1n *} // wp i := i+1 {* lz = LZS a i /\ 0in *}
33
So we obtain … Inv: lz = LZS a i /\ 0 i n {* n0 *}
/\ lt = LZTail a i Inv: lz = LZS a i /\ i n {* n0 *} <initialization> while i<n do { lz := lz max LZTail a (i+1) ; i := i+1 } How to compute this efficiently?? Idea: compute it side by side. Extend inv to capture this. {* lz = LZS a n *}
34
Splitting LZTail Split
The LZTail split theorem: // require i0 LZTail a (i+1) = a[i]0 | (LZTail a i) + 1 a[0..i+1) a[0..i) 1 3 2 a[i] LZTail a (i+1) = a[i] 0 | …
35
Continue… {* lz = LZS a i /\ lt = LZTail a i /\ 0 i n /\ i<n *}
{* a[i]0 0 | lt = a[i]0 0 | LZTail a i + 1 *} lt := a[i]0 0 | lt + 1 {* lt = a[i]0 0 | LZTail a i + 1 *} // split {* lt = LZTail a (i+1) *} lz := lz max LZTail a (i+1) ; i := i+1 replace this with “lt” {* lz = LZS a i /\ lt = LZTail a i /\ … *}
36
Finishing it i:=0 ; lz,lt := 0,0 {* n0 *}
Inv : lz = LZS a i /\ lt = LZTail a i /\ i n <initialization> while i<n do { if a[i]0 then lt:=0 else lt:=lt+1 ; lz := lz max lt i := i+1 } {* lz = LZS a n *}
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.