250 likes | 328 Views
Class 20: Quick Sorting. David Evans http://www.cs.virginia.edu/evans. Queen’s University, Belfast, Northern Ireland. CS200: Computer Science University of Virginia Computer Science. TuttleSort. (define (TuttleSort cf lst) (if (null? lst) lst (let ((most (find-most cf lst)))
E N D
Class 20: Quick Sorting David Evans http://www.cs.virginia.edu/evans Queen’s University, Belfast, Northern Ireland CS200: Computer Science University of Virginia Computer Science
TuttleSort (define (TuttleSort cf lst) (if (null? lst) lst (let ((most (find-most cf lst))) (cons most (TuttleSort cf (delete lst most)))))) (define (find-most cf lst) (insertl (lambda (c1 c2) (if (cf c1 c2) c1 c2)) lst (car lst))) TuttleSort is (n2) If we double the length of the list, we amount of work sort does approximately quadruples. CS 200 Spring 2004
Insert Sort (define (insertel cf el lst) (if (null? lst) (list el) (if (cf el (car lst)) (cons el lst) (cons (car lst) (insertel cf el (cdr lst)))))) (define (insertsort cf lst) (if (null? lst) null (insertel cf (car lst) (insertsort cf (cdr lst))))) insertsort is (n2) CS 200 Spring 2004
Divide and Conquer • Both TuttleSort and InsertSort divide the problem of sorting a list of length n into: • Sorting a list of length n-1 • Doing the right thing with one element • Hence, there are always n steps • And since each step is (n), they are (n2) • To sort more efficiently, we need to divide the problem more evenly each step CS 200 Spring 2004
Can we do better? (insertel < 88 (list 1 2 3 5 6 23 63 77 89 90)) Suppose we had procedures (first-half lst) (second-half lst) that quickly divided the list in two halves? CS 200 Spring 2004
Insert Halves (define (insertelh cf el lst) (if (null? lst) (list el) (let ((fh (first-half lst)) (sh (second-half lst))) (if (cf el (car fh)) (append (cons el fh) sh) (if (null? sh) (append fh (list el)) (if (cf el (car sh)) (append (insertelh cf el fh) sh) (append fh (insertelh cf el sh)))))))) CS 200 Spring 2004
Evaluating insertelh > (insertelh < 3 (list 1 2 4 5 7)) |(insertelh #<procedure:traced-<> 3 (1 2 4 5 7)) | (< 3 1) | #f | (< 3 5) | #t | (insertelh #<procedure:traced-<> 3 (1 2 4)) | |(< 3 1) | |#f | |(< 3 4) | |#t | |(insertelh #<procedure:traced-<> 3 (1 2)) | | (< 3 1) | | #f | | (< 3 2) | | #f | | (insertelh #<procedure:traced-<> 3 (2)) | | |(< 3 2) | | |#f | | (2 3) | |(1 2 3) | (1 2 3 4) |(1 2 3 4 5 7) (1 2 3 4 5 7) (define (insertelh cf el lst) (if (null? lst) (list el) (let ((fh (first-half lst)) (sh (second-half lst))) (if (cf el (car fh)) (append (cons el fh) sh) (if (null? sh) (append fh (list el)) (if (cf el (car sh)) (append (insertelh cf el fh) sh) (append fh (insertelh cf el sh)))))))) Every time we call insertelh, the size of the list is approximately halved! CS 200 Spring 2004
How much work is insertelh? Supposefirst-half and second-half are (1) Each time we call insertelh, the size of lst halves. So, doubling the size of the list only increases the number of calls by 1. (define (insertelh cf el lst) (if (null? lst) (list el) (let ((fh (first-half lst)) (sh (second-half lst))) (if (cf el (car fh)) (append (cons el fh) sh) (if (null? sh) (append fh (list el)) (if (cf el (car sh)) (append (insertelh cf el fh) sh) (append fh (insertelh cf el sh)))))))) List Size Number of insertelh applications 1 1 2 2 4 3 8 4 16 5 CS 200 Spring 2004
How much work is insertelh? Supposefirst-half and second-half are (1) Each time we call insertelh, the size of lst halves. So, doubling the size of the list only increases the number of calls by 1. insertelh is (log2n) log2a=b means 2b=a List Size Number of insertelh applications 1 1 2 2 4 3 8 4 16 5 CS 200 Spring 2004
insertsorth Same as insertsort, except uses insertelh (define (insertsorth cf lst) (if (null? lst) null (insertelh cf (car lst) (insertsorth cf (cdr lst))))) (define (insertelh cf el lst) (if (null? lst) (list el) (let ((fh (first-half lst)) (sh (second-half lst))) (if (cf el (car fh)) (append (cons el fh) sh) (if (null? sh) (append fh (list el)) (if (cf el (car sh)) (append (insertelh cf el fh) sh) (append fh (insertelh cf el sh)))))))) insertsorth would be (n log2n) if we have fast first-half/second-half CS 200 Spring 2004
Is there a fast first-half procedure? • No! • To produce the first half of a list length n, we need to cdr down the first n/2 elements • So: • first-half is (n) • insertelh calls first-half every time…so • insertelh is (n) * (log2 n) = (n log2 n) • insertsorth is (n) * (n log2 n) = (n2log2 n) Yikes! We’ve done all this work, and its still worse than our simple TuttleSort! CS 200 Spring 2004
The Great Lambda Tree of Ultimate Knowledge and Infinite Power CS 200 Spring 2004
Sorted Binary Trees el left right A tree containing all elements x such that (cf x el) is true A tree containing all elements x such that (cf x el) is false CS 200 Spring 2004
Tree Example 3 5 cf: < 2 8 4 7 1 null null CS 200 Spring 2004
Representing Trees (define (make-tree left el right) (list left el right)) (define (get-left tree) (first tree)) (define (get-element tree) (second tree)) (define (get-right tree) (third tree)) left and right are trees (null is a tree) tree must be a non-null tree tree must be a non-null tree tree must be a non-null tree CS 200 Spring 2004
Trees as Lists (define (make-tree left el right) (list left el right)) (define (get-left tree) (first tree)) (define (get-element tree) (second tree)) (define (get-right tree) (third tree)) 5 2 8 1 (make-tree (make-tree (make-tree null 1 null) 2 null) 5 (make-tree null 8 null)) CS 200 Spring 2004
insertel-tree (define (insertel-tree cf el tree) (if (null? tree) (make-tree null el null) (if (cf el (get-element tree)) (make-tree (insertel-tree cf el (get-left tree)) (get-element tree) (get-right tree)) (make-tree (get-left tree) (get-element tree) (insertel-tree cf el (get-right tree)))))) If the tree is null, make a new tree with el as its element and no left or right trees. Otherwise, decide if el should be in the left or right subtree. insert it into that subtree, but leave the other subtree unchanged. CS 200 Spring 2004
How much work is insertel-tree? Each time we call insertel-tree, the size of the tree. So, doubling the size of the tree only increases the number of calls by 1! (define (insertel-tree cf el tree) (if (null? tree) (make-tree null el null) (if (cf el (get-element tree)) (make-tree (insertel-tree cf el (get-left tree)) (get-element tree) (get-right tree)) (make-tree (get-left tree) (get-element tree) (insertel-tree cf el (get-right tree)))))) insertel-tree is (log2n) log2a=b means2b=a CS 200 Spring 2004
insertsort-tree (define (insertsort cf lst) (if (null? lst) null (insertel cf (car lst) (insertsort cf (cdr lst))))) (define (insertsort-worker cf lst) (if (null? lst) null (insertel-tree cf (car lst) (insertsort-worker cf (cdr lst))))) No change…but insertsort-worker evaluates to a tree not a list! (((() 1 ()) 2 ()) 5 (() 8 ())) CS 200 Spring 2004
extract-elements We need to make a list of all the tree elements, from left to right. (define (extract-elements tree) (if (null? tree) null (append (extract-elements (get-left tree)) (cons (get-element tree) (extract-elements (get-right tree)))))) CS 200 Spring 2004
How much work is insertsort-tree? (define (insertsort-tree cf lst) (define (insertsort-worker cf lst) (if (null? lst) null (insertel-tree cf (car lst) (insertsort-worker cf (cdr lst))))) (extract-elements (insertsort-worker cf lst))) (n) applications of insertel-tree each is (log n) (n log2n) CS 200 Spring 2004
Growth of time to sort random list n2 TuttleSort n log2n insertsort-tree CS 200 Spring 2004
Comparing sorts > (testgrowth tuttlesort) n = 250, time = 110 n = 500, time = 371 n = 1000, time = 2363 n = 2000, time = 8162 n = 4000, time = 31757 (3.37 6.37 3.45 3.89) > (testgrowth insertsort) n = 250, time = 40 n = 500, time = 180 n = 1000, time = 571 n = 2000, time = 2644 n = 4000, time = 11537 (4.5 3.17 4.63 4.36) > (testgrowth insertsorth) n = 250, time = 251 n = 500, time = 1262 n = 1000, time = 4025 n = 2000, time = 16454 n = 4000, time = 66137 (5.03 3.19 4.09 4.02) > (testgrowth insertsort-tree) n = 250, time = 30 n = 500, time = 250 n = 1000, time = 150 n = 2000, time = 301 n = 4000, time = 1001 (8.3 0.6 2.0 3.3) CS 200 Spring 2004
Can we do better? • Making all those trees is a lot of work • Can we divide the problem in two halves, without making trees? Continues in Lecture 21… CS 200 Spring 2004