;;; Procedure: ;;; partition ;;; Parameters: ;;; lst, a list ;;; pivot, a value ;;; precedes?, a binary predicate ;;; Purpose: ;;; Partition lst 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 lst. ;;; Postconditions: ;;; (append smaller-elements equal-elements larger-elements) ;;; is a permutation of lst. ;;; (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 random-element (lambda (lst) (list-ref lst (random (length lst))))) (define partition (lambda (lst pivot precedes?) (letrec ((kernel (lambda (remaining smaller-elements equal-elements larger-elements) (cond ((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))) (else (kernel (cdr remaining) smaller-elements (cons (car remaining) equal-elements) larger-elements)))))) (kernel lst null null null)))) ;;; Procedure: ;;; nth-smallest ;;; Parameters: ;;; n, an integer ;;; lst, a list ;;; precedes?, a binary predicate ;;; Purpose: ;;; To find the nth smallest element of lst. ;;; Produces: ;;; nth, a value ;;; Preconditions: ;;; lst has no duplicates ;;; precedes can be applied to any two elements of lst ;;; n < (- (length lst) 1) ;;; Postconditions: ;;; nth is an element of lst ;;; There are exactly elements, e, in lst such that (precedes? e nth) ;;; Alternate Preconditions: ;;; precedes can be applied to any two elements of lst ;;; n < (- (length lst) 1) ;;; Alternate Postconditions: ;;; nth is an element of lst. ;;; There are no more than n elements, e, in lst such that (precedes? e nth) ;;; There are at least n elements, e, in lst such that either ;;; (precedes? e nth) or (not (precedes? nth e)). ;;; Better Postconditions: ;;; nth is equal to (list-ref (sort lst precedes?) n ;;; Alternate Postcondition: ;;; nth is the smallest value (define nth-smallest (lambda (n lst precedes?) (let* ((pivot (random-element lst)) (parts (partition lst pivot precedes?)) (smaller-values (car parts)) (equal-values (cadr parts)) (larger-values (caddr parts)) (num-smaller (length smaller-values)) (num-equal (length equal-values))) (cond ((< n num-smaller) (nth-smallest n smaller-values precedes?)) ((>= n (+ num-smaller num-equal)) (nth-smallest (- n num-smaller num-equal) larger-values precedes?)) (else pivot)))))