What is taught wrongly

The words "on average" are not a number

Quicksort is Θ(n log n) on average. Six hundred runs at n = 512 give a distribution with a mean of 4,945 comparisons, a median of 4,908, and a worst case 32% above the mean. The average is a summary of that picture, it is the least interesting thing in it, and it is almost always the only thing reported.

“Quicksort is Θ(nlogn)\Theta(n \log n) on average” is a statement about a distribution. It is delivered as though it were a statement about a number.

The distribution exists, it is not hard to measure, and it contains everything the average discards: the spread, the shape, the tail, and — the part that usually decides whether an algorithm is usable — how bad the bad case is and how often it happens.

600 runs of quicksort, random pivot at n = 512Every bar is the number of independent random inputs that cost that many comparisons. The mean is 4945; the median is 4908; the worst of 600 runs cost 6,543, which is 1.32 times the mean. The distribution is tight — a relative standard deviation of 6.8% — and skewed to the right, which is the shape that makes "on average" a defensible thing to say about this algorithm and a misleading thing to say about the version that takes the first element as its pivot.mean 494599th 60304,2825,4136,543comparisonsruns600 independent random inputs, n = 512worst run 1.32× the mean
Fig. 1 Six hundred runs of randomised quicksort at n = 512, one bar per bucket of comparison counts. The mean is a single vertical line through this. The distribution is tight — a relative standard deviation of 6.8% — and visibly skewed to the right, which is the shape that makes “on average” a defensible thing to say about this algorithm.

What the distribution looks like

Six hundred independent random inputs at n=512n = 512, randomised pivot:

statistic comparisons
best of 600 4,282
median 4,908
mean 4,945
99th percentile 6,030
worst of 600 6,543

The mean sits 0.8% above the median, which is the signature of a right skew, and the worst run is 32% above the mean. The relative standard deviation is 6.8%.

For a randomised algorithm that is a very well-behaved distribution, and the reason is worth understanding: quicksort’s total cost is a sum of many partition costs, most of which are close to balanced, so the central limit theorem applies and the total concentrates tightly around its mean. A single unlucky pivot near the top of the recursion hurts, but it is one term among many.

The distribution is nonetheless skewed. There is a floor — nothing can average below log2(n!)\log_2(n!), which at n=512n=512 is 3,875, and even the luckiest of these six hundred runs came in at 4,282 — and no ceiling short of n2/2n^2/2. So the distribution has a hard left edge and a long thin right tail, which is the shape almost every cost distribution in this subject has.

Why the tail is the interesting part

For a batch job that sorts a million arrays and reports total time, the mean is exactly the right statistic: total cost is the sum, and the sum of a million draws is the mean times a million with negligible variance.

For almost anything interactive, it is the wrong statistic, and the reason is arithmetic rather than philosophical.

A web request that performs a hundred independent sorts sees the maximum of a hundred draws from this distribution, not the mean. If each sort’s 99th percentile is 22% above its mean, then a request touching a hundred of them will almost certainly contain at least one sort at that level, and quite possibly several. Tail latency compounds with fan-out, and the more components a request touches, the more the tail of each one governs the whole.

This is why serious systems work quotes p99 and p999 rather than means, and why “average case Θ(nlogn)\Theta(n \log n)” is not a specification anyone can build a latency budget from. The distribution is what a budget needs, and the classification never carries one.

The whole range, not the mean — quicksort, random pivotAt each size, the vertical bar spans the best and worst of 200 random inputs, with the mean marked and the information-theoretic floor drawn beneath. The worst run is between 1.18 and 1.38 times the mean and that ratio does not grow with n, so the tail is keeping pace with the average rather than outrunning it. The mean sits about 1.29× the floor throughout.641282565121024204810³10⁴ncomparisonsworst runmeanfloor200 random inputs at each sizerange, not average
Fig. 2 The whole range at each size rather than the mean: the bar spans the best and worst of two hundred random inputs, with the mean marked and the information-theoretic floor beneath. The worst-to-mean ratio does not grow with n — the tail keeps pace with the average rather than outrunning it — which is a genuinely reassuring property and one that has to be measured to be known.

The tail does not grow, and that had to be checked

The measurement above answers a question the classification cannot: does the tail get relatively worse as nn grows?

It is easy to imagine that it would. More elements means more pivots means more chances for a bad one. But it also means more terms in the sum, and more terms means tighter concentration. Which effect wins is not obvious from the algorithm and is immediate from the data: the worst-to-mean ratio runs from 1.38 at n = 64 down to 1.18 at n = 2,048, so if anything the tail tightens slightly as n grows. Concentration wins.

That is a real result and it is the kind of thing an average-case bound is silent about. An algorithm whose mean was Θ(nlogn)\Theta(n \log n) and whose 99th percentile grew like n1.5n^{1.5} would satisfy every textbook statement about quicksort and be unusable in a latency-sensitive system.

The site’s figure asserts the absence of a trend — if the ratio between the largest and smallest worst-to-mean ratios ever exceeded 2, the figure would fail to build. That is the must-be-able-to-fail discipline applied to a claim about a distribution rather than about a class.

How many samples is enough

An average computed from a sample is an estimate with an error, and quoting one without knowing the error is the same category of mistake as quoting a class without naming the input.

The question is answerable directly by watching the running mean settle.

The convergence is fast because the distribution is tight. For a heavier-tailed quantity — the number of probes in a nearly-full hash table, say — the same picture would still be wandering after five hundred samples, and the honest response would be to take more or to report a percentile instead of a mean.

Showing the convergence is cheap, and it converts a bare claim of 600 trials into a demonstration that 600 was enough.

The distribution that is not tight

Randomised quicksort’s distribution is well behaved, and picking it as the example risks suggesting that distributions are usually a formality. They are not, and the contrast is available on this site.

The clean counter-example is a deterministic pivot rule measured across inputs rather than across coin flips. Quicksort taking the first element has a comparison count of 4,887 on random input at n=512n = 512 and 130,816 on a sorted one — a factor of 27, and the sorted case is not somewhere in the right tail of the random-input distribution. It is off the chart entirely, three orders of magnitude past the worst random run.

That is what a genuinely heavy-tailed cost looks like, and no amount of sampling from random inputs would ever discover it, because sorted arrays have probability essentially zero under a uniform distribution over permutations. Sampling from the assumed distribution cannot find inputs the assumed distribution does not generate, and real data is not drawn from a uniform distribution over permutations.

This is the practical difference between average-case and expected-case, and it is worth its own essay. A distribution measured over random inputs describes what happens when the data is random. It says nothing about what happens when it is not, and the ways in which real data fails to be random — sortedness, near-sortedness, few distinct values, adversarial construction — are exactly the ways that break these algorithms.

Whose average is it

There is a question underneath all of this that the word “average” is very good at hiding: averaged over what, and does the reader ever draw from it?

The expected cost of a deterministic algorithm is an average over a population of inputs. A program that sorts a fresh random array every time really does sample that population, and for it the mean is a forecast. A program that sorts the same kind of data every day — a nightly export, a log file, a list that arrives almost in order because it was almost in order yesterday — does not sample it. It draws one point and then draws it again, and if that point is in the tail it is in the tail permanently.

The measurement is easy to arrange. Two hundred runs of first-element quicksort at n=512n = 512, each on a different nearly-sorted input generated from a different seed:

algorithm input population mean min max spread
first-element pivot random 4,935 4,298 6,061 1.41×
first-element pivot nearly sorted 119,381 115,496 123,550 1.07×
random pivot nearly sorted 4,943 4,332 6,139 1.42×

The middle row is the one worth sitting with. Its spread is narrower than the random-input row — 1.07 against 1.41 — so by every measure of concentration it is the better behaved distribution. It is also twenty-four times more expensive, every single time, with no bad luck required and no good luck available.

A user whose data looks like that does not experience “quicksort is Θ(nlogn)\Theta(n \log n) on average with a long tail”. They experience a tight, reliable, quadratic cost. The average-case analysis is not wrong; it is an average over a population that includes them at negligible weight, and the answer it reports is an answer about everybody else.

The third row is what randomisation does about it. The same nearly-sorted inputs, a random pivot, and the distribution is back where the analysis says it should be — mean 4,943, spread 1.42, indistinguishable from the random-input row. The randomness has been moved out of the data, where the user controls it and usually does not have any, and into the algorithm, where it is generated fresh on every run. That is the entire trade, and it is worth a separate essay because the guarantee it produces is a different kind of statement rather than a better number.

Three things called “average”

The word covers three quite different guarantees, and conflating them is the most common error in this area.

Average case. Averaged over an assumed distribution of inputs. Quicksort with a first-element pivot is Θ(nlogn)\Theta(n \log n) in the average case, meaning: given a uniformly random permutation. If the input is sorted — which is an extremely common thing for real data to be — the guarantee does not apply and the algorithm is quadratic. This is the weakest of the three, because it depends on an assumption about the data that may not be checkable and that an adversary can violate deliberately.

Expected. Averaged over the algorithm’s own randomness. Quicksort with a random pivot is expected Θ(nlogn)\Theta(n \log n) on every input, because the randomness is in the coin flips rather than in the data. No input can force the bad case; only bad luck can. This is much stronger, and what randomisation buys is precisely the upgrade from the first guarantee to this one.

Amortised. Averaged over a sequence of operations, with no randomness at all. A dynamic array’s append is O(1)O(1) amortised: a worst-case guarantee about the total cost of any sequence. Stronger again, because no distribution is assumed and no luck is involved.

All three get written as “on average” in casual usage. The three underlying guarantees are: if the data is nice, barring bad luck, and always, over the sequence. Those are not close to the same statement.

The shape is the claim, so it is drawn three more times: at four times the size, under a different pivot rule, and on an input that is not random.

600 runs of quicksort, random pivot at n = 2048Every bar is the number of independent random inputs that cost that many comparisons. The mean is 25429; the median is 25298; the worst of 600 runs cost 31,238, which is 1.23 times the mean. The distribution is tight — a relative standard deviation of 5.3% — and skewed to the right, which is the shape that makes "on average" a defensible thing to say about this algorithm and a misleading thing to say about the version that takes the first element as its pivot.mean 2542999th 2977522,64226,94031,238comparisonsruns600 independent random inputs, n = 2048worst run 1.23× the mean
Fig. 3 Six hundred runs at n = 2,048. The mean is 25,429 and the worst of the six hundred costs 31,238 — 1.23 times the mean — with a relative standard deviation of 5.3%. Four times the array and the shape is the same shape.

The pivot rule is the second dial, and it moves the shape rather than the position — which is the distinction the word average throws away.

600 runs of quicksort, median of three at n = 512Every bar is the number of independent random inputs that cost that many comparisons. The mean is 5087; the median is 5066; the worst of 600 runs cost 5,748, which is 1.13 times the mean. The distribution is tight — a relative standard deviation of 3.1% — and skewed to the right, which is the shape that makes "on average" a defensible thing to say about this algorithm and a misleading thing to say about the version that takes the first element as its pivot.mean 508799th 55364,7545,2515,748comparisonsruns600 independent random inputs, n = 512worst run 1.13× the mean
Fig. 4 The median-of-three rule at the original size. The mean is 5,087, the worst run 5,748 — 1.13 times the mean — and the relative standard deviation is 3.1% rather than 5.3%. A better pivot rule buys a tighter distribution rather than a lower mean.

And the third dial is the input, which for a randomised pivot ought to do nothing at all. That is worth drawing rather than asserting, because it is the property the randomisation was bought for.

600 runs of quicksort, random pivot at n = 512Every bar is the number of independent random inputs that cost that many comparisons. The mean is 4950; the median is 4905; the worst of 600 runs cost 6,185, which is 1.25 times the mean. The distribution is tight — a relative standard deviation of 6.9% — and skewed to the right, which is the shape that makes "on average" a defensible thing to say about this algorithm and a misleading thing to say about the version that takes the first element as its pivot.mean 495099th 59524,3215,2536,185comparisonsruns600 independent random inputs, n = 512worst run 1.25× the mean
Fig. 5 And the random pivot on nearly sorted input rather than random. The distribution does not move, which is the whole reason the pivot is randomised: the spread is over the algorithm’s own coins and not over the data.

What a distribution figure should show

Having argued that distributions matter, it is worth being concrete about what to draw, because a histogram is not automatically informative.

The floor, if there is one. Quicksort’s distribution has a hard left edge at log2(n!)\log_2(n!) and knowing that changes how the left tail reads: the mass piled near the bottom is against a wall rather than trailing off.

A high percentile, marked. The 99th is more actionable than the maximum, because the maximum of a sample is itself a highly variable statistic — take another 600 runs and it moves — while the 99th percentile is stable.

The mean, marked, but not alone. Drawing the mean without the spread is what the classification already does.

The sample size and the seeding. A distribution from 600 runs with stated seeds is reproducible; one from Math.random() is a different distribution on every build and its caption cannot be trusted.

What randomising the pivot buys, n = 512For each pivot rule: the range of comparison counts over 400 random inputs (the bar), and the count on an already sorted array (the marker). Taking the first element as pivot costs 130,816 comparisons on sorted input — 26 times its random-input mean, and the quadratic behaviour the algorithm is supposed to avoid. Choosing the pivot at random costs 5,490 on the same input. Randomisation does not make the bad case impossible; it makes it independent of the input, so an adversary who knows the data cannot choose it.first-element pivot130,816 on sortedmedian of three5088 meanrandom pivot4937 meanblue bar: range over 400 random inputs · marker: the already-sorted inputn = 512a bad case that cannot be chosen is a different kind of bad case
Fig. 6 What the same measurement looks like when a distribution is compared against a single catastrophic point. The blue bars are the range over four hundred random inputs for each pivot rule; the markers are the cost on an already sorted array. For the first-element rule the marker is far outside its own distribution — the sorted case is not in the tail, it is off the chart — and no summary of the random-input distribution would have predicted it.

Why the shape is always the same

Almost every cost distribution in this subject has the same shape — a hard floor, a peak near it, and a long thin right tail — and the reason is structural rather than coincidental.

The floor is real. Sorting has an information-theoretic minimum that no input can get under. A hash table insertion takes at least one probe. A dynamic array append costs at least one write. Costs are bounded below by the work that must be done and unbounded above by however badly things can go.

The peak is concentration. Most of these algorithms are sums of many small random contributions — quicksort’s partition costs, a hash table’s probe counts, a random tree’s path lengths — and sums of many independent contributions concentrate. That is the central limit theorem doing what it does.

The tail is the rare compound failure. Getting an unusually bad total requires several of the contributions to be bad at once, which is rare, and the rarity falls off faster than the badness grows. Hence thin.

The practical upshot is that the mean and the median are close for almost everything here, and neither of them is the 99th percentile. Reporting the mean and reporting the median are nearly the same act; reporting either and calling it the cost is the omission.

600 runs of quicksort, first-element pivot at n = 512Every bar is the number of independent random inputs that cost that many comparisons. The mean is 4944; the median is 4889; the worst of 600 runs cost 6,137, which is 1.24 times the mean. The distribution is tight — a relative standard deviation of 6.4% — and skewed to the right, which is the shape that makes "on average" a defensible thing to say about this algorithm and a misleading thing to say about the version that takes the first element as its pivot.mean 494499th 58804,3005,2196,137comparisonsruns600 independent random inputs, n = 512worst run 1.24× the mean
Fig. 7 The same distribution shape for a different pivot rule, over random inputs. Hard left edge, peak, thin right tail — the shape is not a property of randomised quicksort in particular. What differs between rules is where the distribution sits and, crucially, what happens on inputs this picture never samples.

The arithmetic of fan-out

The claim that a request touching a hundred sorts “will almost certainly contain at least one at the 99th percentile” is exact enough to compute, and computing it turns a piece of systems folklore into a number that changes what a component should be measured against.

If each sort independently comes in below its own 99th percentile with probability 0.99, then all hundred do with probability 0.99100=0.3660.99^{100} = 0.366. Nearly two requests in three contain at least one sort in its top percentile, and the request’s own latency is the maximum rather than the sum of what it fans out to.

Turn that around and ask what a component’s percentile must be for the request to meet a 99th-percentile target. Every component must come in below its threshold with probability 0.991/100=0.99990.99^{1/100} = 0.9999, so the number to measure and budget against is the component’s p99.99, not its p99. On a hundred-way fan-out the useful statistic is four nines further out than the one anybody reports.

That is why the distribution matters more than any single percentile of it, and it is also why the shape of the distribution decides how bad the news is. Randomised quicksort’s tail is thin: its worst of six hundred runs is 32% above the mean, so even the p99.99 is within a small factor and a fan-out of a hundred costs a modest premium. A heavy-tailed component — one whose p99.99 is ten times its mean — turns the same fan-out into a system whose typical request is dominated by its unluckiest part.

So the tightness of the distribution is not a cosmetic property; it is what decides whether an architecture can fan out at all. The measurement two sections above, that the worst-to-mean ratio does not grow with nn, is exactly the property a system architect needs and exactly the one the complexity class does not carry.

A fourth thing called average

Three meanings were separated above and there is a fourth, which is the one a practitioner should usually want and which has no standing in the literature at all.

The measured case. Averaged over the inputs that actually arrive, sampled from production rather than assumed. Not a distribution somebody proved a theorem about, and not a distribution somebody hoped for — the empirical one, taken by recording real inputs and replaying them.

It is the only one of the four that is a statement about a particular deployment, and it is the only one that would have caught the nearly-sorted case in the table above before it became a problem. The average-case bound describes a population the user is not in; the expected-case bound holds but is a bound about coins rather than about the data; the amortised bound is about sequences of operations and says nothing about which inputs they act on. Recording a thousand real inputs and running them settles all three questions at once.

It is also the cheapest of the four to obtain, requiring no theory whatever, which makes its absence from most performance discussions slightly remarkable. The reason is probably that it is not portable — a measured case is a fact about one system’s data and transfers to nobody else — and the literature deals in transferable claims. That is a good reason for a textbook and a poor one for anybody choosing an algorithm for a system whose data they can look at.

The general form of the complaint

A single number summarising a distribution throws away information, and which information it throws away depends on which number is picked. That is unavoidable and it is not the problem.

The problem is that the convention in this subject is to report one summary — the average — and to report it in a notation that cannot express the others. There is no standard way to write “the 99th percentile is Θ(nlogn)\Theta(n \log n) with constant 1.13”, and so the fact goes unstated, and systems get built on a mean.

Everything on this site that reports an average also reports the distribution it came from, because the distribution is cheap to measure and it is where the surprises are.

What this makes readable

Essays that name this one as a prerequisite.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

What links here

The 8 essays that link to this one and share the most of its objects, of 31 that link here.

The objects this essay names

Each one links to every other essay that touches it.

Average caseComparison countConcentrationDistributionExpected valueGuaranteeHash tablePivotQuicksortSamplingTail latency