Accumulators and Running Totals
A two-dimensional array is traversed with nested loops: the outer loop chooses a row and the inner loop visits each column of that row.
Try it: Loop Tracer
How a Python for loop visits each item in turn, and how an accumulator variable (a total, a count, a best-so-far or a result list) changes on every iteration.
How it works
- Initialise the accumulator before the loop.
- Each iteration, the loop variable takes the next value from the list.
- An if inside the loop decides whether to update the accumulator.
- After the last item the loop ends and the accumulator holds the answer.
Default run (13 steps): values = [4, 9, 2, 7, 5]. Run the loop one statement at a time. … The loop has used every value. print(total) shows 27.
Simplified: A model of five fixed loop programs (sum, count, maximum, minimum, filter) run on your list — it steps the program exactly as Python would, but it does not run Python.
Loading the simulation…
A Grid of Store Profits
Imagine a table of profit values with one row for each store and one column for each month. The company needs a single number: its total profit across every store and every month. A program that computes this number has to look at every cell in the grid, one at a time, and carry a running sum from the first cell to the last. This article follows that movement step by step, so that you can predict what the program does rather than guess.
Before reading any more code, make a prediction. Take a smaller grid with two rows and three columns. The first row holds the values 1, 2, 3 and the second row holds 4, 5, 6. If a program adds up every cell exactly once, what should the total be? Commit to an answer first, because the reasoning you use to guess is the same reasoning the nested loops perform.
What do you think happens?
Adding every cell of the 2 by 3 grid exactly once, what total should the program produce?
Reveal answer
21
Row 0 holds 1, 2, and 3, which add to 6. Row 1 holds 4, 5, and 6, which add to 15. Together they give 6 + 15 = 21. Your guess was right if you counted each cell once, and the nested loops are built to do exactly that.
Walking the Grid Cell by Cell
A two-dimensional array is traversed with nested loops. The outer loop chooses a row, and the inner loop visits each column of that row. In the source example, the outer loop controls the store index and runs from 0 up to but not including 25. The inner loop controls the month index and runs from 0 up to but not including 12. For every single value of the store index, the inner loop runs through all twelve month indices before the outer loop moves on to the next store.
Because each loop stops before its bound, the valid indices are 0 through 24 for stores and 0 through 11 for months. Those two ranges together address 25 times 12, or 300, cells. The diagram below applies the same pattern to the smaller 2 by 3 grid. Notice that the month, or column, changes fastest: the program finishes every column of row 0 before it begins row 1.
The rule to remember is that the outer loop moves slowly and the inner loop moves quickly. Each full pass of the inner loop covers one complete row, and the outer loop advances only after that row is finished. This is why every cell is reached exactly once: the pairs of row and column indices are generated in a fixed order with no pair skipped and none repeated.
How Row and Column Indices Address a Cell
The two index variables work together as a pair of coordinates. The first index selects a row, which in the store data means a store. The second index selects a column within that row, which means a month. The expression profit[store][month] names one specific cell. The outer loop variable supplies the first coordinate because it decides which row is currently active, and the inner loop variable supplies the second coordinate because it sweeps across the columns of that row.
0 0 1
0 1 2
0 2 3
1 0 4
1 1 5
1 2 6- The outer loop variable store chooses the row.
- The inner loop variable month chooses the column within that row.
- Each printed line shows one row and column pair and the value stored there.
The Accumulator and Its Starting Value
The total is held in a single variable, named totalProfit in the source. Before the loops start, the code sets totalProfit to 0. The loops never replace it; they only ever add to it. Inside the inner loop, the statement totalProfit += profit[store][month] reads the current value of totalProfit, adds the value of the cell at that row and column, and stores the new sum back into totalProfit. This pattern is called accumulation, and the variable that holds the running result is called an accumulator.
The statement totalProfit += value is shorthand for totalProfit = totalProfit + value. Read it as: take what the accumulator currently holds, add the new cell, and keep the result. Because each step builds on the one before, the value of the accumulator at any moment is the sum of every cell visited so far. The diagram below traces that value through the 2 by 3 grid, cell by cell.
Notice that the accumulator never forgets its earlier values. After cell 3 it holds 6, which is 1 plus 2 plus 3. The next addition does not start from zero; it starts from 6. This is what makes a running total useful: at every point in the loop, the variable reports the total so far.
The starting value matters just as much as the additions. The loops only add to the accumulator, so whatever it holds before the first addition is carried into the final answer. Starting at 0 means nothing extra is included. Starting at 1, or at a leftover value from an earlier run of the program, adds that extra amount to every total the program produces.
totalProfit = 0
for store in range(2):
for month in range(3):
totalProfit += profit[store][month]
print(totalProfit)The accumulator starts at 0, so the six cells add up to 21.
totalProfit = 1
for store in range(2):
for month in range(3):
totalProfit += profit[store][month]
print(totalProfit)The accumulator starts at 1, so the same six cells add up to 22. The extra 1 was never removed.
21totalProfitis set to 0 before any loop runs.- The += statement runs once per cell, six times in total.
- The print statement runs after both loops finish, so it reports the final accumulated value.
Mistakes That Corrupt a Running Total
Wrong totals usually come from one of a few specific places. Each of the mistakes below has a recognizable symptom, so knowing them in advance makes debugging faster.
Common mistake Forgetting to initialize the accumulator
What it looks likeUsing totalProfit += profit[store][month] without first setting totalProfit = 0, or reusing a variable that still holds a value from an earlier calculation.
Why it failsThe loops only add to the accumulator, so any leftover value is carried into the final total.
Do this insteadSet the accumulator to 0 immediately before the loops start.
Common mistake Swapping the row and column indices
What it looks likeWriting profit[month][store] when the outer loop runs over stores, which is valid only when the grid happens to be square in the right way, or fails with an index error otherwise.
Why it failsThe first index selects the row and the second selects the column. Swapping them reads cells in the wrong place or reads positions that do not exist.
Do this insteadKeep the outer loop variable as the first index and the inner loop variable as the second, matching how the grid is laid out.
Common mistake Using an inclusive loop bound
What it looks likeWriting a loop that goes up to and including the number of rows, such as a condition that allows store to equal 25 in the source example.
Why it failsThe index 25 does not exist in a grid of 25 rows, so the loop reads past the end or adds a value that should not be counted.
Do this insteadCompare against the size with a strict less-than, so the indices run from 0 to size minus 1.
Common mistake Placing the addition in the wrong loop
What it looks likePutting totalProfit += profit[store][month] in the outer loop instead of the inner loop, where month is not yet defined for each cell.
Why it failsThe addition must run once for every cell. Placed in the outer loop, it adds at most once per row and skips the other cells in that row.
Do this insteadPlace the accumulation statement inside the inner loop, where both indices identify a single cell.
Checkpoints for Finding the Error
When a total is wrong, the most useful question is where the running value first stops matching what you expected. You answer that by checking the accumulator at well-chosen points. For a grid organized by store, the natural checkpoint is the end of each outer loop iteration, because that is the point where a full store's months have been added.
Compare the running total at each checkpoint against a hand calculation. If the value after store 0 is already wrong, the error lies in that row's cells, the starting value, or the indices used for that row. If the value after store 0 is correct but the final total is wrong, the error lies in a later row or in the final loop boundary. This narrows the search to a single row instead of the whole grid.
Trace a Smaller Grid
A program stores profits for a 3 by 2 grid, with three rows and two columns, where the rows hold 2, 4 and 6, 8, 10 and 12 respectively. Trace the accumulator through every cell by hand, writing down its value after each addition. Then state the final total, and say which row boundary you would check first if the program printed 50 instead.
Hints
- Count the rows and columns first: the outer loop runs over 3 rows and the inner loop runs over 2 columns.
- Add the values in order, row by row, and write the running total after each addition.
- The correct total is the sum of all six values. Compare that with 50 and check the running total at the end of each row.
Key Takeaways
- A two-dimensional array is traversed with nested loops: the outer loop chooses a row and the inner loop visits each column of that row, so every cell is visited exactly once.
- An accumulator such as
totalProfitmust start at 0, and the statementtotalProfit+= profit[store][month] adds each cell into it, carrying a running sum from the first cell to the last. - Loop bounds should be strict comparisons against the number of rows and columns so that every index from 0 to size minus 1 is valid and visited once.
- Wrong totals usually come from an uninitialized accumulator, swapped indices, an inclusive bound, or an addition placed in the wrong loop, and checking the running total at the end of each row locates the error.