180 likes | 293 Views
Chapter 11. Proof by Induction. Induction and Recursion. Two sides of the same coin. Induction usually starts with small things, and then generalizes to the large. E.g. inductive definition of a list: [] is a list; and if xs is a list, then so is x : xs.
E N D
Chapter 11 Proof by Induction
Induction and Recursion Two sides of the same coin. • Induction usually starts with small things, and then generalizes to the large. • E.g. inductive definition of a list:[] is a list; and if xs is a list, then so is x : xs. • Recursion usually starts with the large thing, working its way back to the small. • E.g. recursion over lists:Start with non-empty list x : xs; do something with x, then recurse on xs. • It is not surprising then, that proof by induction is closely tied to recursion: it is usually needed when proving properties about recursive functions.
Proof by Induction on Lists To prove property P by induction on the length of a list: • Prove P([]) -- the base case. • Assume P(xs) is true –- the induction hypothesisand prove that P(x:xs) is true -– the induction step
Example 1:A Property About foldr Recall this property about foldr: foldr (:) [] xs = xs A proof by induction: Base case: xs = []foldr (:) [] [] [](immediate from the definition of foldr) Induction step: assume foldr (:) [] xs = xs foldr (:) [] (x:xs) x : foldr (:) [] xs -- unfold foldr x : xs -- by the induction hypothesis
Example 2:A Property About length • Prove that:length (xs++ys) = length xs + length ys • Question: Over which list (xs or ys) should we perform the induction? • Hint: look at the definitions of length and (++), which recurse over the front of their arguments – thus we should use xs above.
Example 2, cont’d Base case: xs = [] length ([] ++ ys) length ys 0 + length ys length [] + length ys Induction step: assume length (xs++ys) = length xs + length ys length ((x:xs) ++ ys) length (x : (xs ++ ys)) 1 + length (xs ++ ys) 1 + (length xs + length ys) (1 + length xs) + length ys length (x:xs) + length ys
Proving Function Equivalence • Remember the mantra: “get it right first”. • Later, we can improve our programs for efficiency reasons. • But how do we know that the new program is the same as the old? • Answer: by proving them so. • And in the case of recursive functions, we will need induction.
Example 3:Equivalence of reverse Def’ns Recall these three definitions of reverse: reverse1 [] = [] reverse1 (x:xs) = reverse1 xs ++ [x] reverse2 xs = rev [] xs where rev acc [] = acc rev acc (x:xs) = rev (x:acc) xs reverse3 = foldl (flip (:)) [] reverse1 is the obvious, but inefficient version. reverse2 is the recursive, efficient version. reverse3 is the non-recursive, efficient version.
Example 3, cont’d Let’s prove that reverse1 = reverse3. • Base case: xs = []reverse1 [] [] -- unfold reverse1 foldl (flip (:)) [] [] -- fold foldl reverse3 [] -- fold reverse3 • Induction step: assume reverse1 xs = reverse3 xs reverse1 (x:xs) reverse1 xs ++ [x] -- unfold reverse1 reverse3 xs ++ [x] -- induction hypothesis foldl (flip (:)) [] xs ++ [x] -- unfold reverse3 foldl (flip (:)) [] (x:xs) -- LEMMA!!! reverse3 (x:xs) -- fold reverse3 • The lemma states: foldl (flip (:)) [] xs ++ [x] = foldl (flip (:)) [] (x:xs)How do we prove this?
Generalizing Lemmas • The lemma:foldl (flip (:)) [] xs ++ [x] = foldl (flip (:)) [] (x:xs)is actually hard to prove directly. • It ‘s easier to prove this generalization:foldl (flip (:)) ys xs ++ [y] = foldl (flip (:)) (ys++[y]) xsfrom which the first lemma is easy to prove. • Knowing when to introduce a lemma, and when to generalize it, it is a skill in proving theorems in any context.(See text for proofs of lemmas.)
Laws of Lists • There are many useful properties of functions such as (++), map, foldl, and foldr on lists. • These properties can be used as “lemmas” in proving other properties of larger programs that use these functions. • Some of them depend on function “strictness”: A function f is strict iff _|_ = _|_.
Laws of Map map (\x->x) = \x->x map (f . g) = map f . map g map f . tail = tail . map f map f . reverse = reverse . map f map f . concat = concat . map (map f) map f (xs ++ ys) = map f xs ++ map f ys For all strict f: f . head = head . map f
Laws of fold If op is associative, e `op` x = x, and x `op` e = x, then for all finite xs: foldr op e xs = foldl op e xs If the following are true: x `op1` (y `op2` z) = (x `op1` y) `op2` z x `op1` e = e `op2` x then for all finite xs: foldr op1 e xs = foldl op2 e xs For all finite xs: foldr op e xs = foldl (flip op) e (reverse xs)
Induction on Other Data Types • Proof by induction is not limited to lists. Indeed, it is usually taught on natural numbers first. • Example: Exponentiation, defined by:(^) :: Integer -> Integer -> Integer x^0 = 1 x^n = x * x^(n-1) • Suppose we wish to prove that, for all natural numbers m and n:x^(n+m) = x^n * x^m
Example 4:A Property About Exponentiation As before, we proceed by induction. • Base case: n = 0 x^(0+m) x^m 1 * (x^m) x^0 * x^m • Induction step: assume x^(n+m) = x^n * x^m x^((n+1)+m) x * x^(n+m) x * (x^n * x^m) (x * x^n) * x^m x^(n+1) * x^m
Induction OverUser-Defined Data Types • Recall the tree data type: data Tree a = Leaf a | Branch (Tree a) (Tree a) • And define these functions on it: flatten :: Tree a -> [a] flatten (Leaf x) = [x] flatten (Branch t1 t2) = flatten t1 ++ flatten t2 sumTree :: Tree Int -> Int sumTree (Leaf x) = x sumTree (Branch t1 t2) = sumTree t1 + sumTree t2 • Further, assume that: sum = foldl (+) 0 • Lemma: sum (xs++ys) = sum xs + sum ys
Example 5:A Property About Trees Theorem: sum . flatten = sumTree Proof by induction: • Base case: t = Leaf xsum (flatten (Leaf x)) sum [x] -- unfold flatten foldl (+) 0 (x:[]) -- syntax, and unfold sum x + (foldl (+) 0 []) -- unfold foldl x + 0 -- unfold foldl x -- arithmetic sumTree (Leaf x) -- fold sumTree
Example 5, cont’d • Induction step: assume that • sum (flatten t1) = sumTree t1 • sum (flatten t2) = sumTree t2 sum (flatten (Branch t1 t2)) sum (flatten t1 ++ flatten t2) -- unfold flatten sum (flatten t1) + sum (flatten t2) -- lemma sumTree t1 + sumTree t2 -- induction hyp. sumTree (Branch t1 t2) -- fold sumTree