The floors

The floor under every comparison sort

No algorithm that sorts by comparing pairs of elements can average fewer than log₂(n!) comparisons. Not one that exists, and not one that ever will. The argument takes three sentences, it is about counting leaves in a tree, and it is one of the few results in this subject that is genuinely about every possible algorithm rather than about a particular one.

Almost everything in the analysis of algorithms is an upper bound. Somebody wrote an algorithm, somebody counted what it does, and the answer is that this much work suffices. A better algorithm might come along tomorrow, and often does.

A lower bound is a different kind of statement. It says that no algorithm does better — not the ones that exist, not the ones nobody has thought of, not the ones that will be invented in fifty years. It is a claim about an infinite set of programs, most of which will never be written, and it is not obvious that such a claim could be established at all.

For comparison sorting it can, and the argument is short enough to fit in a paragraph.

Why the floor is log₂(n!): four elements need five comparisonsEach internal node is one comparison and has two outcomes, so a run of the algorithm is a root-to-leaf path and the path's length is that run's comparison count. Every one of the 24 orderings of 4 elements must arrive at its own leaf, or two inputs needing different answers would receive the same one. The deepest tree on the left has 16 leaves. Eight orderings, in red, have nowhere to go — so no comparison sort of four elements can always finish in four comparisons, and the floor is ⌈log₂ 24⌉ = 5.every algorithm that makes at most 4 comparisonsthe 24 orderings of 4 elementsroot16 leaves16 seated · 8 with no leafone comparison per level, two outcomes per comparison⌈log₂(4!)⌉ = 5 comparisons
Fig. 1 The whole proof, for four elements. Each internal node is one comparison with two possible outcomes, so a run of any comparison sort is a path from the root to a leaf and its length is that run’s comparison count. Every one of the 24 orderings of four elements must reach a distinct leaf. A tree of depth 4 has 16 leaves. Eight orderings have nowhere to go.

The argument

Take any algorithm that sorts by comparing pairs of elements, and consider what it does on inputs of size nn.

Each comparison has two outcomes. The algorithm’s behaviour is therefore a binary tree: the root is its first comparison, each node’s two children are what it does next depending on the answer, and a leaf is where it stops and produces an ordering. A run of the algorithm is a path from the root to a leaf, and the length of that path is the number of comparisons that run made.

Now, the algorithm must be able to produce every one of the n!n! possible orderings. If two different input permutations led to the same leaf, the algorithm would produce the same output for both, and one of them would be wrong. So the tree has at least n!n! leaves.

A binary tree of depth dd has at most 2d2^d leaves, so 2dn!2^d \ge n! forces dlog2(n!)d \ge \log_2(n!).

Therefore some path is at least log2(n!)\log_2(n!) long, which is to say: some input costs the algorithm at least that many comparisons. And the average path length in a binary tree with LL leaves is also at least log2L\log_2 L, so the average over all inputs is at least log2(n!)\log_2(n!) too — which is the stronger and more useful form of the statement.

That is the whole thing. No algorithm was examined, no code was read, and the conclusion applies to every comparison sort that has ever been or will ever be written.

Reading the four-element case concretely

For n=4n = 4 there are 4!=244! = 24 orderings. 24=16<242^4 = 16 < 24, so no comparison sort can always finish four elements in four comparisons — some input will need five. 25=32242^5 = 32 \ge 24, so five might be enough, and in fact five is achievable.

The figure above is that statement drawn: a complete binary tree of depth four with all sixteen of its leaves, next to twenty-four orderings, eight of which cannot be assigned a leaf. There is no cleverness available. The eight orderings are not being handled badly; they are not being handled at all.

Worth noting what the argument does not assume. It does not assume the algorithm is deterministic in any particular way, that it compares adjacent elements, that it uses an array, or anything about how it decides which comparison to make next. It assumes only that the algorithm learns about the input exclusively through pairwise comparisons and that it must be correct on every input.

Computing the bound exactly

log2(n!)\log_2(n!) is wanted at sizes in the thousands, and the obvious implementation does not survive contact with a computer: n!n! overflows a double-precision float at n=171n = 171. Beyond that, Math.log2(factorial(n)) returns infinity.

The fix is to never form n!n!. Since log(ab)=loga+logb\log(ab) = \log a + \log b,

log2(n!)=k=2nlog2k\log_2(n!) = \sum_{k=2}^{n} \log_2 k

and summing a few thousand logarithms is instant and accurate. That is how the bound is computed here.

Accumulating thousands of floating-point additions invites error, though, so the sum is checked against an independent route. Stirling’s approximation gives

ln(n!)nlnnn+12ln(2πn)+112n\ln(n!) \approx n \ln n - n + \tfrac{1}{2}\ln(2\pi n) + \frac{1}{12n}

and dividing by ln2\ln 2 converts it. The two agree to better than five parts in ten million at every size the site uses, with the largest disagreement at the smallest nn, which is exactly where an asymptotic approximation should be worst.

The floor, computed two waysThe exact bound log₂(n!) summed term by term, Stirling's closed form laid over it, and n log₂ n above both for comparison. The exact sum and the approximation agree to better than one part in a million across this range. The gap to n log₂ n is n log₂ e, about 1.44n, which is why an algorithm at exactly n log₂ n comparisons is not optimal but is within a factor that shrinks towards 1.1010010³10⁴10010³10⁴10⁵ncomparisonsn log₂ nlog₂(n!)Stirlingthe two routes agree to 5.1e-7 relativeexact sum · Stirling
Fig. 2 The floor computed both ways, with n log₂ n drawn above for scale. The exact sum is the heavy line and Stirling’s values are the dots on top of it — they are indistinguishable at this resolution, which is the point of drawing them together. The gap between the floor and n log₂ n is n log₂ e ≈ 1.44n, which is why an algorithm sitting at exactly n log₂ n comparisons is close to optimal but not optimal.

What the bound is worth in numbers

At n=256n = 256: log2(256!)=1,684\log_2(256!) = 1{,}684 comparisons. Nothing sorts 256 elements in fewer, on average, ever.

For scale, nlog2nn \log_2 n at n=256n = 256 is 256×8=2,048256 \times 8 = 2{,}048. So the floor is about 18% below nlog2nn \log_2 n — the difference being the nlog2e1.44nn \log_2 e \approx 1.44n term, which is 369 comparisons here. That term shrinks relative to the total as nn grows, so the ratio log2(n!)/(nlog2n)\log_2(n!) / (n \log_2 n) creeps towards 1, reaching about 0.89 by n=8,192n = 8{,}192.

Which means: an algorithm whose comparison constant is 1.0 is asymptotically optimal, and one whose constant is 0.84 — as merge sort’s is — is below nlog2nn \log_2 n and much closer to the floor than to it. How close each algorithm actually gets is the natural next question and gets its own essay.

The bound in bits

There is a second way to state the theorem that some people find more intuitive, and it is the same statement.

Before the algorithm starts, the input could be any of n!n! orderings, so the algorithm’s uncertainty is log2(n!)\log_2(n!) bits. When it finishes it must have no uncertainty left — it has to know the answer. Each comparison returns one bit. Therefore at least log2(n!)\log_2(n!) comparisons.

Put that way, “the information-theoretic floor” stops being jargon. It is literally a count of bits, the algorithm is literally acquiring them one at a time, and the bound is literally the amount it needs divided by the amount each question gives.

The bit framing also makes the escape routes obvious. Beating the bound requires either a question that returns more than one bit — which is what indexing by value does, since reading a bucket index supplies log2r\log_2 r bits at once — or less uncertainty to begin with, which is what knowing the data is nearly sorted amounts to. Both routes are used by real algorithms and both step outside the theorem rather than contradicting it.

It is worth noticing how much information sorting actually requires. Sorting a million elements means acquiring about 18.5 million bits, one comparison at a time. Sorting a billion means about 29 billion bits. The numbers are large because n!n! is astronomically large, and n!n! is astronomically large because there really are that many possible answers.

Comparison counts against n, random inputMeasured counts on logarithmic axes, where a power law is a straight line and its exponent is the slope. The quadratic algorithms rise at twice the gradient of the linearithmic ones, and the vertical gaps between the parallel lines are the constants the notation discards.10010³10010³10⁴10⁵ncomparisonsMergeHeapsortQuicksorta power law is a straight line herecomparisons, counted exactly
Fig. 3 Three algorithms whose counts sit within a factor of two of the floor across this whole range. On these axes the floor would be a line just below the lowest of them, very slightly less steep — which is what “within a small constant factor of optimal” looks like when it is drawn rather than asserted.

What “comparison” is doing in the theorem

The bound applies to comparison sorts. That qualifier is not decoration, and understanding what it excludes is most of understanding the theorem.

The argument’s engine is that each comparison yields one bit. Distinguishing n!n! possibilities requires log2(n!)\log_2(n!) bits, and the algorithm cannot acquire them faster than one per comparison. If an algorithm can obtain information about the input some other way, the counting no longer applies.

Counting sort is the canonical case. Given nn integers known to lie in a range of size rr, it makes an array of rr counters, walks the input incrementing them, then walks the counters emitting values. It performs zero comparisons and finishes in Θ(n+r)\Theta(n + r) operations, which is linear when rr is O(n)O(n).

There is no contradiction. Counting sort learns about an element by using its value as an address, which is an operation the decision-tree model does not have. It also demands something ordinary sorting does not: that the keys be small integers. Radix sort, bucket sort and every other linear-time sort make a comparable demand — and pay for it in an access pattern the hardware cannot anticipate.

So the theorem’s real content is: within the class of algorithms that can only ask “is aa less than bb”, log2(n!)\log_2(n!) is a wall. Escaping it requires leaving the class, and leaving the class requires knowing something about the values. The floor moves when the question changes, and by how much is measurable.

The bound as an instrument

There is a practical use for a lower bound that has nothing to do with proving things, and it is the reason this site computes it on every build.

A measurement below the floor is a bug in the measurement. If the machinery ever reported a comparison sort averaging fewer than log2(n!)\log_2(n!) comparisons, the correct conclusion would not be that information theory is wrong. It would be that a counter is not counting — an algorithm reaching past the instrumented primitives to touch the array directly, or a primitive that forgot to increment.

That failure is otherwise nearly undetectable. Every curve would still be a curve, every fitted exponent would still be right, because a constant factor missing from every measurement does not change a slope on logarithmic axes. The floor is a check that catches it, because the floor is an absolute number rather than a relative one.

So the site’s gate averages every sorting algorithm over sixteen random inputs at n=256n = 256 and requires all ten to come in above 1,684. It also requires the best of them to come in near it — under 1.35 times — because a floor nothing approaches would be the wrong quantity to be plotting against, and the fact that merge sort lands at 1.02 is what makes the bound informative rather than merely valid.

Comparisons used, as a multiple of the floor, n = 256The information-theoretic floor at n = 256 is 1,684 comparisons: a binary decision tree with 256! leaves cannot be shallower than that. The bar is what each algorithm averaged over 16 random inputs, divided by the floor. Merge sort comes within 2% of it; Selection sort uses 19 times as many.the floorMerge sort1.02×Quicksort, first1.23×Quicksort, random1.24×Merge sort + cutoff1.29×Quicksort, median-31.32×Shellsort1.45×Heapsort1.96×Insertion sort9.69×Bubble sort19.26×Selection sort19.38×floor = log₂(256!) = 1,684 comparisonsmean of 16 runs, against a proved bound
Fig. 4 The gate’s measurement, drawn. Ten algorithms averaged over sixteen random inputs at n = 256, each as a multiple of the floor. Nothing crosses the green line. Merge sort comes within 2.2% of it. Selection sort uses nineteen times as many comparisons, which is a much more informative statement about selection sort than “quadratic”.

Averages, worst cases, and a subtlety worth not glossing

The bound as stated is about the average over all n!n! input permutations, and it is important that a particular input can be sorted in far fewer comparisons than the floor.

Insertion sort sorts an already-ordered array of 256 elements in 255 comparisons. The floor is 1,684. There is no contradiction, and the resolution is that insertion sort achieves 255 on that input by being catastrophically bad on others — its average over random inputs is 16,317, nearly ten times the floor.

The decision tree makes this precise: a tree with n!n! leaves can certainly have some short paths. What it cannot have is short paths everywhere, because there are not enough short paths to go round. Any algorithm that is fast on some inputs is paying for it on others, and the total is bounded below.

This is why the site’s floor assertions average over sixteen random seeds rather than measuring one run. Comparing a single run against an average-case bound would be comparing two different quantities, and it would fail — correctly — the first time somebody handed it a sorted array.

Insertion sort on four kinds of inputThe same algorithm, the same range of n, four input distributions. The best and worst differ by a factor of 1024 at n = 2048, so a single complexity class describes this algorithm only if the input is also stated.10010³10010³10⁴10⁵10⁶ncomparisonsalready sortednearly sortedrandomreversedadaptive — it does almost nothing to an already sorted arraycomparisons, counted exactly
Fig. 5 The subtlety, drawn. Insertion sort on four inputs: on sorted data it costs n−1 comparisons, far under the floor for that n, and on reversed data it costs n(n−1)/2. The floor is a statement about the average of these, and no algorithm can arrange to be at the bottom of this picture everywhere at once.

What the bound does not constrain

Three things, and each is a place where the theorem’s precise wording matters.

It says nothing about writes. An algorithm could sort with log2(n!)\log_2(n!) comparisons and an enormous amount of data movement, or with very few writes and many comparisons — selection sort does the second. There is no comparable floor for the write count, and the reason is that the decision-tree argument counts information acquired, which is a property of comparisons specifically.

It says nothing about time. An algorithm at the comparison floor with a terrible memory access pattern will lose on real hardware to one well above the floor with good locality. The bound is about a count, and the count is not the duration.

It says nothing about small nn in the way one might expect. The bound is exact at every nnlog24!=5\lceil\log_2 4!\rceil = 5 is a hard fact about four elements, not an asymptotic statement. That is unusual and worth appreciating: most bounds in this subject are limits, and the difference between a limit and a value causes trouble elsewhere on this site. Here there is none.

Comparisons and swaps at n = 512Selection sort performs the most comparisons of any algorithm here and among the fewest swaps — it never moves an element it does not have to. Ordering these algorithms by comparisons and ordering them by swaps gives two different orders, which is why the question "how many operations" needs the operation named before it has an answer.comparisonsswapsInsertion sort63,071 / 0Selection sort130,816 / 504Bubble sort129,688 / 62,563Merge sort3,964 / 0Heapsort7,653 / 4,170Quicksort5,049 / 2,380n = 512, random inputcounted in the same run
Fig. 6 The first of those three, drawn. Comparisons and swaps for the same algorithms and runs. The floor constrains the blue bars and has nothing at all to say about the orange ones — selection sort sits nineteen times above the comparison floor and does fewer swaps than almost anything here.

Why lower bounds are rare

Comparison sorting is one of a small number of problems where a tight lower bound is both known and easy. That is not typical, and it is worth saying why so the result is not over-generalised.

The decision-tree argument works because the output space is easy to count — there are exactly n!n! orderings — and because the model of computation is restrictive enough to bound the information gained per step. Most interesting problems have neither property. For a great many, including some very famous ones, the best known lower bound is simply that the input has to be read, which is Ω(n)\Omega(n) and usually far below the best known algorithm.

The gap between the best known upper bound and the best known lower bound is where most of the open problems in the field live. Comparison sorting is unusual in having no gap at all: the floor is log2(n!)\log_2(n!), merge sort achieves within a few percent of it, and there is nothing left to find. That completeness is rare enough to be worth appreciating when it occurs.