15-150 Fall 2020 Lecture 7 Stephen Brookes last time Sorting a - - PowerPoint PPT Presentation

15 150 fall 2020 lecture 7
SMART_READER_LITE
LIVE PREVIEW

15-150 Fall 2020 Lecture 7 Stephen Brookes last time Sorting a - - PowerPoint PPT Presentation

15-150 Fall 2020 Lecture 7 Stephen Brookes last time Sorting a list of integers insertion sort O(n 2 ) for lists of merge sort length n O(n log n) Specifications and proofs helper functions that really help principles


slide-1
SLIDE 1

15-150 Fall 2020 Lecture 7

Stephen Brookes

slide-2
SLIDE 2

last time

  • Sorting a list of integers
  • insertion sort
  • merge sort
  • Specifications and proofs
  • helper functions that really help

O(n2) O(n log n) for lists of length n

slide-3
SLIDE 3

principles

  • Every function needs a spec
  • Every spec needs a proof
  • Recursive functions need inductive proofs
  • Pick an appropriate method...
  • Choose helper functions wisely!

proof of msort was easy, because of split and merge

slide-4
SLIDE 4

the joy of specs

  • The proof for msort relied only on the

specification proven for split and the specification proven for merge

  • We can replace split and merge

by any functions that satisfy the specifications, and the msort proof is still valid!

slide-5
SLIDE 5

even more joy

  • The work analysis for msort relied on the

correctness of split and merge and their work

  • We can replace split and merge

by any functions that satisfy the specifications and have the same work, and get a version of msort with the same work as before (asymptotically)!

slide-6
SLIDE 6

advantages

  • These joyful comments are intended to

convince you of the advantages of compositional reasoning!

  • We can reason about correctness, and

analyze efficiency, in a syntax-directed way.

slide-7
SLIDE 7

so far

  • We proved correctness of isort and showed

that the work for isort L is O(n2)

  • We proved correctness of msort and showed

that the work for msort L is O(n log n) Wmsort(n) = O(n) + 2 Wmsort(n div 2)

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

Wmerge(n) = O(n) Wsplit(n) = O(n)

slide-8
SLIDE 8

can we do better?

Q: Would parallel processing be beneficial? A: Find the span for isort L and msort L

  • If the span is asymptotically better

than the work, there’s a potential speed-up

slide-9
SLIDE 9

can we do better?

Q: Would parallel processing be beneficial? A: Find the span for isort L and msort L

  • If the span is asymptotically better

than the work, there’s a potential speed-up add the work for sub-expressions add the span for dependent sub-expressions max the span for independent sub-expressions

slide-10
SLIDE 10
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L

slide-11
SLIDE 11
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1

slide-12
SLIDE 12
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1 Wins(n) is O(n)

slide-13
SLIDE 13
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1 Wins(n) is O(n) Sins(0) = 1 Sins(n) = Sins(n-1) + 1

slide-14
SLIDE 14
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1 Wins(n) is O(n) Sins(0) = 1 Sins(n) = Sins(n-1) + 1 Sins(n) is O(n)

slide-15
SLIDE 15
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1 Wins(n) is O(n) Sins(0) = 1 Sins(n) = Sins(n-1) + 1 Sins(n) is O(n) Wisort(0) = 1 Wisort(n) = Wisort(n-1) + Wins(n-1) + 1 = O(n) + Wisort(n-1)

slide-16
SLIDE 16
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1 Wins(n) is O(n) Sins(0) = 1 Sins(n) = Sins(n-1) + 1 Sins(n) is O(n) Wisort(0) = 1 Wisort(n) = Wisort(n-1) + Wins(n-1) + 1 = O(n) + Wisort(n-1) Wisort(n) is O(n2)

slide-17
SLIDE 17
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1 Wins(n) is O(n) Sins(0) = 1 Sins(n) = Sins(n-1) + 1 Sins(n) is O(n) Wisort(0) = 1 Wisort(n) = Wisort(n-1) + Wins(n-1) + 1 = O(n) + Wisort(n-1) Sisort(0) = 1 Sisort(n) = Sisort(n-1) + Sins(n-1) + 1 = O(n) + Sisort(n-1) Wisort(n) is O(n2)

slide-18
SLIDE 18
  • The list ops in ins are sequential
  • isort can’t be parallelized - code is dependent

isort

fun isort [ ] = [ ] | isort (x::L) = ins(x, isort L)

fun ins(x, [ ]) = [x] | ins(x, y::L) = if y<x then y::ins(x, L) else x::y::L Wins(0) = 1 Wins(n) = Wins(n-1) + 1 Wins(n) is O(n) Sins(0) = 1 Sins(n) = Sins(n-1) + 1 Sins(n) is O(n) Wisort(0) = 1 Wisort(n) = Wisort(n-1) + Wins(n-1) + 1 = O(n) + Wisort(n-1) Sisort(0) = 1 Sisort(n) = Sisort(n-1) + Sins(n-1) + 1 = O(n) + Sisort(n-1) Wisort(n) is O(n2) Sisort(n) is O(n2)

slide-19
SLIDE 19
  • The list ops in split, merge are sequential
  • But we could use parallel evaluation

for the recursive calls to msort A and msort B

How would this affect runtime?

msort

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

slide-20
SLIDE 20

span of msort

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

slide-21
SLIDE 21

span of msort

Smsort(0) = 1 Smsort(1) = 1

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

slide-22
SLIDE 22

span of msort

Smsort(n) = Ssplit(n) + Smsort(n div 2) + Smerge(n) + 1 Smsort(0) = 1 Smsort(1) = 1

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

slide-23
SLIDE 23

span of msort

Smsort(n) = Ssplit(n) + Smsort(n div 2) + Smerge(n) + 1 Smsort(0) = 1 Smsort(1) = 1

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

first split, then (parallel) recursive calls, then merge

slide-24
SLIDE 24

span of msort

Smsort(n) = Ssplit(n) + Smsort(n div 2) + Smerge(n) + 1 for n>1 Smsort(0) = 1 Smsort(1) = 1

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

first split, then (parallel) recursive calls, then merge

slide-25
SLIDE 25

span of msort

Smsort(n) = Ssplit(n) + Smsort(n div 2) + Smerge(n) + 1 Smsort(n) = O(n) + Smsort(n div 2) for n>1 Smsort(0) = 1 Smsort(1) = 1

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

first split, then (parallel) recursive calls, then merge

slide-26
SLIDE 26

span of msort

Smsort(n) = Ssplit(n) + Smsort(n div 2) + Smerge(n) + 1 Smsort(n) = O(n) + Smsort(n div 2) Smsort(n) is O(n) for n>1 Smsort(0) = 1 Smsort(1) = 1

end fun msort [ ] = [ ] | msort [x] = [x] | msort L = let val (A, B) = split L in merge (msort A, msort B)

first split, then (parallel) recursive calls, then merge

slide-27
SLIDE 27

S(n) = n + S(n div 2) = n + n/2 + S(n div 4) = n + n/2 + n/4 +… + n/2k where k = log2 n = n(1 +1/2 + 1/4 + … + 1/2k) Deriving the span for msort ≤ 2n Simplify recurrence to: So Smsort(n) is O(n) = n + n/2 + n/4 + S(n div 8) This S has same asymptotic behavior as Smsort

slide-28
SLIDE 28

summary

  • msort(L) has O(n log n) work, O(n) span
  • So the potential speed-up factor

from parallel evaluation is O(log n) … in principle, we can speed up mergesort on lists by a factor of log n

slide-29
SLIDE 29

summary

  • msort(L) has O(n log n) work, O(n) span
  • So the potential speed-up factor

from parallel evaluation is O(log n) … in principle, we can speed up mergesort on lists by a factor of log n but this would require O(n) parallel processors… expensive!

slide-30
SLIDE 30

summary

  • msort(L) has O(n log n) work, O(n) span
  • So the potential speed-up factor

from parallel evaluation is O(log n) … in principle, we can speed up mergesort on lists by a factor of log n

slide-31
SLIDE 31

summary

  • msort(L) has O(n log n) work, O(n) span
  • So the potential speed-up factor

from parallel evaluation is O(log n) … in principle, we can speed up mergesort on lists by a factor of log n To do any better, we need a different data structure…

slide-32
SLIDE 32

summary

  • msort(L) has O(n log n) work, O(n) span
  • So the potential speed-up factor

from parallel evaluation is O(log n) … in principle, we can speed up mergesort on lists by a factor of log n To do any better, we need a different data structure…

slide-33
SLIDE 33

pause for thought

  • We’re going to try using trees instead of lists

to represent collections of integers

  • We’ll define a sorting function for trees…
  • … and we’ll analyze its work and span

to see if trees enable improved efficiency

  • We’ll begin with some basic functions on trees
  • And first, a reminder…
slide-34
SLIDE 34

work and spam

span is the number of evaluation steps, assuming unlimited parallelism work is the number of evaluation steps, assuming sequential processing

slide-35
SLIDE 35

work and spam

span is the number of evaluation steps, assuming unlimited parallelism work is the number of evaluation steps, assuming sequential processing span is always ≤ work

slide-36
SLIDE 36

work and spam

span is the number of evaluation steps, assuming unlimited parallelism work is the number of evaluation steps, assuming sequential processing span is always ≤ work For sequential code, span = work

slide-37
SLIDE 37

dealing with trees

  • Q: How to represent binary trees,

with data at nodes

  • A: Use an ML datatype for binary trees

Q: How to work with special trees A: Use an invariant

  • binary search trees
  • sorted trees
  • balanced trees
slide-38
SLIDE 38

datatypes

  • ML allows users to invent their own types
  • With constructors for building values…
  • … and patterns for de-constructing values

And a tailor-made form of induction… structural induction

slide-39
SLIDE 39

a datatype of trees

  • A user-defined type family ’a tree
  • With value constructors Empty and Node

datatype ’a tree = Empty | Node of ’a tree * ’a * ’a tree

Empty : ’a tree Node : ’a tree * ’a * ’a tree -> ’a tree

A value of type int tree is a binary tree with integers at its nodes

slide-40
SLIDE 40

tree values

  • Empty
  • Node(T1, x, T2)

an empty tree a non-empty tree, with x at the root node, left child T1, right child T2

.

T1 T2 x

(not drawn)

Every tree value is either Empty,

  • r built with Node from “smaller” trees
slide-41
SLIDE 41

A tree value is either Empty

  • r Node(T1, x, T2),

where T1 and T2 are tree values and x is a value. list values A list value is either nil

  • r x::L, where L is a list value and x is a value.

An inductive definition

tree values

slide-42
SLIDE 42

equality

Empty = T if and only if T is Empty

Node(T1, x, T2) = Node(U1, y, U2) if and only if T1 = U1, x = y and T2 = U2 for list values nil = L if and only if L is nil x::L = y::R if and only if x=y and L = R for tree values

(a.k.a equivalence)

slide-43
SLIDE 43

equality types

  • A type of form t tree is an equality type if

and only if t is an equality type

fun equal(Empty, Empty) = true | equal(Empty, Node _) = false | equal(Node _, Empty) = false | equal(Node(A1, x1, B1), Node(A2, x2, B2)) = (x1 = x2) andalso equal(A1, A2) andalso equal(B1, B2); val equal = fn : ''a tree * ''a tree -> bool

slide-44
SLIDE 44

equality types

  • A type of form t tree is an equality type if

and only if t is an equality type

fun equal(Empty, Empty) = true | equal(Empty, Node _) = false | equal(Node _, Empty) = false | equal(Node(A1, x1, B1), Node(A2, x2, B2)) = (x1 = x2) andalso equal(A1, A2) andalso equal(B1, B2); val equal = fn : ''a tree * ''a tree -> bool stdIn:10.5 Warning: calling polyEqual

(ignore this!)

slide-45
SLIDE 45

structural definition

  • Base clause:

Define F(Empty)

  • Inductive clause:

Define F(Node(T1, x, T2)) using x and F(T1) and F(T2). Contrast with structural definition for lists To define a function F on trees: That’s enough! Why?

slide-46
SLIDE 46

structural induction

  • Base case: Prove P(Empty).
  • Inductive case:

Assume IH: P(T1) and P(T2). Prove P(Node(T1, x, T2)), for all values x. Contrast with structural induction for lists To prove: For all tree values T, P(T) holds by structural induction on T That’s enough! Why?

slide-47
SLIDE 47

tree patterns

Empty an empty tree Node(_, _, _) a non-empty tree Node(Empty, _, Empty) a tree with one node Node(_, 42, _) a tree with 42 at root Empty Node(p1, p, p2) pattern matching values

slide-48
SLIDE 48

tree patterns match tree values

Empty matches T iff T is Empty Node(p1, p, p2) matches T iff T is Node(T1, v, T2) such that p1 matches T1, p matches v, p2 matches T2 and combines all the bindings Node(A, x, B) matches 3 4 2

and binds x to 3, A to Node(Empty,4,Empty) B to Node(Empty,2,Empty)

. . .

slide-49
SLIDE 49

using trees

  • Let’s introduce some useful functions for

building and manipulating tree values Full : int * int -> int tree Full(x, n) = a complete binary tree of depth n size : ’a tree -> int the number of nodes depth : ’a tree -> int the longest path length

slide-50
SLIDE 50

tree code

fun Leaf(x:int): int tree = Node(Empty, x, Empty) fun Full(x:int, n:int): int tree = if n=0 then Empty else let val T = Full(x, n-1) in Node(T, x, T) end 42

.

42

.

42

.

42

.

42

.

42

. 42 . 42 .

Leaf 42 Full(42,3)

slide-51
SLIDE 51

tree code

fun Full(x:int, n:int): int tree = if n=0 then Empty else Node(Full(x, n-1), x, Full(x, n-1)) Same function, but WAY slower! Show why, using work recurrences

slide-52
SLIDE 52

size

fun size Empty = 0 | size (Node(T1, _, T2)) = size T1 + size T2 + 1 Uses tree patterns Recursion is structural

size(Node(T1, v, T2)) calls size(T1) and size(T2)

the number of nodes

Easy to prove by structural induction that for all trees T, size(T) = a non-negative integer

slide-53
SLIDE 53

size matters

  • Size is non-negative

size(T) ≥ 0

  • Children have smaller size

size(Ti) < size(Node(T1, x, T2))

  • Many recursive functions on trees make

recursive calls on trees with smaller size.

  • Use induction on size to prove correctness.
slide-54
SLIDE 54

depth

(or height) fun depth Empty = 0 | depth (Node(T1, _, T2)) = Int.max(depth T1, depth T2) + 1 Can prove by structural induction that for all trees T, depth(T) = a non-negative integer

the length of longest path from root to a leaf node

slide-55
SLIDE 55

depth matters

  • For all trees T, depth(T) ≥ 0.
  • Children have smaller depth

depth(Ti) < depth(Node(T1, x, T2))

  • Many recursive functions on trees make

recursive calls on trees with smaller depth.

  • Can use induction on depth to prove

properties or analyze efficiency.

slide-56
SLIDE 56

exercises

  • Prove that for all n≥0

size(Full(42, n)) = 2n-1 depth(Full(42, n)) = n 42

.

42

.

42

.

42

.

42

. 42 . 42 .

Full(42, 3) size is 7 depth is 3

slide-57
SLIDE 57

useful facts

  • The children of a full binary tree of depth d

are full binary trees of depth d-1

  • Each child of a full binary tree of size 2d-1

has size 2d-1-1 so contains about half the items

  • For all tree values T,

size T ≤ 2depth T - 1

(Exercise: prove this, using structural induction)

slide-58
SLIDE 58

traversal

  • We can generate a list from a tree by

traversing it and collecting the data at nodes.

  • There are many different traversal orders in

wide use; each can be programmed in ML. inorder preorder postorder breadth-first depth-first

}

slide-59
SLIDE 59

traversal

42

.

2

.

21

.

3

.

7

.

[2, 42, 4, 3, 21, 7] in-order

.

4 pre-order [42, 2, 21, 3, 4, 7] post-order [2, 4, 3, 7, 21, 42] breadth-first [42, 2, 21, 3, 7, 4]

slide-60
SLIDE 60

inorder traversal

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2) inord : ’a tree -> ’a list

ENSURES inord T = the in-order traversal list for T

slide-61
SLIDE 61

inorder traversal

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2) inord : ’a tree -> ’a list

ENSURES inord T = the in-order traversal list for T

go left, then root, then right

slide-62
SLIDE 62

inorder traversal

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2) inord : ’a tree -> ’a list

ENSURES inord T = the in-order traversal list for T

slide-63
SLIDE 63

inorder traversal

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2) inord : ’a tree -> ’a list

ENSURES inord T = the in-order traversal list for T

42

.

2

.

21

.

3

.

7

.

[2, 42, 4, 3, 21, 7] inord

.

4

slide-64
SLIDE 64

inord

For all trees T,

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

length (inord T) = size T length (L1 @ L2) = length L1 + length L2 For all lists L1, L2 of the same type

slide-65
SLIDE 65

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

slide-66
SLIDE 66

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

if Node(T1, x, T2) is full, depth n then T1 and T2 are full, depth n-1

slide-67
SLIDE 67

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

if Node(T1, x, T2) is full, depth n then T1 and T2 are full, depth n-1

work for L1@L2 is O(length L1)

slide-68
SLIDE 68

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

if Node(T1, x, T2) is full, depth n then T1 and T2 are full, depth n-1

work for L1@L2 is O(length L1)

length(inord T1) = 2n-1

slide-69
SLIDE 69

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

work for L1@L2 is O(length L1)

length(inord T1) = 2n-1

slide-70
SLIDE 70

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

length(inord T1) = 2n-1

slide-71
SLIDE 71

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

slide-72
SLIDE 72

work

Let Winord(n) be the work to evaluate inord(T) when T is a full binary tree of depth n

depth(T) = n, size(T) = 2n-1

  • Winord(0) = 1
  • Winord(n) = 2Winord(n-1) + O(2n), for n>0

fun inord Empty = [ ] | inord (Node(T1, x, T2)) = inord T1 @ (x :: inord T2)

Winord(n) is O(n2n)

slide-73
SLIDE 73

faster inord

inorder : ’a tree * ’a list -> ’a list

fun inorder (Empty, L) = L | inorder (Node(T1, x, T2), L) = inorder (T1 , x :: inorder (T2, L))

Theorem For all trees T and lists L, inorder (T, L) = (inord T) @ L The work for inorder(T, L), when T is a full tree of depth n, is O(2n)

slide-74
SLIDE 74

balanced trees

  • Empty is balanced
  • Node(A, x, B) is balanced iff

|size(A) - size(B)| ≤ 1 and A, B are balanced

An inductive characterization

  • f the set of balanced trees
slide-75
SLIDE 75

balanced trees

We can build a balanced tree from a list… … and (if we do it right) get the same list back by in-order traversal

1 4 2

[4,1,2]

list2tree inord

slide-76
SLIDE 76

a helper

fun takedrop (0, L) = ([ ], L) | takedrop (n, x::R) = let val (A, B) = takedrop (n-1, R) in (x::A, B) end

takedrop : int * ’a list -> ’a list * ’a list “chops list into two”

slide-77
SLIDE 77

takedrop spec

For all L : int list and n : int with 0 ≤ n ≤ length L, takedrop (n, L) = a pair of lists (A, B) such that L = A@B and length A = n

PROOF By induction on length of L

  • r by structural induction on L
  • the recursive call is on the tail
  • the recursive call is on a shorter list
slide-78
SLIDE 78

building a balanced tree

fun list2tree [ ] = Empty | list2tree [x] = Node(Empty, x, Empty) | list2tree L = let val n = length L val (A, x::B) = takedrop (n div 2, L) in Node(list2tree A, x, list2tree B) end

QUESTION Why is the pattern (A, x::B) justifiable here?

list2tree : ’a list -> ’a tree

list2tree [4,1,2] = ???

slide-79
SLIDE 79

specification

list2tree : int list -> int tree ENSURES list2tree L = a balanced tree T such that inord(T) = L

proof

By induction on list length

  • in recursive calls, length A and length B are less than length L
slide-80
SLIDE 80

correctness

  • Base case (i) L is [ ]

list2tree [ ] = Empty, a balanced tree, and inord Empty = [ ]. QED

  • Base case (ii) L is [x]

list2tree [x] = Node(Empty, x, Empty) and this is a balanced tree. Its inorder traversal list is [x]. QED.

  • Inductive step: next slide…

By induction on length of L

slide-81
SLIDE 81

correctness

  • Inductive step: L has length n ≥ 2.

Assume IH

For all shorter lists R,

list2tree R = a balanced tree with inorder traversal list R.

Show that

list2tree L = a balanced tree with inorder traversal list L.

  • Let (A, C) = takedrop (n div 2, L). Remember that n ≥ 2.

A and C are shorter than L, and A@C=L. C is non-empty, so let C = x::B. B is also shorter than L, and A @ x::B = L. By IH, list2tree A = a balanced tree T1 with inord T1 = A. By IH, list2tree B = a balanced tree T2 with inord T2 = B.

  • list2tree L = Node (T1, x, T2)

This is a balanced tree, and its inorder traversal list is (inord T1) @ x :: (inord T2) = A @ x::B = L

slide-82
SLIDE 82

important

  • Add justifications to the proof steps above
  • See how we used the function definitions

for inord, takedrop, list2tree

  • See where the proven specs for takedrop

and inord were used (sometimes implicitly)

  • Notice the subtle details that justify steps,

e.g. when n ≥ 2 we get 1 ≤ n div 2 < n

(so A and C are shorter than L, C is non-empty)

That proof was sketchy… but a good outline

slide-83
SLIDE 83

example

list2tree [4,1,2,42,3,5] = takedrop (6 div 2, [4,1,2,42,3,5]) = ([4,1,2], [42,3,5]) list2tree [4,1,2] = list2tree [3,5] = 1 4 2 5 3 1 4 2 5 3 42 balanced balanced balanced inorder traversals are as intended

slide-84
SLIDE 84

exercise

  • Write an ML function

layers : ’a tree -> (’a list) list such that layers T = a list of the cross-sections of T (in class, if time)

slide-85
SLIDE 85

exercise

  • Write an ML function

layers : ’a tree -> (’a list) list such that layers T = a list of the cross-sections of T (in class, if time) 42

.

2

.

21

.

3

.

7

.

[[42],[2, 21], [3, 7]] layers

slide-86
SLIDE 86

exercise

  • Using layers : ’a tree -> ’a list list,

define a breadth-first traversal function bftrav : ’a tree -> ’a list

slide-87
SLIDE 87

exercise

  • Using layers : ’a tree -> ’a list list,

define a breadth-first traversal function bftrav : ’a tree -> ’a list 42

.

2

.

21

.

3

.

7

.

[42, 2, 21, 3, 7] bftrav

slide-88
SLIDE 88

coming soon

  • How to sort a tree of integers
  • What’s a sorted tree?
  • Can we exploit parallelism here?
slide-89
SLIDE 89

coming soon

  • How to sort a tree of integers
  • What’s a sorted tree?
  • Can we exploit parallelism here?