Summing a loop
After thiswhat you will be able to doCount the exact executions of a nested loop that visits each unordered pair once, and derive n(n - 1)/2 rather than calling it n squared by inspection.
Questionwhat this lesson answersEveryone calls a nested loop quadratic before anyone counts it. If we actually count how many times the inner line runs, what number do we get, and why is the honest answer only half of n squared?
Not coveredwhat this lesson leaves outWe count one shape of loop, the one that pairs each item with the later ones. We take its closed form from a pairing picture rather than proving it holds for every n.
Suppose a program has to compare every item in a list with every item that comes after it. The first item has many later partners. The last item has none. Here is one way to write that work. The two for lines repeat work. The changing numbers i and j mark which item each repeat is looking at. Such changing position numbers are called counters. A repeated instruction controlled by for is called a loop, and here one loop sits inside the other, which is called a nested loop.
for (let i = 0; i < n; i += 1) {
for (let j = i + 1; j < n; j += 1) {
compare(items[i], items[j]);
}
}
The inner counter starts at i + 1, not at zero. When i names the first item, j can name only a later one. When i moves on, pairs already compared are not revisited in the other order. The line that calls compare therefore runs once for each pair of distinct items, and never for an item paired with itself. We will count just that line. Calling every run one unit of cost is an assumption about the machine, not a discovery about the code.
Take to mean the actual number of items, numbered from zero through . At , i is zero and j would start at one, already beyond the list, so the inner line runs 0 times. At , the first item compares with the second and the second has nobody after it, so it runs 1 time. At , the rows of possible later partners have lengths 2, 1, and 0, giving . At , they have lengths 3, 2, 1, and 0, giving . The count is growing because every new item gives all earlier items one new later partner.
Ignore the initial zero for a moment and read the additions from the other end. The totals are , then , then , then . These totals run one step ahead of the loop: the total is the loop’s count for four items, not three. A running total made by adding the next whole number each time is called a triangular number. The name comes from the dots you can arrange in a triangle: one dot, then a row of two, then a row of three. Our loop has the same shape with one row shifted away, because its first row has later items, not .
There is a short expression that replaces the ever-longer addition. Such a replacement is called a closed form. Put the sum above the same sum in reverse order. Each column now adds to , and there are columns:
The two rows together are , so one row is . Written out, that is : a quadratic term with coefficient one half, plus a linear term with coefficient one half. This pairing picture explains why the triangular numbers have that value. It does not establish the claim for every possible input size. The Proof by induction door names the method that closes that gap.
The code above is one row shorter than the sum that begins at 1. Its exact count is therefore
That minus sign is the cost of being precise about j = i + 1. The actual loop still has a quadratic term with coefficient one half, together with a linear term whose coefficient is negative one half. It runs about half as many times as , not exactly . The triangular formula that begins with 1 and ends with is the same picture with the indexing moved by one. Both formulas make the same large-input claim, but they are not interchangeable when you are counting a real loop.
The picture makes the half visible. Draw an by grid whose row chooses the first item and whose column chooses the second. There are choices in which first and second position matter: choosing item A then item B occupies a different place from choosing B then A. These are ordered choices. The diagonal is the line of cells where row and column choose the same item. This loop fills only the cells above that diagonal, where . It leaves the diagonal empty because an item is not compared with itself, and it leaves the lower half empty because the reverse comparison has already happened.
There are filled cells in the square. Divide that number by to compare it with the whole square. That fraction, called a ratio, is . It rises toward one half from below. The gap that keeps it under a perfect half is , which is half of the diagonal’s empty cells: the loop skips the whole diagonal, and half of those skipped cells are what a perfect half would have included.
Four items make six later pairs
At n = 4, the 6 filled cells sit above the diagonal in a square of 16cells. The filled share is 0.375, still well below one half.
One visit per later pair
The loop fills only the upper half
A row chooses the first item and a column chooses the second. The line where both choices are the same is the diagonal. Starting j at i + 1 leaves that line, and every cell below it, empty.
Each outlined cell is one ordered item position. Filled cells are the visits made by the loop.
- Later-pair visits
- 6
- n squared cells
- 16
- Filled share of n squared
- 0.375
6 later-pair visits out of 16 possible ordered positions.
Now contrast a different inner loop, one that starts at zero and walks the full range for every outer item. It has rows, each with runs, for exactly runs. Both loops are called quadratic because each has a term shaped like . Their counts differ by a fixed multiplier, called a constant factor: the full grid does about twice as much work as the later-pair grid. The lesson on what we agree to throw away chose to stop seeing fixed multipliers. That is why calling both loops quadratic is true, and why the word hides the difference between a full square and almost half of one.
Doorswhat to read next, and why
- What a cost is made ofThis lesson leans on the words term, quadratic and linear without redefining them. The lesson that builds those words is where they come from.
- Proof by inductionThe pairing picture makes the closed form obvious for the sizes you can draw. It does not prove the formula holds for every n, and closing that gap is a method of its own.
- What we agree to throw awayThe honest count comes out as half of n squared minus half of n. Turning that into plain n squared is a separate decision, made in another lesson.
- The machine modelCounting how many times a line runs measures cost only if every run of that line costs the same. This lesson assumes that and does not check it.
Symbolswhat each one means, and whether we defined it, measured it, or just started there
- nStatus: defined
- the number of items the outer loop walks over
- i, jStatus: defined
- the two loop counters, which exist here only so we can count how often the inner line runs
- T(n)Status: defined
- the number of times the inner line runs, which is the quantity this lesson is counting
- one run of the inner lineStatus: bottoms out
- the unit being counted. Treating every run as the same size is an assumption made here and reopened by the machine model door.
What these classifications mean
- defined
- circular by construction, true because we chose it
- empirical
- a measured claim about the world that could have come out otherwise
- bottoms out
- a primitive of the model, with nothing under it here
- door
- used here, explained elsewhere