Functional Data Structures
Total Page:16
File Type:pdf, Size:1020Kb
Functional Data Structures Tobias Nipkow February 20, 2021 Abstract A collection of verified functional data structures. The emphasis is on conciseness of algorithms and succinctness of proofs, more in the style of a textbook than a library of efficient algorithms. For more details see [13]. Contents 1 Sorting 4 2 Creating Almost Complete Trees 13 3 Three-Way Comparison 19 4 Lists Sorted wrt < 20 5 List Insertion and Deletion 21 6 Specifications of Set ADT 24 7 Unbalanced Tree Implementation of Set 26 8 Association List Update and Deletion 29 9 Specifications of Map ADT 32 10 Unbalanced Tree Implementation of Map 34 11 Augmented Tree (Tree2) 36 12 Function isin for Tree2 37 13 Interval Trees 37 14 AVL Tree Implementation of Sets 44 1 15 Function lookup for Tree2 54 16 AVL Tree Implementation of Maps 55 17 AVL Tree with Balance Factors (1) 59 18 AVL Tree with Balance Factors (2) 63 19 Height-Balanced Trees 68 20 Red-Black Trees 77 21 Red-Black Tree Implementation of Sets 78 22 Alternative Deletion in Red-Black Trees 85 23 Red-Black Tree Implementation of Maps 89 24 2-3 Trees 91 25 2-3 Tree Implementation of Sets 92 26 2-3 Tree Implementation of Maps 101 27 2-3 Tree from List 105 28 2-3-4 Trees 108 29 2-3-4 Tree Implementation of Sets 109 30 2-3-4 Tree Implementation of Maps 122 31 1-2 Brother Tree Implementation of Sets 126 32 1-2 Brother Tree Implementation of Maps 139 33 AA Tree Implementation of Sets 144 34 AA Tree Implementation of Maps 156 35 Join-Based Implementation of Sets 161 36 Join-Based Implementation of Sets via RBTs 169 37 Braun Trees 176 38 Arrays via Braun Trees 180 2 39 Tries via Functions 195 40 Tries via Search Trees 197 41 Binary Tries and Patricia Tries 200 42 Queue Specification 206 43 Queue Implementation via 2 Lists 207 44 Priority Queue Specifications 209 45 Heaps 210 46 Leftist Heap 212 47 Binomial Heap 217 48 Time functions for various standard library operations 232 49 The Median-of-Medians Selection Algorithm 233 50 Bibliographic Notes 258 3 1 Sorting theory Sorting imports Complex Main HOL−Library:Multiset begin hide const List:insort declare Let def [simp] 1.1 Insertion Sort fun insort :: 0a::linorder ) 0a list ) 0a list where insort x [] = [x] j insort x (y#ys) = (if x ≤ y then x#y#ys else y#(insort x ys)) fun isort :: 0a::linorder list ) 0a list where isort [] = [] j isort (x#xs) = insort x (isort xs) 1.1.1 Functional Correctness lemma mset insort: mset (insort x xs) = f#x#g + mset xs apply(induction xs) apply auto done lemma mset isort: mset (isort xs) = mset xs apply(induction xs) apply simp apply (simp add: mset insort) done lemma set insort: set (insort x xs) = fxg [ set xs by(simp add: mset insort flip: set mset mset) lemma sorted insort: sorted (insort a xs) = sorted xs apply(induction xs) apply(auto simp add: set insort) done lemma sorted isort: sorted (isort xs) 4 apply(induction xs) apply(auto simp: sorted insort) done 1.1.2 Time Complexity We count the number of function calls. insort x [] = [x] insort x (y#ys) = (if x ≤ y then x#y#ys else y#(insort x ys)) fun T insort :: 0a::linorder ) 0a list ) nat where T insort x [] = 1 j T insort x (y#ys) = (if x ≤ y then 0 else T insort x ys) + 1 isort [] = [] isort (x#xs) = insort x (isort xs) fun T isort :: 0a::linorder list ) nat where T isort [] = 1 j T isort (x#xs) = T isort xs + T insort x (isort xs) + 1 lemma T insort length: T insort x xs ≤ length xs + 1 apply(induction xs) apply auto done lemma length insort: length (insort x xs) = length xs + 1 apply(induction xs) apply auto done lemma length isort: length (isort xs) = length xs apply(induction xs) apply (auto simp: length insort) done lemma T isort length: T isort xs ≤ (length xs + 1 ) ^ 2 proof(induction xs) case Nil show ?case by simp next case (Cons x xs) have T isort (x#xs) = T isort xs + T insort x (isort xs) + 1 by simp also have ::: ≤ (length xs + 1 ) ^ 2 + T insort x (isort xs) + 1 using Cons:IH by simp 5 also have ::: ≤ (length xs + 1 ) ^ 2 + length xs + 1 + 1 using T insort length[of x isort xs] by (simp add: length isort) also have ::: ≤ (length(x#xs) + 1 ) ^ 2 by (simp add: power2 eq square) finally show ?case . qed 1.2 Merge Sort fun merge :: 0a::linorder list ) 0a list ) 0a list where merge [] ys = ys j merge xs [] = xs j merge (x#xs)(y#ys) = (if x ≤ y then x # merge xs (y#ys) else y # merge (x#xs) ys) fun msort :: 0a::linorder list ) 0a list where msort xs = (let n = length xs in if n ≤ 1 then xs else merge (msort (take (n div 2 ) xs)) (msort (drop (n div 2 ) xs))) declare msort:simps [simp del] 1.2.1 Functional Correctness lemma mset merge: mset(merge xs ys) = mset xs + mset ys by(induction xs ys rule: merge:induct) auto lemma mset msort: mset (msort xs) = mset xs proof(induction xs rule: msort:induct) case (1 xs) let ?n = length xs let ?ys = take (?n div 2 ) xs let ?zs = drop (?n div 2 ) xs show ?case proof cases assume ?n ≤ 1 thus ?thesis by(simp add: msort:simps[of xs]) next assume : ?n ≤ 1 hence mset (msort xs) = mset (msort ?ys) + mset (msort ?zs) by(simp add: msort:simps[of xs] mset merge) also have ::: = mset ?ys + mset ?zs using h: ?n ≤ 1 i by(simp add: 1 :IH ) also have ::: = mset (?ys @ ?zs) by (simp del: append take drop id) 6 also have ::: = mset xs by simp finally show ?thesis . qed qed Via the previous lemma or directly: lemma set merge: set(merge xs ys) = set xs [ set ys by (metis mset merge set mset mset set mset union) lemma set(merge xs ys) = set xs [ set ys by(induction xs ys rule: merge:induct)(auto) lemma sorted merge: sorted (merge xs ys) ! (sorted xs ^ sorted ys) by(induction xs ys rule: merge:induct)(auto simp: set merge) lemma sorted msort: sorted (msort xs) proof(induction xs rule: msort:induct) case (1 xs) let ?n = length xs show ?case proof cases assume ?n ≤ 1 thus ?thesis by(simp add: msort:simps[of xs] sorted01 ) next assume : ?n ≤ 1 thus ?thesis using 1 :IH by(simp add: sorted merge msort:simps[of xs]) qed qed 1.2.2 Time Complexity We only count the number of comparisons between list elements. fun C merge :: 0a::linorder list ) 0a list ) nat where C merge [] ys = 0 j C merge xs [] = 0 j C merge (x#xs)(y#ys) = 1 + (if x ≤ y then C merge xs (y#ys) else C merge (x#xs) ys) lemma C merge ub: C merge xs ys ≤ length xs + length ys by (induction xs ys rule: C merge:induct) auto fun C msort :: 0a::linorder list ) nat where C msort xs = 7 (let n = length xs; ys = take (n div 2 ) xs; zs = drop (n div 2 ) xs in if n ≤ 1 then 0 else C msort ys + C msort zs + C merge (msort ys)(msort zs)) declare C msort:simps [simp del] lemma length merge: length(merge xs ys) = length xs + length ys apply (induction xs ys rule: merge:induct) apply auto done lemma length msort: length(msort xs) = length xs proof (induction xs rule: msort:induct) case (1 xs) show ?case by (auto simp: msort:simps [of xs] 1 length merge) qed Why structured proof? To have the name "xs" to specialize msort.simps with xs to ensure that msort.simps cannot be used recursively. Also works without this precaution, but that is just luck. lemma C msort le: length xs = 2^k =) C msort xs ≤ k ∗ 2^k proof(induction k arbitrary: xs) case 0 thus ?case by (simp add: C msort:simps) next case (Suc k) let ?n = length xs let ?ys = take (?n div 2 ) xs let ?zs = drop (?n div 2 ) xs show ?case proof (cases ?n ≤ 1 ) case True thus ?thesis by(simp add: C msort:simps) next case False have C msort(xs) = C msort ?ys + C msort ?zs + C merge (msort ?ys)(msort ?zs) by (simp add: C msort:simps msort:simps) also have ::: ≤ C msort ?ys + C msort ?zs + length ?ys + length ?zs using C merge ub[of msort ?ys msort ?zs] length msort[of ?ys] length msort[of ?zs] by arith 8 also have ::: ≤ k ∗ 2^k + C msort ?zs + length ?ys + length ?zs using Suc:IH [of ?ys] Suc:prems by simp also have ::: ≤ k ∗ 2^k + k ∗ 2^k + length ?ys + length ?zs using Suc:IH [of ?zs] Suc:prems by simp also have ::: = 2 ∗ k ∗ 2^k + 2 ∗ 2 ^ k using Suc:prems by simp finally show ?thesis by simp qed qed lemma C msort log: length xs = 2^k =) C msort xs ≤ length xs ∗ log 2 (length xs) using C msort le[of xs k] apply (simp add: log nat power algebra simps) by (metis (mono tags) numeral power eq of nat cancel iff of nat le iff of nat mult) 1.3 Bottom-Up Merge Sort fun merge adj :: ( 0a::linorder) list list ) 0a list list where merge adj [] = [] j merge adj [xs] = [xs] j merge adj (xs # ys # zss) = merge xs ys # merge adj zss For the termination proof of merge all below.