Three Quadratic Sorts
October 27, 2009
Since we will be sorting arrays in-place, and since Scheme has awkward syntax for imperative operations on vectors, we will use Awk to implement our sorting algorithms. By choosing Awk, we continue in the fine tradition of Chapter 7 of The Awk Programming Language by Alfred V. Aho, Brian W. Kernighan and Peter J. Weinberger and several of the Columns in Jon L. Bentley’s books Programming Pearls and More Programming Pearls.
We begin with the test program, which sorts arrays of identical, sorted, reversed and random integers for all sizes of array from 0 to 20, and also for an array of size 1000, to give an idea of the efficiency of the sort:
BEGIN {
for (i=0; i<=20; i++)
tester(i)
tester(1000) }
function tester(i) {
genid(x,i); sort(x,i); check(x,i); clear(x)
gensort(x,i); sort(x,i); check(x,i); clear(x)
genrev(x,i); sort(x,i); check(x,i); clear(x)
genrand(x,i); sort(x,i); check(x,i); clear(x) }
function genid(x,n, i) {
for (i=1; i<=n; i++)
x[i] = 1 }
function gensort(x,n, i) {
for (i=1; i<=n; i++)
x[i] = i }
function genrev(x,n, i) {
for (i=1; i<=n; i++)
x[i] = n + 1 - i }
function genrand(x,n, i) {
for (i=1; i<=n; i++)
x[i] = int(n * rand()) }
function check(x,n, i) {
for (i=1; i<n; i++)
if (x[i+1]+0 < x[i]+0) {
printf "error in sort"
exit } }
function clear(x, i) {
for (i in x)
delete x[i] }
Our first sorting algorithm is bubble sort; we use a do
/while
loop so that the body is executed at least once:
function sort(A,n, i,switched,t) {
do {
switched = 0
for (i=2; i<=n; i++)
if (A[i] < A[i-1]) {
t = A[i-1]; A[i-1] = A[i]; A[i] = t
switched = 1 }
} while (switched) }
I can remember learning bubble sort over thirty years ago in my first programming class, in Fortran. I remember being amazed. Now, I am equally amazed by bubble sort, though for rather different reasons. Here is selection sort:
function sort(A,n, i,j,min,t) {
for (i=1; i<=n; i++) {
min = i
for (j=i+1; j<=n; j++)
if (A[j] < A[min])
min = j
t = A[min]; A[min] = A[i]; A[i] = t } }
And here is insertion sort; we split the swap into pieces, and move the invariant parts of the swap out of the inner loop, which makes the function much faster:
function sort(A,n, i,j,t) {
for (i=2; i<=n; i++) {
t = A[i]
for (j=i; j>1 && t < A[j-1]; j--)
A[j] = A[j-1]
A[j] = t } }
All three of these sorts have time-complexity O(n²). Of the three, bubble sort is generally the worst, and insertion sort is generally the best. Insertion sort is stable, meaning that equal elements will appear in the output in the same order as the input, which is sometimes important; insertion sort also has the property that if the input is nearly sorted, it runs very quickly, even more quickly than some more sophisticated sorting algorithms.
The code is collected at http://programmingpraxis.codepad.org/vuYNXRdL, though you can’t run it, because codepad doesn’t provide an Awk interpreter.
Clojure solution. Bubble and selection sorts are implemented over Java ArrayLists, as they really make no sense to implement in a functional manner. I’ve generalized from the requirement and allow any ordered datatype to be sorted, either using the built-in compare or using a supplied custom comparator.
The insertion sort is done functionally, and can sort over anything Clojure can turn into a sequence (any Clojure collection, ArrayLists, etc.). So as not to clutter the code further, in this case I chose to use numbers only, allowing the use of Clojure’s built-in min function.
I’m just learning to think in a functional language so this code is probably not as neat or efficient as it could be.
The functions are called with any of Clojure’s sequence data types, b-sort! and sel-sort! return sorted vectors and in-sort!
returns a sorted Lazy sequence.
By using compare we can also sort sequences containing nil items
Here goes..
I’ve done the C versions a long time ago, and I just revisited this exercise while teaching myself python. Thought I’d post these here.
and the C versions:
My c version