Answers · Making it fast enough
Why do nested loops multiply their running times instead of adding them?
In shortBecause 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.
Two nested loops multiply because the inner loop is not run once: it is run in full for every pass of the outer loop. A thousand outer passes, each containing a thousand inner passes, is a thousand groups of a thousand, which is a million.
By hand it is three lines, in this order. How many times does the outer loop repeat, in terms of the input size ? What does one pass of the outer loop cost, which is the whole inner loop? Multiply. A single pass over a list of a thousand items is a thousand steps. Put that pass inside another pass over the same list: the outer repeats a thousand times, and each of those times a fresh thousand step job runs, so the total is one thousand runs of a thousand steps. Two loops side by side are different in kind. The first finishes entirely, then the second begins, so the work is a thousand steps followed by another thousand steps. Nothing gets repeated on anybody else's behalf, and the totals are a thousand times apart.
| n | two loops in sequence (2n) | one loop inside the other (n squared) | ratio |
|---|---|---|---|
| 100 | 200 | 10,000 | 50 |
| 1000 | 2,000 | 1,000,000 | 500 |
| 10,000 | 20,000 | 100,000,000 | 5,000 |
The guess that fails is to see two loops and count two loops' worth of work, then add. That is exactly right when the loops are siblings and exactly wrong when one sits inside the other. Adding counts the inner loop as though it were paid for once, which is the cost of the first outer pass only; the remaining 999 passes each pay it again. The tell is in the numbers above. If adding were correct, the nested loop would cost 2,000 at a thousand items, and doubling the input would double the run time. It does not double. It goes from 10,000 to 1,000,000 when the input goes from a hundred to a thousand, a factor of a hundred.
Nesting that is not written as a loop
A loop over items whose body asks whether is in a list has two loops in it, and only one of them is spelled for. The membership test walks the list until it finds a match or runs out, so its cost grows with , and that cost is charged on every outer pass: passes of an step scan is . At ten thousand items that is 100,000,000 counted steps against the 10,000 you would count if you priced the test at one. Swap the list for a set and the same test becomes a constant time hash lookup, the body costs one step, and the whole thing collapses to . The two versions read identically; they differ by an entire rung of the ladder.
Where the multiplication changes shape
Three things move it. If the inner loop shrinks as the outer advances, say it runs times on pass , the total is the triangular sum below, still quadratic but with half the constant. If the loops run over different things, rows and columns, the total is , which is linear in the number of cells and quadratic only when ; this is why calling a grid traversal quadratic without saying in what starts arguments between people who are both right. And a third nesting multiplies again: a thousand cubed is a thousand million.
The rule also stops applying the moment the repetition count stops depending on what is left. A loop that removes ten items per pass from a million runs a hundred thousand times, not twenty, because removing a fixed number of items is still a linear pass whatever the constant. Only removing a fixed fraction divides what is left each time, and that is the pattern that costs the logarithm: , then , and so on down to one, is about twenty passes. Multiplication is what repetition inside repetition does; how many times the inner thing repeats is a separate question, and it is the one that decides between the rungs.
Worth keeping
- Nested loops multiply because the inner loop is charged to every outer pass, not paid once.
- Two loops in sequence add, and the smaller term is then discarded, so a linear pass bolted onto a quadratic procedure costs nothing.
- A hidden scan inside a loop, such as a list membership test, is a second loop and turns a linear procedure into a quadratic one.
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 Reading the Cost Off the Work, 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 courseOther questions people ask about this
- 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.
- Which grows faster, n log n or n squared?The 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.
- 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.
- 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.
- 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.
- 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.