Quicksort. Alternative Strategies for Dividing Lists. Fundamentals of Computer Science I (CS F)

Similar documents
Repetition Through Recursion

Laboratory: Recursion Basics

Treaps. 1 Binary Search Trees (BSTs) CSE341T/CSE549T 11/05/2014. Lecture 19

Representing Images as Lists of Spots

User-defined Functions. Conditional Expressions in Scheme

Announcements. The current topic: Scheme. Review: BST functions. Review: Representing trees in Scheme. Reminder: Lab 2 is due on Monday at 10:30 am.

Quicksort (CLRS 7) We saw the divide-and-conquer technique at work resulting in Mergesort. Mergesort summary:

CS 137 Part 8. Merge Sort, Quick Sort, Binary Search. November 20th, 2017

Scheme. Functional Programming. Lambda Calculus. CSC 4101: Programming Languages 1. Textbook, Sections , 13.7

Richard Feynman, Lectures on Computation

CS 171: Introduction to Computer Science II. Quicksort

We can use a max-heap to sort data.

CS240 Fall Mike Lam, Professor. Quick Sort

Intro. Scheme Basics. scm> 5 5. scm>

Computer Science 21b (Spring Term, 2015) Structure and Interpretation of Computer Programs. Lexical addressing

Functional Programming. Pure Functional Programming

COSC242 Lecture 7 Mergesort and Quicksort

Week - 04 Lecture - 01 Merge Sort. (Refer Slide Time: 00:02)

Better sorting algorithms (Weiss chapter )

(Refer Slide Time: 01.26)

Statistics Case Study 2000 M. J. Clancy and M. C. Linn

Chapter 5: Algorithms and Heuristics. CS105: Great Insights in Computer Science

Design and Analysis of Algorithms Prof. Madhavan Mukund Chennai Mathematical Institute

Sorting. Sorting in Arrays. SelectionSort. SelectionSort. Binary search works great, but how do we create a sorted array in the first place?

;;; Determines if e is a primitive by looking it up in the primitive environment. ;;; Define indentation and output routines for the output for

Lab 9: More Sorting Algorithms 12:00 PM, Mar 21, 2018

Department of Electrical Engineering and Computer Sciences Fall 2000 Instructor: Dan Garcia CS 3 Final Exam

Sorting: Given a list A with n elements possessing a total order, return a list with the same elements in non-decreasing order.

Lecture 8: Mergesort / Quicksort Steven Skiena

Mergesort again. 1. Split the list into two equal parts

S O R T I N G Sorting a list of elements implemented as an array. In all algorithms of this handout the sorting of elements is in ascending order

CS 171: Introduction to Computer Science II. Quicksort

April 2 to April 4, 2018

1 ICS 161: Design and Analysis of Algorithms Lecture notes for January 23, Bucket Sorting

CS125 : Introduction to Computer Science. Lecture Notes #38 and #39 Quicksort. c 2005, 2003, 2002, 2000 Jason Zych

DIVIDE & CONQUER. Problem of size n. Solution to sub problem 1

Sorting. CSE 143 Java. Insert for a Sorted List. Insertion Sort. Insertion Sort As A Card Game Operation. CSE143 Au

Sorting is a problem for which we can prove a non-trivial lower bound.

Sorting: Quick Sort. College of Computing & Information Technology King Abdulaziz University. CPCS-204 Data Structures I

17/05/2018. Outline. Outline. Divide and Conquer. Control Abstraction for Divide &Conquer. Outline. Module 2: Divide and Conquer

Lecture Notes on Quicksort

9. The Disorganized Handyman

Overview. CS301 Session 3. Problem set 1. Thinking recursively

PRAM Divide and Conquer Algorithms

Mergesort again. 1. Split the list into two equal parts

CS61A Notes 02b Fake Plastic Trees. 2. (cons ((1 a) (2 o)) (3 g)) 3. (list ((1 a) (2 o)) (3 g)) 4. (append ((1 a) (2 o)) (3 g))

Sorting. CSC 143 Java. Insert for a Sorted List. Insertion Sort. Insertion Sort As A Card Game Operation CSC Picture

Unit-2 Divide and conquer 2016

Lecture Notes on Quicksort

CSE373: Data Structure & Algorithms Lecture 21: More Comparison Sorting. Aaron Bauer Winter 2014

Computer Science 385 Analysis of Algorithms Siena College Spring Topic Notes: Divide and Conquer

Lecture 6: Sequential Sorting

PROBLEM SOLVING 11. July 24, 2012

Quicksort (Weiss chapter 8.6)

MORE SCHEME. 1 What Would Scheme Print? COMPUTER SCIENCE MENTORS 61A. October 30 to November 3, Solution: Solutions begin on the following page.

1.3. Conditional expressions To express case distinctions like

About this exam review

COMP 250 Fall recurrences 2 Oct. 13, 2017


CS102 Sorting - Part 2

Plan of the lecture. Quick-Sort. Partition of lists (or using extra workspace) Quick-Sort ( 10.2) Quick-Sort Tree. Partitioning arrays

Lecture 7 Quicksort : Principles of Imperative Computation (Spring 2018) Frank Pfenning

Discussion 4. Data Abstraction and Sequences

CS 275 Name Final Exam Solutions December 16, 2016

6.001 Notes: Section 8.1

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

Data structures. More sorting. Dr. Alex Gerdes DIT961 - VT 2018

Table ADT and Sorting. Algorithm topics continuing (or reviewing?) CS 24 curriculum

Admin. How's the project coming? After these slides, read chapter 13 in your book. Quizzes will return

CS S-11 Sorting in Θ(nlgn) 1. Base Case: A list of length 1 or length 0 is already sorted. Recursive Case:

CSE 332: Data Structures & Parallelism Lecture 12: Comparison Sorting. Ruth Anderson Winter 2019

15 150: Principles of Functional Programming Sorting Integer Lists

Principles of Algorithm Design

Key question: how do we pick a good pivot (and what makes a good pivot in the first place)?

Generative and accumulative recursion. What is generative recursion? Example revisited: GCD. Readings: Sections 25, 26, 27, 30, 31

Scribe: Sam Keller (2015), Seth Hildick-Smith (2016), G. Valiant (2017) Date: January 25, 2017

Jana Kosecka. Linear Time Sorting, Median, Order Statistics. Many slides here are based on E. Demaine, D. Luebke slides

CS 4349 Lecture October 18th, 2017

DATA STRUCTURE AND ALGORITHM USING PYTHON

Copyright 2012 by Pearson Education, Inc. All Rights Reserved.

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Faster Sorting Methods

6.001 Notes: Section 4.1

Functional abstraction. What is abstraction? Eating apples. Readings: HtDP, sections Language level: Intermediate Student With Lambda

Functional abstraction

An Explicit-Continuation Metacircular Evaluator

IS 709/809: Computational Methods in IS Research. Algorithm Analysis (Sorting)

CS 342 Lecture 7 Syntax Abstraction By: Hridesh Rajan

QuickSort

CS3L Summer 2011 Final Exam, Part 2 You may leave when finished; or, you must stop promptly at 12:20

Syntactic Sugar: Using the Metacircular Evaluator to Implement the Language You Want

The components of a tree are called nodes. At the top is the root node of the tree; in the

Introduction to Algorithms February 24, 2004 Massachusetts Institute of Technology Professors Erik Demaine and Shafi Goldwasser Handout 8

Divide and Conquer CISC4080, Computer Algorithms CIS, Fordham Univ. Instructor: X. Zhang

UNIT 5C Merge Sort. Course Announcements

Comparison Sorts. Chapter 9.4, 12.1, 12.2

Divide and Conquer CISC4080, Computer Algorithms CIS, Fordham Univ. Acknowledgement. Outline

Sorting Algorithms. Slides used during lecture of 8/11/2013 (D. Roose) Adapted from slides by

CS 314 Principles of Programming Languages

Question 7.11 Show how heapsort processes the input:

Transcription:

Fundamentals of Computer Science I (CS151.01 2006F) Quicksort Summary: In a recent reading, you explored merge sort, a comparatively efficient algorithm for sorting lists or vectors. In this reading, we consider one of the more interesting sorting algorithms, Quicksort. Contents: Alternative Strategies for Dividing Lists Identifying Small and Large Elements Selecting a Pivot Refactoring An Alternate Strategy for Building the Lists of Small, Equal, and Large Values Which Quicksort is Best? Alternative Strategies for Dividing Lists As you may recall, the two key ideas in merge sort are: (1) use the technique known as of divide and conquer to divide the list into two halves (and then sort the two halves); (2) merge the halves back together. As we saw in both the reading and the corresponding lab, we can divide the list in almost any way. Are there better sorting algorithms than merge sort? If our primary activity is to compare values, we cannot do better than some constant times nlog 2 n steps in the sorting algorithm. However, that hasn t stopped computer scientists from exploring alternatives to merge sort. (In the next course, you ll learn some reasons that we might want such alternatives.) One way to develop an alternative to merge sort is to split the values in the list in a more sensible way. For example, instead of splitting into about half the elements and the remaining elements, we might choose the to divide into the smaller elements and the larger elements. Why would this strategy be better? Well, if we know that every small element precedes every large element, then we can significantly simplify the merge step: We just need to append the two sorted lists together, with the equal elements in the middle. (define new-sort (lambda ( precedes?) (if (or (null? ) (null? (cdr ))) (let ((smaller (small-elements precedes?)) (same (equal-elements precedes?)) (larger (large-elements precedes?))) (append (new-sort smaller) same (new-sort larger)))))) 1

You ll note that we used precedes? rather than the may-precede? that we used in previous sorting algorithms. That s because we really want to segment out the strictly larger and strictly smaller values. (Other variants of this sorting algorithm might include the equal elements in one side or the others. However, such versions require consideration of some subtleties that we d like to avoid.) Identifying Small and Large Elements It sounds like a great idea, doesn t it? Instead of split and merge, we can sort by writing small-elements, equal elements, and large-elements. So, how do we write those procedures? A statistician might tell us that the small elements are all the elements less than the median and that the large elements are the elements greater than the median. That s a pretty good starting definition. Now, how do we find the median? Usually, we sort the values and look at the middle position. Whoops. If we need the median to sort, and we need to sort to get the median, we re stuck in an overly recursive situation. So, what do we do? A computer scientist named C. A. R. Hoare had an interesting suggestion: Randomly pick some element of the list and use that as a simulated median. That is, anything smaller than that element is small and anything larger than that element is large. Because it s not the median, we need another name for that element. Traditionally, we call it the pivot. Is using a randomly-selected pivot a good strategy? You need more statistics than most of us know to prove formally that it works well. However, practice suggests that it works very well. We can now write small-elements, equal-elements, large-elements by including the pivot. (define small-elements (lambda ( precedes? pivot) ((null? ) null) ((precedes? (car ) pivot) (cons (car ) (small-elements (cdr ) precedes? pivot))) (small-elements (cdr ) precedes? pivot))))) (define large-elements (lambda ( precedes? pivot) ((null? ) null) ((precedes? pivot (car )) (cons (car ) (large-elements (cdr ) precedes? pivot))) (large-elements (cdr ) precedes? pivot))))) (define equal-elements (lambda ( precedes? pivot) ((null? ) null) ((and (not (precedes? pivot (car ))) (not (precedes? (car ) pivot))) (cons (car ) (equal-elements (cdr ) precedes? pivot))) (equal-elements (cdr ) precedes? pivot))))) 2

You may note that equal-elements does not use equal? to compare the pivot to the car. Why not? Because our ordering may be more subtle. For example, we may be comparing people by height (one of the many values we store for each person), and two different people can still be equal in terms of height. Once we ve defined these three procedures, we then have to update our main sorting algorithm to find the pivot and to use it in identifying small, equal, and large elements. Since we use the sublists only once, we won t even bother naming them. We ll call the new algorithm Quicksort, since that s what Hoare called it. (lambda ( precedes?) (if (or (null? ) (null? (cdr ))) (let ((pivot (random-element ))) (append (Quicksort (small-elements precedes? pivot) precedes?) (equal-elements precedes? pivot) (Quicksort (large-elements precedes? pivot) precedes?)))))) Selecting a Pivot How do we select a random element from the list? We ve done so many times before that the code should be self explanatory. (define random-element (lambda () (list-ref (random (length ))))) Refactoring Are we done? In one sense, yes, we have a working sorting procedure. However, good design practice suggests that we look for ways to simplify or otherwise clean up our code. What are the main principles? (1) Don t name anything you don t need to name. (2) Don t duplicate code. The only thing we ve named is the pivot. We ve used it three times, which argues for naming it. More importantly, since the pivot is a randomly chosen value, our code will not work the same (nor will it work correctly) if we substitute (random-element ) for each of the instances of pivot. Hence, the naming is a good strategy. Do we have any duplicated code? Yes, small-values, equal-values, and large-values are very similar. Each scans a list of values and selects those that meet a particular predicate (in the first case, those less than the pivot, in the second, those equal to the pivot, in the third, those greater than to the pivot). Hence, we might want to write a select procedure that extracts the similar code. ;;; Procedure: ;;; select ;;; Parameters: ;;; pred?, a unary predicate ;;;, a list ;;; Purpose: 3

;;; Create a list of all values in for which pred? holds. ;;; Produces: ;;; selected, a list ;;; Preconditions: ;;; pred? can be applied to each element of. ;;; Postconditions: ;;; Every element in selected is in. ;;; pred? holds for every element of selected. ;;; If there s a value in for which pred? holds, then the value is in ;;; selected. (define select (lambda (pred? ) ((null? ) null) ((pred? (car )) (cons (car ) (select pred? (cdr )))) (select pred? (cdr )))))) Now, we can write Quicksort without relying on the helpers small-elements and large-elements. (lambda ( precedes?) (if (or (null? ) (null? (cdr ))) (let ((pivot (random-element ))) (append (Quicksort (select (lambda (val) (precedes? val pivot)) ) precedes?) (select (lambda (val) (and (not (precedes? val pivot)) (not (precedes? pivot val)))) ) (Quicksort (select (lambda (val) (precedes? pivot val)) ) precedes?)))))) Can we make this even shorter? Well, the selection of large elements looks a lot like a use of left-section and the selection of small elements is a lot like a use of right-section. The selection of equal elements we may just have to leave in its more complex form. Now we can write something even more concise. (lambda ( precedes?) (if (or (null? ) (null? (cdr ))) (let ((pivot (random-element ))) (append (Quicksort (select (r-s precedes? pivot) ) precedes?) (select (lambda (val) (and (not (precedes? val pivot)) (not (precedes? pivot val)))) ) (Quicksort (select (l-s precedes? pivot) ) precedes?)))))) 4

An Alternate Strategy for Building the Lists of Small, Equal, and Large Values Some designers might focus not on the duplication of code between small-elements, equal-elements, and large-elements and instead focus on the issue that all we re really doing is splitting into three lists. They might suggest that instead of writing select, we write a partition procedure that breaks a list into three parts. ;;; Procedure: ;;; partition ;;; Parameters: ;;;, a list ;;; pivot, a value ;;; precedes?, a binary predicate ;;; Purpose: ;;; Partition into three lists, ;;; one for which (precedes? val pivot) holds, ;;; one for which (precedes? pivot val) holds, and ;;; one for which neither holds. ;;; Produces: ;;; (smaller-elements equal-elements larger-elements), A two element list ;;; Preconditions: ;;; precedes? can be applied to pivot and any value of. ;;; Postconditions: ;;; (append smaller-elements equal-elements larger-elements) ;;; is a permutation of. ;;; (precedes? (list-ref smaller-elements i) pivot) ;;; holds for every i, 0 < i < (length smaller-elements). ;;; (precedes? pivot (list-ref larger-elements j)) ;;; holds for every j, 0 < j < (length larger-elements). ;;; Neither (precedes? (list-ref equal-elements k) pivot) ;;; nor (precedes? pivot (list-ref equal-elements k)) ;;; holds for eery k, 0 < k < (length equal-elements) (define partition (lambda ( pivot precedes?) (letrec ((kernel (lambda (remaining smaller-elements equal-elements larger-elements) ((null? remaining) (list smaller-elements equal-elements larger-elements)) ((precedes? (car remaining) pivot) (kernel (cdr remaining) (cons (car remaining) smaller-elements) equal-elements larger-elements)) ((precedes? pivot (car remaining)) (kernel (cdr remaining) smaller-elements equal-elements (cons (car remaining) larger-elements))) (kernel (cdr remaining) 5

smaller-elements (cons (car remaining) equal-elements) larger-elements)))))) (kernel null null null)))) Here s yet another version of Quicksort that uses this procedure. (lambda ( precedes?) (display ) (newline) (if (or (null? ) (null? (cdr ))) (let* ((pivot (random-element )) (parts (partition pivot precedes?))) (append (Quicksort (car parts) precedes?) (cadr parts) (Quicksort (caddr parts) precedes?)))))) Which Quicksort is Best? You ve now seen four versions of Quicksort. Which one is best? The last one is probably the most efficient, since it only scans the list once to identify the small elements and large elements. The other three scan the list twice. Different readers find different versions clearer or less clear. You ll need to decide which you like the most from that perspective (and you might even want to think about how you d express the criteria by which you make your decision). Copyright 2006 Samuel A. Rebelsky. This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. 6