Skip to content
Adrien Hubert

Sorting, by ear.

Each bar is a number and each number has a pitch. Every time an algorithm reads or writes a bar, that pitch sounds. Heapsort keeps returning to the root, and at low speed you hear the largest remaining value leave the heap on each pass, one step lower each time. Radix sort plays the whole array once per digit without ever comparing two values.

Algorithm
Input
comparisons
0
array writes
0
floor, ⌈log₂ n!⌉
0
comparisons / floor
—

Space plays and pauses. White marks a comparison, red marks a write. Headphones help.

Note

A sort that learns only by asking "is a less than b" gets one bit per question. There are n! orderings to tell apart, so in the worst case it needs at least log₂ n! questions. For n = 128 that floor is 717. The ratio readout shows how far above it each run lands. Merge sort usually sits within a few percent of it. Insertion sort on a shuffled array lands near n²/4 comparisons, which is about five times the floor at n = 128 and grows from there.

Radix sort reads zero comparisons. It never asks which of two values is larger. It looks at one base-4 digit at a time, starting from the lowest, and deals the array into four stable piles. The floor does not apply because the question it bounds is never asked. The price is that radix sort has to know what the keys look like.

The floor is a worst case over all n! shuffles, so a single easy input can come in under it. Merge sort on a reversed array does: every merge empties one side after half the comparisons, and the ratio drops to about 0.6. The few-unique input also has far fewer than n! distinct orderings, which lowers the real floor.

Try quicksort on the few-unique input. This one uses Lomuto partitioning with the middle element as pivot, and every value equal to the pivot goes to the right. With only four distinct values the partitions stop being balanced, and the comparison count climbs toward the quadratic case. Hoare's original scheme and three-way partitioning both avoid this. I left it in because you can hear it: long runs of the same note where the work should have been split.

Shellsort uses Marcin Ciura's gap sequence 701, 301, 132, 57, 23, 10, 4, 1, which he found by computer search in 2001. It is still the usual default. Each gap is a coarse insertion sort, and the last pass with gap 1 is a plain insertion sort on an array that is already almost in order.

The idea of sonifying sorts is not mine. Timo Bingmann's Sound of Sorting did it in 2013 and is still the reference. This version is smaller and runs in the page. Each algorithm is a JavaScript generator that yields one event per comparison or write, and the frame loop decides how many events to pull per frame.

Sources

  • Knuth, D. E. (1998). The Art of Computer Programming, Vol. 3: Sorting and Searching, 2nd ed., section 5.3.1 (minimum-comparison sorting).
  • Ciura, M. (2001). Best increments for the average case of Shellsort. Fundamentals of Computation Theory, LNCS 2138, pages 106-117.
  • Hoare, C. A. R. (1962). Quicksort. The Computer Journal, 5(1), pages 10-16.
  • Williams, J. W. J. (1964). Algorithm 232: Heapsort. Communications of the ACM, 7(6), pages 347-348.
  • Bingmann, T. (2013). The Sound of Sorting. panthema.net/2013/sound-of-sorting