comments isort (y::R) calls isort R • The proof was “by structural induction on L” • Every list value L is either [ ] (nil) or y::R, where R is a “smaller” list value • We could just as well have said “by induction on length of L” • [ ] has length 0 • 0 ≤ length R < length(y::R)
perm facts y::(a perm of R) is a perm of (y::R) A perm of a perm of L is a perm of L In the correctness proof we used some obvious facts about permutations.
corollaries
corollaries isort is a total function from int list to int list
corollaries isort is a total function from int list to int list When e evaluates to L, isort e evaluates to the sorted version of L
a variation fun isort [ ] = [ ] | isort (x::R) = ins (x, isort R) fun isort ’ [ ] = [ ] | isort ’ [x] = [x] | isort ’ (x::R) = ins (x, isort ’ R)
a variation fun isort [ ] = [ ] | isort (x::R) = ins (x, isort R) is this clause fun isort ’ [ ] = [ ] redundant | isort ’ [x] = [x] | isort ’ (x::R) = ins (x, isort ’ R)
variation isort ’ : int list -> int list fun isort ’ [ ] = [ ] | isort ’ [x] = [x] | isort ’ (x::R) = ins (x, isort ’ R) If in doubt, test, then prove
variation isort ’ : int list -> int list fun isort ’ [ ] = [ ] | isort ’ [x] = [x] | isort ’ (x::R) = ins (x, isort ’ R) If in doubt, test, then prove
variation isort ’ : int list -> int list fun isort ’ [ ] = [ ] | isort ’ [x] = [x] | isort ’ (x::R) = ins (x, isort ’ R) If in doubt, test, then prove
equivalent • isort and isort ’ are extensionally equivalent: For all L : int list, isort L = isort ’ L. • Proof? See lecture notes… OR: Re-do the isort proof for isort ’ (easy) Hence they satisfy the same spec, so For all L : int list, isort L = isort ’ L = the sorted perm of L
equivalent • isort and isort ’ are extensionally equivalent: For all L : int list, isort L = isort ’ L. • Proof? See lecture notes… OR: Re-do the isort proof for isort ’ (easy) Hence they satisfy the same spec, so For all L : int list, isort L = isort ’ L = the sorted perm of L No need for extra clause but it doesn’t do any harm
work • Let W ins (n) be the work for ins(x, L) when x, L are values and L has length n • Let W isort (n) be the work for isort(L) when L is a list of length n
work • Let W ins (n) be the work for ins(x, L) when x, L are values and L has length n W ins (n) is O(n) • Let W isort (n) be the work for isort(L) when L is a list of length n
work • Let W ins (n) be the work for ins(x, L) when x, L are values and L has length n W ins (n) is O(n) • Let W isort (n) be the work for isort(L) when L is a list of length n W isort (0) = 1 W isort (n) = 1 + W ins (n-1) + W isort (n-1) for n > 0
work • Let W ins (n) be the work for ins(x, L) when x, L are values and L has length n W ins (n) is O(n) • Let W isort (n) be the work for isort(L) when L is a list of length n
work • Let W ins (n) be the work for ins(x, L) when x, L are values and L has length n W ins (n) is O(n) • Let W isort (n) be the work for isort(L) when L is a list of length n W isort (0) = 1 W isort (n) = O(n) + W isort (n-1) for n > 0
work • Let W ins (n) be the work for ins(x, L) when x, L are values and L has length n W ins (n) is O(n) • Let W isort (n) be the work for isort(L) when L is a list of length n W isort (0) = 1 W isort (n) = O(n) + W isort (n-1) for n > 0 W isort (n) is O(n 2 )
work WE CAN DO BETTER! • Let W ins (n) be the work for ins(x, L) THIS IS SLOW! when x, L are values and L has length n W ins (n) is O(n) • Let W isort (n) be the work for isort(L) when L is a list of length n W isort (0) = 1 W isort (n) = O(n) + W isort (n-1) for n > 0 W isort (n) is O(n 2 )
mergesort Conceptually, a merge sort works as follows: 1. Divide the unsorted list into n sublists, each containing 1 element. 2. Repeatedly Merge sublists to produce new sublists until there is only 1 sublist left.
mergesort Conceptually, a merge sort works as follows: 1. Divide the unsorted list into n sublists, each containing 1 element. 2. Repeatedly Merge sublists to produce new sublists until there is only 1 sublist left. Wrong! Wrong! Wrong!
mergesort Conceptually, a merge sort works as follows: 1. Divide the unsorted list into n sublists, each containing 1 element. 2. Repeatedly Merge sublists to produce new sublists until there is only 1 sublist left. Wrong! Wrong! Wrong! Doesn’t say “recursive”...
mergesort Conceptually, a merge sort works as follows: 1. Divide the unsorted list into n sublists, … what’s n? each containing 1 element. 2. Repeatedly Merge sublists to produce new sublists until there is only 1 sublist left. Wrong! Wrong! Wrong! Doesn’t say “recursive”...
mergesort Conceptually, a merge sort works as follows: 1. Divide the unsorted list into n sublists, … what’s n? each containing 1 element. … repeatedly???? 2. Repeatedly Merge sublists to produce new sublists until there is only 1 sublist left. Wrong! Wrong! Wrong! Doesn’t say “recursive”...
mergesort Conceptually, a merge sort works as follows: 1. Divide the unsorted list into n sublists, … what’s n? each containing 1 element. … repeatedly???? 2. Repeatedly Merge sublists to produce new sublists until there is only 1 sublist left. … and then? Wrong! Wrong! Wrong! Doesn’t say “recursive”...
mergesort Conceptually, a merge sort works as follows: 1. Divide the unsorted list into n sublists, … what’s n? each containing 1 element. … repeatedly???? 2. Repeatedly Merge sublists to produce new sublists until there is only 1 sublist left. … and then? What’s the output? How does it relate to the input? Wrong! Wrong! Wrong! Doesn’t say “recursive”...
mergesort A recursive divide-and-conquer algorithm • If list has length 0 or 1, do nothing. • Otherwise, split the list into two shorter lists, sort these two lists, merge the (sorted) results
implementation • First, let’s design helper functions split : int list -> int list * int list merge : int list * int list -> int list
implementation • First, let’s design helper functions split : int list -> int list * int list merge : int list * int list -> int list (what specs should we use?)
implementation • First, let’s design helper functions split : int list -> int list * int list merge : int list * int list -> int list (what specs should we use?) split splits a list into two sublists merge combines two sorted lists into one
implementation • First, let’s design helper functions split : int list -> int list * int list merge : int list * int list -> int list (what specs should we use?) split splits a list into two sublists merge combines two sorted lists into one (a bit imprecise, but we’ll fix that…)
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L.
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. write as length(A) ≈ length(B)
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L.
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. fun split [ ] = ([ ], [ ])
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ])
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) =
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) = let val (A, B) = split L in
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) = let val (A, B) = split L in (x::A, y::B) end
split split : int list -> int list * int list ENSURES split(L) = a pair of lists (A, B) such that length(A) and length(B) differ by at most 1, and A@B is a permutation of L. fun split [ ] = ([ ], [ ]) | split [x] = ([x], [ ]) | split (x::y::L) = let val (A, B) = split L in (x::A, y::B) end note the use of list patterns and pair patterns
split equations For all values x, y : int and L : int list, split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) = let val (A, B) = split L in (x::A, y::B) end
split equations For all values x, y : int and L : int list, split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) split (x::y::L) =
split equations For all values x, y : int and L : int list, split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) (x::A, y::B), split (x::y::L) = where (A, B) = split L
split equations For all values x, y : int and L : int list, split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) (x::A, y::B), split (x::y::L) = where (A, B) = split L Can be used to calculate split R for any value R : int list
split equations For all values x, y : int and L : int list, split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) (x::A, y::B), split (x::y::L) = where (A, B) = split L Can be used to calculate split R for any value R : int list split [4,2,1,3] = ([4,1], [2,3])
split equations For all values x, y : int and L : int list, split [ ] = ([ ], [ ]) split [x] = ([x], [ ]) (x::A, y::B), split (x::y::L) = where (A, B) = split L Can be used to calculate split R for any value R : int list split [4,2,1,3] = ([4,1], [2,3]) split [4,2,1] = ([4,1], [2])
For all L:int list, split(L) = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a permutation of L. • Proof : by (strong) induction on length of L • Base cases : L = [ ], [x] EASY • Inductive case : L=x::(y::R) R is shorter than L Assume Induction Hypothesis: split(R) = a pair (A ’ , B ’ ) such that length(A ’ ) ≈ length(B ’ ) and A ’ @B ’ is a perm of R. Show that split(x::y::R) = a pair (A, B) such that length(A) ≈ length(B) and A@B is a perm of x::(y::R).
For all L:int list, split(L) = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a permutation of L. • Proof : by (strong) induction on length of L • Base cases : L = [ ], [x] split [ ] = ([ ], [ ]) EASY split [x] = ([x], [ ]) • Inductive case : L=x::(y::R) R is shorter than L Assume Induction Hypothesis: split(R) = a pair (A ’ , B ’ ) such that length(A ’ ) ≈ length(B ’ ) and A ’ @B ’ is a perm of R. Show that split(x::y::R) = a pair (A, B) such that length(A) ≈ length(B) and A@B is a perm of x::(y::R).
For all L:int list, split(L) = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a permutation of L. • Proof : by (strong) induction on length of L • Base cases : L = [ ], [x] split [ ] = ([ ], [ ]) EASY split [x] = ([x], [ ]) • Inductive case : L=x::(y::R) R is shorter than L Assume Induction Hypothesis: split(R) = a pair (A ’ , B ’ ) such that length(A ’ ) ≈ length(B ’ ) and A ’ @B ’ is a perm of R. Show that split(x::y::R) = a pair (A, B) such that length(A) ≈ length(B) and A@B is a perm of x::(y::R). split (x::y::R) = (x::A ’ , y::B ’ ) length(x::A ’ ) ≈ length(y::B ’ ) (x::A ’ )@(y::B ’ ) is a perm of x::(y::R)
comments • We used strong induction on length of L Reason: split(x::y::R) calls split(R) and length of R is two less than length of x::y::R. • If length L = n > 1 and split(L) = (A, B), A and B are shorter than L If n is even > 1, length A = length B = n div 2 < n. If n is odd > 1, length A = (n div 2) + 1 < n, length B = n div 2 < n.
merge merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B
merge merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B fun merge (A, [ ]) = A | merge ([ ], B) = B
merge merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of
merge merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R)
merge merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R)
merge merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R) | GREATER => y :: merge(x::L, R)
merge merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R) | GREATER => y :: merge(x::L, R) We need a 3-way branch, so cased comparison is better than nested if-then-else
merge equations For all values x, y : int and A, B : int list, merge (A, [ ]) = A merge ([ ], B) = B merge (x::A, y::B) = case compare(x, y) of LESS => x :: merge(A, y::B) | EQUAL => x :: y :: merge(A, B) | GREATER => y :: merge(x::A, B)
merge equations For all values x, y : int and A, B : int list, merge (A, [ ]) = A merge ([ ], B) = B
merge equations For all values x, y : int and A, B : int list, merge (A, [ ]) = A merge ([ ], B) = B if x<y merge (x::A, y::B) = x :: merge(A, y::B) if x=y = x :: y :: merge(A, B) if x>y = y :: merge(x::A, B)
merge equations For all values x, y : int and A, B : int list, merge (A, [ ]) = A merge ([ ], B) = B if x<y merge (x::A, y::B) = x :: merge(A, y::B) if x=y = x :: y :: merge(A, B) if x>y = y :: merge(x::A, B) Can be used to evaluate merge(L, R) for all values L, R : int list
merge equations For all values x, y : int and A, B : int list, merge (A, [ ]) = A merge ([ ], B) = B if x<y merge (x::A, y::B) = x :: merge(A, y::B) if x=y = x :: y :: merge(A, B) if x>y = y :: merge(x::A, B) Can be used to evaluate merge(L, R) for all values L, R : int list merge([1,4], [2,3]) = [1,2,3,4]
correctness? fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R) | GREATER => y :: merge(x::L, R) How do we prove this function satisfies the spec? • Induction, but on on what ? - in base cases, at least one list is empty - in recursive calls, one or both is shorter
correctness? fun merge (A, [ ]) = A | merge ([ ], B) = B | merge (x::L, y::R) = case compare(x, y) of LESS => x :: merge(L, y::R) | EQUAL => x :: y :: merge(L, R) | GREATER => y :: merge(x::L, R) How do we prove this function satisfies the spec? • Induction, but on on what ? The product of list lengths! - in base cases, at least one list is empty - in recursive calls, one or both is shorter
correctness For all sorted lists A and B, merge(A, B) = a sorted permutation of A@B. Proof : strong induction on product of lengths of A, B. • Base cases : (A, [ ]) and ([ ], B). (i) Show: if A is sorted, merge(A, [ ]) = a sorted perm of A@[ ]. (ii) Show: if B is sorted, merge([ ], B) = a sorted perm of [ ]@B. • Inductive case : (x::A, y::B) Assume IH: for all pairs of sorted lists (A ’ , B ’ ) with smaller product of lengths than (x::A, y::B), merge(A ’ , B ’ ) = a sorted perm of A ’ @B ’ . Show: if x::A and y::B are sorted, then merge(x::A, y::B) = a sorted perm of (x::A)@(y::B). Exercise: fill in the details!
msort • We proved that split and merge are correct split : int list -> int list * int list ENSURES split L = a pair of lists (A, B) such that length(A) ≈ length(B) and A@B is a perm of L merge : int list * int list -> int list REQUIRES A and B are sorted lists ENSURES merge(A, B) = a sorted perm of A@B • Now let’s use them to define msort msort : int list -> int list ENSURES msort L = a sorted perm of L
msort msort : int list -> int list ENSURES msort(L) = a sorted perm of L
msort msort : int list -> int list ENSURES msort(L) = a sorted perm of L fun msort [ ] = [ ]
Recommend
More recommend