Basis Lab

Answers · Making it fast enough

Which grows faster, n log n or n squared?

In shortThe squared option grows faster: doubling the input multiplies the log-times-linear option by a little over two, but multiplies the squared option by four.

n2n^2 grows faster. Doubling the input multiplies nlog⁡nn \log n by a little over two, while it multiplies n2n^2 by four, and the gap widens at every larger size.

The comparison at real sizes

Take logarithms base 2, which is the usual convention for counting halving steps. At n=1024n = 1024, log⁡2n=10\log_2 n = 10, so nlog⁡2n=1024×10=10,240n \log_2 n = 1024 \times 10 = 10{,}240. Meanwhile n2=1024×1024=1,048,576n^2 = 1024 \times 1024 = 1{,}048{,}576. The quadratic count is already about 102102 times larger. At n=1,000,000n = 1{,}000{,}000, log⁡2n\log_2 n is about 19.9319.93, so nlog⁡2nn \log_2 n is about 19,931,56919{,}931{,}569, while n2n^2 is 1,000,000,000,0001{,}000{,}000{,}000{,}000. Dividing gives a ratio near 50,17250{,}172.

FIG 1
nn log2 nn squaredn squared divided by n log2 n
824642.67
643844,09610.67
1,02410,2401,048,576102.4
1,000,00019,931,5691,000,000,000,000about 50,172

The last column is the answer at that size. At n=8n=8 the quadratic count is only 2.672.67 times the nlog⁡nn \log n count, which is why small tests can look harmless. At n=64n=64 it is 10.6710.67 times. At n=1024n=1024 it is 102.4102.4 times. At a million it is about 50,17250{,}172 times. The ratio is n/log⁡2nn/\log_2 n, and that quotient has no ceiling: as nn grows, the numerator grows while the denominator creeps up by one each time nn doubles. This is why an nlog⁡nn \log n method and an n2n^2 method can look comparable in a toy test and be in different worlds in production.

(2n)log⁡2(2n)nlog⁡2n=2(1+1log⁡2n),(2n)2n2=4.\frac{(2n)\log_2(2n)}{n\log_2 n} = 2\left(1+\frac{1}{\log_2 n}\right), \qquad \frac{(2n)^2}{n^2}=4.

The natural guess

Because nlog⁡nn \log n contains an extra logarithm, it is tempting to read it as the more decorated and therefore heavier expression. The decoration is not an extra factor of nn. Write the two as nn times something: nlog⁡2nn \log_2 n is nn times log⁡2n\log_2 n, while n2n^2 is nn times nn. For every integer n>1n > 1, log⁡2n<n\log_2 n < n, so nlog⁡2n<n2n \log_2 n < n^2. The logarithm grows very slowly: at a million, log⁡2n\log_2 n is still under 2020, which is nothing beside the million in the other factor. A constant factor can hide this at small sizes, but it cannot change which expression eventually dominates.

The doubling ratio is the quickest mental test. For n2n^2, replacing nn by 2n2n gives (2n)2=4n2(2n)^2 = 4n^2, so the cost quadruples exactly. For nlog⁡2nn \log_2 n, the same replacement gives 2n(log⁡2n+1)2n(\log_2 n + 1), which is 2nlog⁡2n2n\log_2 n plus 2n2n. The ratio is 2(1+1/log⁡2n)2(1+1/\log_2 n), so it is slightly more than 22 and falls towards 22 as nn grows. At n=1024n=1024 it is 2(1+1/10)=2.22(1+1/10)=2.2. At n=64n=64 doubling to 128128: 64log⁡264=38464 \log_2 64 = 384 becomes 128log⁡2128=896128 \log_2 128 = 896, a ratio of 2.332.33, while 40964096 becomes 16,38416{,}384, a ratio of 44. A procedure that quadruples when the input doubles is pulling away from one that a little more than doubles.

The asymptotic statement is one directional. nlog⁡nn \log n is O(n2)O(n^2) because the ratio nlog⁡2n/n2=log⁡2n/nn \log_2 n / n^2 = \log_2 n / n tends to 00. It is not true that n2n^2 is O(nlog⁡n)O(n \log n), since the reverse ratio n/log⁡2nn/\log_2 n tends to infinity. That is the formal version of the same arithmetic.

Where the ordering can reverse

The statement is about growth, not about a stopwatch at every size. At small nn, lower order terms and constant factors can make a quadratic procedure faster than an nlog⁡nn \log n procedure. Insertion sort, which is quadratic in the worst case, often beats merge sort below a few dozen items because its inner loop is tiny and merge sort allocates and copies. The crossover is a measured number, and below it the worse growth shape can be the right choice. The same caution applies to the logarithm's base: changing from base 2 to natural log multiplies nlog⁡nn \log n by a constant, which shifts timings but does not change the growth class. The formal order is still nlog⁡nn \log n below n2n^2; only the measured winner at that size changes.

At a million rows, the nlog⁡nn \log n procedure is still standing: its count is about 19.919.9 million, while the n2n^2 procedure is at a million million. If a design choice is between the two, the crossover question is only relevant below the size where constants matter; above it, the nlog⁡nn \log n side wins by the ratio n/log⁡2nn/\log_2 n.

Worth keeping

  • Doubling the input multiplies an n log n cost by a little over two and an n squared cost by four.
  • The ratio of n squared to n log n is n divided by log base 2 n, so it grows without bound.
  • At small sizes constants can make the quadratic method faster, but that is a crossover, not a change in which curve eventually wins.

Where this goes next

This question is one step of How Long It Takes, a course that works through its subject from the beginning. The page that teaches this part of it is The Shape of the Curve, and it reads the whole way through whether or not you have an account.

What an account changes is the practice: problems standing under every idea, marked with a reason rather than a tick, in a copy of the course that is yours.

Read that pageThe whole course

Other questions people ask about this

  1. Why is O(2n) treated the same as O(n)?Because it describes how cost grows rather than exact cost, doubling every cost does not change the growth class.
  2. Why do nested loops multiply their running times instead of adding them?Because the inner loop runs in full once for every pass of the outer loop, so the cost is one count times the other, not one plus the other.
  3. Why doesn't breadth-first search give the cheapest path once the links have different costs?Breadth-first search counts links rather than costs, so it can return a route with fewer links that is more expensive than a longer route.
  4. How does a hash function turn a word into an array index?A hash function reads the word as bytes, mixes them into one large number, then takes the remainder after dividing by the table size to get an index from zero to size minus one.
  5. How can you check whether something is in a set without storing every item?Record a few hashed marks per item in a bitmap; all marks set means probably present, any mark clear means certainly absent.
  6. What's the difference between memoized recursion and filling a DP table bottom-up?Memoised recursion keeps the calls and only solves states it reaches; a bottom-up table removes the calls, fills every entry in a dependency-safe order, and is usually faster when most entries are needed.

Read alongside