This lesson introduces the break statement and the while True idiom, builds Newton's method as a loop that improves an estimate until it stops changing, explains why floats must not be tested for equality, and defines what an algorithm is.
Subject: Python · 65 slides · code lesson
Open the interactive version of this deck
Title
Python · Chapter 7 — Iteration
§7.4-7.7, pp. 66-68
Objectives
Five things, each one you can check yourself at an interpreter prompt.
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 66-68 — the pages these objectives are drawn from
Warm-up
Every loop so far knew its stopping condition before it began. Find a case that does not.
Discussion prompt
Think of a task where you cannot know in advance how many repetitions it will take — only that you will recognise the end when you reach it. Describe one, and say what tells you to stop.
Hint: Searching for something, or improving something until it is good enough.
Answer:
Searching a list until you find what you want, kneading dough until it is smooth, or refining an estimate until it stops changing.
In every case the stopping condition is about what you have found, not about how many times you have gone round.
That is exactly what a while loop is for, and this lesson's main example — computing a square root by successive improvement — is the clearest case in the book: nobody knows in advance how many steps it takes.
Concept
Loops are often used in programs that compute numerical results by starting with an approximate answer and iteratively improving it. In such a loop the number of passes is not known in advance — and the loop's whole job is to recognise when it has arrived.
algorithm — A mechanical process for solving a category of problems, in which each step follows from the last according to a simple set of rules.
Newton's method is an example of an algorithm: a mechanical process for solving a category of problems, in this case computing square roots. This lesson builds it, and then asks what makes it an algorithm at all.
Figure (svg): A diagram showing an estimate being improved repeatedly until it stops changing
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 66-67
Section
Section 1
Concept
Sometimes you do not know it is time to end a loop until you get half way through the body. In that case you can use the break statement to jump out of the loop.
while True:
line = input('> ')
if line == 'done':
break
print(line)
print('Done!')| Line | Its role | Note |
|---|---|---|
| while True: | the condition is always true | the loop runs until break |
| line = input('> ') | read one line | the work happens first |
| if line == 'done': break | the real stopping condition | in the middle of the body |
| print(line) | only reached if we did not break | echoes the input |
The loop condition is True, which is always true, so the loop runs until it hits the break statement. Each time through, it prompts the user with an angle bracket; if the user types done, the break statement exits the loop.
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 66-66
Picture it
The loop cannot decide whether to stop until it has read a line, and reading is inside the body.
Figure (svg): A flow chart showing a loop that reads input, tests it, and either breaks out or prints and loops back
A while condition at the top could not do this: it would have to test a line before any line had been read.
Worked example
Try to write this loop without break and see what goes wrong.
# attempted, without break:
line = ''
while line != 'done':
line = input('> ')
print(line)| Aspect | What happens | Verdict |
|---|---|---|
| the initialization | line must be set before the first test | an artificial value |
| the last pass | prints 'done' before testing it | unwanted output |
| the break version | tests before printing | no unwanted output |
Notice the initialization problem.
Why: The condition tests line before any line exists, so line has to be given a fake starting value that means not done yet.
Notice the ordering problem.
Why: The body reads a line and then prints it, so on the final pass the word done is printed before the condition gets a chance to stop the loop.
Compare with the break version.
Why: Reading, testing and printing happen in the order the problem requires, with no artificial initialization and no unwanted output.
Figure (svg): Two columns comparing a loop with a top condition against the same loop written with while True and break
The top-tested version needs a fake initial value and prints the sentinel before stopping. The break version puts the test exactly where the decision is actually made.
Verify: Run both and compare the last two lines of output.
Why: The break version's session ends with Done!, and the other prints done first. That one extra line is the whole difference, and it is caused entirely by where the test can be placed.
Prediction
The break is in the middle of the body.
n = 0
while True:
n = n + 1
if n > 3:
break
print(n)| Pass | The test | Output |
|---|---|---|
| n becomes 1 | 1 > 3 is false | print 1 |
| n becomes 2 | false | print 2 |
| n becomes 3 | false | print 3 |
| n becomes 4 | 4 > 3 is true | break, before printing |
Predict first
What does this print?
Correct: 1, 2, 3 — the fourth pass breaks before reaching the print statement.
Why: The break jumps out of the loop immediately, so anything after it in the body is skipped for that pass. That is exactly why the break idiom is useful: the decision to stop is made in the middle, and the work after it does not happen on the final pass. Compare this with the input example, where the same placement is what stops the word done from being echoed.
Worked example
The book gives a second reason for this idiom, and it is about reading.
# negative: keep going until that happens
while line != 'done':
# affirmative: stop when this happens
while True:
...
if line == 'done':
break| Form | How it reads | Note |
|---|---|---|
| the negated form | keep going while NOT done | a double negative to read |
| the affirmative form | stop when done | reads directly |
| with several conditions | the negation compounds | much worse |
Read the negated condition aloud.
Why: While line is not done — you are stating the continuation condition, which is the opposite of the thing you actually care about.
Read the affirmative version.
Why: If the line is done, stop. You are stating the stopping condition directly.
Consider several stopping conditions.
Why: Written as a while condition they combine with and and negations; written as breaks they are simply separate if statements, each saying stop when this happens.
Figure (svg): The state of the program after each line of Worked example affirmative stop conditions, drawn as a ladder with one rung per traced line
The break form lets you express the stop condition affirmatively — stop when this happens — rather than negatively, which is the book's second reason for the idiom and the one that matters most as loops get more complicated.
Verify: Write a loop with two stopping conditions each way.
Why: As a while condition it becomes while line != 'done' and count < 100, which is two negations joined. As breaks it is two independent if statements, each reading as a sentence. The gap widens with every extra condition.
Trap
A student adopts the while True idiom and then writes the stopping logic without a break statement in it.
Treat while True as the standard way to start a loop
Why: The idiom is common, so it starts to feel like boilerplate rather than a decision.
Now the condition really is always true and nothing ever leaves. This is a genuine infinite loop, and unlike a forgotten update it looks deliberate.
while True is a promise that a break exists somewhere in the body.
Write the break before you write anything else in the body
Why: Then the exit exists from the start and cannot be forgotten.
Check that every path through the body can reach it
Why: A break inside a condition that is never true is the same as no break at all.
This is lesson 7a's requirement in a different form: the body must be able to end the loop. With a top condition that means updating a variable; with while True it means reaching a break.
Discrimination
Ask whether the stopping decision can be made before the work.
Sort into buckets
For each loop, is a top condition enough, or is a break needed?
Faded example
The test goes after the read and before the work.
Fill in the blanks
while True:
line = input('> ')
if line == '':
break
print('you typed:', line)
Why: break exits the loop immediately, so the print statement below it is skipped on the final pass — which is the point, since an empty line should not be echoed. Note the shape: read, test, work. That ordering is what the while True idiom exists to allow, and it is impossible with a condition at the top.
Socratic
The condition is literally always true. Decide whether the name applies.
Discussion prompt
An infinite loop is one whose condition never becomes false. while True has a condition that can never become false. Is every while True loop therefore infinite?
Hint: What ends a loop, besides the condition?
Answer:
No, because a break ends a loop without the condition ever being tested again. The loop has an exit; it simply is not the condition.
So the definition needs widening: an infinite loop is one that has no way to end, not one whose condition stays true. A while True with a reachable break is finite.
What makes the idiom safe is the promise it implies. Writing while True is a commitment to putting a break in the body, and a while True without one is an infinite loop in the fullest sense.
Section
Section 2
Concept
One way of computing square roots is Newton's method. Suppose you want to know the square root of a. If you start with almost any estimate x, you can compute a better estimate with a single formula — and repeating it converges on the answer.
>>> a = 4
>>> x = 3
>>> y = (x + a/x) / 2
>>> y
2.1666666666666665| Step | What happens | Value |
|---|---|---|
| x = 3 | a deliberately poor first guess | the true answer is 2 |
| (x + a/x) / 2 | the improvement formula | 2.1666... |
| repeat | using the new estimate as x | closer each time |
The result is closer to the correct answer, and if you repeat the process with the new estimate it gets closer still. In general we do not know ahead of time how many steps it takes to get to the right answer, but we know when we get there, because the estimate stops changing.
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 66-67
Picture it
Five improvements from a poor starting guess.
Figure (svg): A ladder showing successive Newton estimates for the square root of four, from three down through two point one six seven to two
Notice how fast it converges: after three steps the answer is right to four decimal places, and after five it has stopped changing altogether.
Worked example
The book's version. Notice where the test is.
while True:
print(x)
y = (x + a/x) / 2
if y == x:
break
x = y| Line | Its role | Note |
|---|---|---|
| print(x) | show the current estimate | the work |
| y = (x + a/x) / 2 | compute a better one | the improvement |
| if y == x: break | has it stopped changing? | the stopping test |
| x = y | adopt the new estimate | the update |
Notice why the test cannot be at the top.
Why: The stopping condition compares the new estimate with the old one, and the new estimate is computed inside the body. There is nothing to compare before the first pass.
Notice the order of the last two lines.
Why: The test comes before the update, so that x still holds the previous estimate when the comparison is made.
Notice what the loop is watching.
Why: Not the number of passes and not the accuracy directly, but whether the estimate has stopped changing — which is a signal the loop can actually detect.
Figure (svg): The state of the program after each line of Worked example the loop that stops when the estimate settles, drawn as a ladder with one rung per traced line
A while True loop with the test in the middle: improve, compare, and either break or adopt the new estimate. The number of passes depends on the starting guess and is not known in advance.
Verify: Try a different starting guess and count the passes.
Why: Starting from 100 rather than 3 takes several more passes for the same answer. That the count varies with the input is exactly why a while loop is the right construct — a for loop would need a number nobody can supply.
Prediction
Apply the formula once by hand.
a = 9
x = 4
y = (x + a/x) / 2| Step | Calculation | Value |
|---|---|---|
| a/x | 9 / 4 | 2.25 |
| x + a/x | 4 + 2.25 | 6.25 |
| divided by 2 | 6.25 / 2 | 3.125 |
Predict first
What is the new estimate y?
Correct: 3.125 — the average of 4 and 2.25.
Why: The true square root of 9 is 3, and the estimate has moved from 4 to 3.125 in one step. Notice the bracketing: 4 is above the answer and 9/4 is below it, so their average lands between them. One more pass gives 3.00125, and a third gives 3.0000002 — the doubling of correct digits at each step.
Worked example
It is not magic. Look at what the average is averaging.
# if x is too small, then a/x is too big
# if x is too big, then a/x is too small
# so the true root lies between x and a/x
# and their average is closer than either
# a = 4, x = 3: a/x = 1.333, average = 2.167| Quantity | Relative to the true root | Value |
|---|---|---|
| x = 3 | too big | 3 > 2 |
| a/x = 1.333 | too small | 1.333 < 2 |
| their average | between them, so closer | 2.167 |
Notice the relationship between x and a/x.
Why: If x is bigger than the square root of a, then a/x is smaller than it, and the other way round. They bracket the answer.
Take the average.
Why: A value between two numbers that bracket the target must be closer to the target than at least one of them — and in this case closer than both.
See why it converges quickly.
Why: Each step roughly halves the error and then some, which is why five steps take you from a guess of 3 to an exact 2.
Figure (svg): A number line style diagram showing x above the true root and a over x below it, with their average between them
The two quantities x and a/x always bracket the true square root, so their average is closer than either. Repeating that squeeze converges very fast.
Verify: Check the bracketing claim on the second pass.
Why: With x = 2.1667, a/x is 1.8462 — one above 2 and one below, exactly as predicted. That the bracketing survives every pass is what guarantees the method converges rather than wandering.
Trap
A student replaces the while loop with a for loop of ten passes, reasoning that ten improvements will surely be enough.
Turn an unknown count into a generous fixed one
Why: Ten looks safe, and a for loop is simpler to write.
For an easy case it wastes passes after the answer has settled; for a hard one — a huge value, or a terrible starting guess — ten may not be enough, and the loop returns a wrong answer with no indication.
Let the loop decide, by watching for the thing that actually signals completion.
Stop when the estimate stops changing
Why: That is a condition the loop can test, and it is true exactly when there is no more work to do.
Use while, not for, when the count is data-dependent
Why: In general we do not know ahead of time how many steps it takes, but we know when we get there.
This is the defining case for the while statement. A for loop needs a count in advance; a while loop needs only a way to recognise the end.
Invariant
Step through Newton's method for the square root of 4, starting from 3.
Step through it
Roughly what happens to the error at each pass, and why does that mean the loop is short?
The error is roughly squared each time — 1, then 0.17, then 0.006, then 0.00001. That is why five passes suffice from a bad guess, and why doubling the starting error costs only one extra pass rather than doubling the work.
Edge cases
The book says almost any estimate. Find the one that is not allowed.
Discussion prompt
The formula is (x + a/x) / 2. Which starting value of x would break it immediately, and what would you do about it?
Hint: Look at the division.
Answer:
Zero. The formula divides by x, so a starting estimate of zero raises a ZeroDivisionError on the first pass.
The fix is a guardian, from lesson 6c: reject a starting estimate of zero, or simply choose the starting estimate inside the function rather than accepting it — a is often a reasonable first guess, and it is never zero unless a is.
This is why the book says almost any estimate. The word is doing real work, and finding the exception is exactly the boundary-checking habit that turns a working function into a reliable one.
Real world
This shape of loop is everywhere in computing.
Discussion prompt
Newton's method starts with a guess and improves it until it settles. Name another situation — in computing or outside it — with the same shape, and say what plays the role of the improvement formula.
Hint: Anything that converges on an answer rather than calculating it directly.
Answer:
Machine learning training is the large modern example: start with random parameters, improve them a little using the error, repeat until the error stops falling.
Outside computing, adjusting a recipe by tasting, or tuning an instrument by comparing and correcting, have the same shape: measure the error, apply a correction, repeat.
What they share is that no direct formula exists, or the direct formula is harder than the iteration. That is the situation iterative improvement is for, and recognising it is worth more than the specific square-root formula.
Section
Section 3
Concept
For most values of a the equality test works fine, but in general it is dangerous to test float equality. Floating-point values are only approximately right: most rational numbers, like one third, and irrational numbers, like the square root of two, cannot be represented exactly with a float.
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False| Expression | What happens | Result |
|---|---|---|
| 0.1 + 0.2 | neither can be represented exactly | a tiny error accumulates |
| the result | 0.30000000000000004 | not equal to 0.3 |
| the comparison | False | and no error is raised |
Rather than checking whether two values are exactly equal, it is safer to use the built-in function abs to compute the magnitude of the difference between them, and to compare that with a small number.
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 67-67
Picture it
The difference is about one part in a hundred thousand billion — and equality does not care how small it is.
Figure (svg): Two columns contrasting an exact equality test on floats with a tolerance test using abs
The tolerance is not a fudge. It is a statement of what accuracy the answer needs, which is information the exact test never had a way to express.
Worked example
One line changes, and the loop becomes safe for every input.
epsilon = 0.0000001
while True:
y = (x + a/x) / 2
if abs(y - x) < epsilon:
break
x = y| Part | What it means | Note |
|---|---|---|
| abs(y - x) | the magnitude of the change | always non-negative |
| < epsilon | is the change small enough? | close enough to stop |
| epsilon | chosen by you | how close is close enough |
Compute the difference and take its magnitude.
Why: abs computes the absolute value, so the test works whether the estimate overshot or undershot.
Compare with a small number.
Why: Where epsilon has a value like 0.0000001 that determines how close is close enough.
Notice what this makes explicit.
Why: The exact test silently demanded infinite precision. The tolerance version states the required accuracy, which is a decision somebody should be making anyway.
Figure (svg): A flow chart showing the improvement loop with a tolerance test deciding whether to break
A loop that terminates for every input, with an explicit statement of how accurate the answer needs to be. The change is one line and it converts a risky loop into a reliable one.
Verify: Ask what happens if epsilon is set far too small, say 1e-300.
Why: The loop may again fail to terminate, because no achievable difference is that small. The tolerance does not remove the requirement to choose a sensible value — it makes the choice visible, which is the improvement.
Prediction
Both look like ordinary decimals.
Predict first
What does 0.1 + 0.2 == 0.3 give in Python?
Correct: False — neither 0.1 nor 0.2 can be represented exactly, so their sum is very slightly off.
Why: The sum is 0.30000000000000004, which is not 0.3. Note that no error is raised: the comparison quietly answers no, which is what makes this dangerous rather than merely surprising. This is the same shape of silent failure as comparing a string with a number in lesson 5a — a condition that is always false and nothing to tell you why.
Worked example
It worked for the square root of 4. Understand why that is misleading.
# a = 4: the estimates reach exactly 2.0 and stop
# a = 2: the true root is irrational
# the estimates may oscillate between two adjacent floats
# and y == x may never be true| Case | Why | Outcome |
|---|---|---|
| a = 4 | the answer is exactly representable | y == x eventually holds |
| a = 2 | the answer is irrational | may oscillate forever |
| the lesson | it works when the answer happens to be exact | which you cannot rely on |
Notice what made the first case work.
Why: Two is exactly representable as a float, so the sequence of estimates can land on it exactly and then stop changing.
Notice what is different about an irrational root.
Why: Most irrational numbers cannot be represented exactly with a float, so the estimates approach a value the machine cannot hold and may bounce between the two nearest representable numbers forever.
Draw the general conclusion.
Why: For most values of a this works fine — which is precisely what makes it dangerous. It fails on inputs you did not test.
Figure (svg): The state of the program after each line of Worked example why the exact test usually works, drawn as a ladder with one rung per traced line
The exact test works when the answer happens to be exactly representable, which is a property of the input rather than of the program. A test that depends on the data in that way is not a test you can rely on.
Verify: Connect this to lesson 7a's termination discussion.
Why: This is a loop whose termination depends on properties of the input that are hard to reason about — exactly the situation lesson 7a warned about. The tolerance test restores provable termination, because the difference shrinks and must eventually fall below any fixed positive epsilon.
Trap
A program checks whether a computed total equals an expected value with a plain equality test on floats.
Treat floats as though they were exact
Why: They print as though they were, and for whole-number values they behave as though they were.
0.1 + 0.2 is not 0.3, so the check fails, and the failure is invisible — the numbers look identical when printed.
Compare floats with a tolerance, and say what tolerance you mean.
Use abs of the difference against a small number
Why: abs(a - b) < epsilon asks whether they are close enough, which is the question you actually have.
Choose epsilon from the problem, not from habit
Why: How accurate does the answer need to be? A currency calculation and a physics simulation want very different values.
Integers are exempt: they are exact, and testing them for equality is fine. It is specifically floats — computed ones especially — where equality is the wrong question.
Discrimination
Exact values may be compared exactly; computed floats may not.
Sort into buckets
For each comparison, is a plain equality test safe?
Faded example
One built-in function and one small number.
Fill in the blanks
epsilon = 0.0000001
if abs(y - x) < epsilon:
break
Why: abs computes the absolute value, or magnitude, of the difference — so the test works whether the new estimate is above or below the old one. Without it, a negative difference would always be less than epsilon and the loop would break on the first pass in which the estimate decreased, which is worse than the original bug.
Explain it to yourself
It is not a magic constant. Say what it means.
Discussion prompt
Explain what the value of epsilon controls, and what would go wrong if it were set much larger — say 1.0 — or much smaller than the machine can achieve.
Hint: It is a statement about accuracy.
Answer:
It determines how close is close enough — the accuracy the answer needs to have before the loop is willing to stop.
Too large, and the loop stops early with a poor answer: with epsilon of 1.0, the square root of 4 might come back as 2.17.
Too small, and the loop may never terminate, because no achievable difference is that tiny. So epsilon is a real decision with failure modes on both sides, and choosing it means knowing what the answer is for — which is exactly the kind of thing the exact-equality test hid.
Section
Section 4
Concept
Newton's method is an example of an algorithm: a mechanical process for solving a category of problems. To understand what an algorithm is, it helps to start with something that is not one.
# not an algorithm: the multiplication table
# 100 memorized specific solutions
# an algorithm: multiplying by 9
# write n-1 as the first digit
# write 10-n as the second digit
# e.g. 7 * 9 -> first digit 6, second digit 3 -> 63| Knowledge | What it is | Verdict |
|---|---|---|
| the table | specific answers, memorised | no general method |
| the trick | a rule that works for any single digit | a general solution |
| the difference | a category of problems, not a list of answers | that is an algorithm |
When you learned to multiply single-digit numbers you probably memorised the multiplication table — in effect, one hundred specific solutions. That kind of knowledge is not algorithmic. But the trick for multiplying by 9 is a general solution for any single-digit number, and that is an algorithm.
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 67-68
Picture it
Both let you multiply by 9. Only one of them is a method.
Figure (svg): Two columns contrasting a memorised multiplication table with the general rule for multiplying by nine
One of the characteristics of algorithms is that they do not require any intelligence to carry out. They are mechanical processes where each step follows from the last according to a simple set of rules.
Worked example
Apply the rule and verify it against something you know.
# n = 7
# first digit = n - 1 = 6
# second digit = 10 - n = 3
# answer = 63
# n = 4
# first digit = 3, second digit = 6, answer = 36| Input | The two digits | Check |
|---|---|---|
| n = 7 | 6 and 3 | 63, and 7 * 9 is 63 |
| n = 4 | 3 and 6 | 36, and 4 * 9 is 36 |
| n = 9 | 8 and 1 | 81, and 9 * 9 is 81 |
Apply the rule mechanically.
Why: Subtract one for the first digit, subtract from ten for the second. No judgement is required, which is one of the defining characteristics.
Check it against known answers.
Why: Three cases, three correct results. That is evidence rather than proof, but it is the right first step.
Notice why it works.
Why: Nine times n is ten times n minus n, which is why the digits come out as n-1 and 10-n. The trick is arithmetic, not coincidence — though you can use it without knowing that, which is itself an algorithmic property.
Figure (svg): The state of the program after each line of Worked example checking the multiply-by-9 trick, drawn as a ladder with one rung per traced line
The rule produces 63, 36 and 81, all correct. It is a general solution for multiplying any single-digit number by 9, which is what makes it an algorithm rather than a memorised fact.
Verify: Test it on the one case where it needs care.
Why: For n = 1 the digits are 0 and 9, giving 09, which is 9 — correct if you allow a leading zero. Finding the edge case is what tells you whether a rule is really general, and this one survives with a small caveat about notation.
Sorting
Apply the three properties: mechanical, general, rule-following.
Sort into buckets
Sort each piece of knowledge or procedure.
Worked example
Three properties, and each excludes something.
# an algorithm is:
# 1. mechanical -- no intelligence required to carry out
# 2. general -- solves a category of problems, not one
# 3. finite -- each step follows from the last by simple rules| Property | What it excludes | Example |
|---|---|---|
| mechanical | excludes anything needing judgement | season to taste is not one |
| general | excludes memorised answers | the multiplication table is not one |
| rule-following | excludes vague instructions | handle it appropriately is not one |
Take the mechanical requirement seriously.
Why: They are mechanical processes where each step follows from the last according to a simple set of rules — so anything requiring taste or interpretation is not one.
Take the generality requirement seriously.
Why: It solves a category of problems. A single answer, however correct, is not an algorithm.
Notice the connection to lesson 1a.
Why: This is the decomposition test again: break a task down until each piece is one of the five basic instructions. A piece that still needs judgement has not been decomposed far enough.
Figure (svg): A three-stage diagram showing the three properties that make a process an algorithm
Mechanical, general, and rule-following. Between them these exclude memorised answers, tasks needing judgement, and instructions that leave a decision to the reader.
Verify: Test the definition on long division.
Why: It is mechanical, it solves any division rather than one, and each step follows by simple rules — so it qualifies, exactly as the book says. Similarly the techniques for addition with carrying and subtraction with borrowing are all algorithms.
Trap
A student writes a list of steps for a task and calls it an algorithm, even though two of the steps require a judgement call.
Treat a list of steps as the definition
Why: It is most of the definition, and the list really is a procedure.
A step like choose a reasonable value cannot be carried out mechanically, so the list cannot be followed by something with no intelligence — which is the property the book singles out.
Check that every step could be carried out by something that understands nothing.
Look for words that hide a judgement
Why: Reasonable, appropriate, suitable, enough. Each conceals a decision that has not been specified.
Replace each with a rule
Why: Choose a reasonable epsilon becomes use 0.0000001, or a rule for computing one.
This is exactly the decomposition test from lesson 1a, and the connection is not accidental: a program IS an algorithm written in a formal language, so a step Python cannot carry out is a step that has not been decomposed far enough.
Prediction
Apply the rule mechanically. No multiplication allowed.
Predict first
Using the multiply-by-9 trick, what is 6 times 9?
Correct: 54 — the first digit is 6 minus 1, which is 5, and the second is 10 minus 6, which is 4.
Why: The rule was applied without doing any multiplication, which is the point: it is a mechanical process that produces the right answer for a whole category of problems. Note that the two digits always add to 9, which is a nice check on the rule and a consequence of it being n-1 and 10-n.
Counterexample
Programs are mechanical and rule-following. Test the third property.
Discussion prompt
A program is certainly mechanical and rule-following. Give an example of a program that is nonetheless not an algorithm, and say which property it fails.
Hint: Consider a program that never finishes.
Answer:
A program with an infinite loop. It is mechanical and rule-following and it never produces an answer, so it does not solve anything.
A program that only handles one specific input also fails, on generality: print(63) computes 7 times 9 and is not a method for multiplying by nine.
So the properties are independent, and being written in a formal language is not enough on its own. That is worth noticing because it means I wrote a program and I have an algorithm are different claims.
Explain it
The book's definition is best given with its contrast.
Discussion prompt
Explain to somebody what an algorithm is, using the multiplication table and the multiply-by-9 trick as your example. Then give them one everyday thing that is definitely not an algorithm and say why.
Hint: The contrast is what makes the definition land.
Answer:
Say: memorising the times table is a hundred specific answers, and knowing the trick for nines is one rule that handles them all. The rule is an algorithm; the memorised answers are not.
The defining feature is that carrying it out requires no intelligence — each step follows from the last by a simple rule, which is why a machine can do it.
For the counterexample, the book's own is the best: understanding natural language. We all do it without conscious thought, and nobody has been able to explain how, at least not in the form of an algorithm. The things people find easiest are often the hardest to express algorithmically.
Section
Section 5
Concept
As you start writing bigger programs you might find yourself spending more time debugging. One way to cut that time is debugging by bisection: rather than checking a hundred lines one at a time, check the middle and eliminate half.
# 100 lines, checked one at a time: 100 steps
# 100 lines, halved each time:
# 100 -> 50 -> 25 -> 13 -> 7 -> 4 -> 2
# about 7 steps| Method | What each check buys | Total checks |
|---|---|---|
| one at a time | eliminate one line per check | 100 checks |
| bisection | eliminate half per check | about 7 checks |
| the difference | linear versus halving | grows with program size |
Look at the middle of the program, or near it, for an intermediate value you can check. Add a print statement and run the program. If the mid-point check is incorrect, there must be a problem in the first half; if it is correct, the problem is in the second half. Every time you perform a check like this, you halve the number of lines you have to search.
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 68-68
Picture it
Each check eliminates half of what is left.
Figure (svg): A graph comparing a linear search of n lines against a bisection search taking about log n steps
For a hundred lines it is seven checks against a hundred. For a thousand it is ten against a thousand, which is why the technique matters more the bigger the program.
Worked example
One print statement, placed deliberately, halves the search.
# a program of about 40 lines produces a wrong final answer
# step 1: find an intermediate value about halfway through
# step 2: print it and compare with what you expect
# step 3a: if it is wrong, the bug is above
# step 3b: if it is right, the bug is below| Stage | Lines remaining | Note |
|---|---|---|
| before any check | 40 lines to search | - |
| after one check | 20 lines | half eliminated |
| after two | 10 lines | and so on |
| after five | 1 or 2 lines | found |
Find a checkable intermediate value near the middle.
Why: Not literally the middle line: it does not make sense to count lines and find the exact midpoint. You need a value you can predict independently.
Print it and compare.
Why: Add a print statement, or something else that has a verifiable effect, and run the program.
Eliminate half.
Why: If the mid-point check is incorrect there must be a problem in the first half; if it is correct, the problem is in the second half.
Figure (svg): The state of the program after each line of Worked example bisecting a program, drawn as a ladder with one rung per traced line
Five or six checks for a forty-line program, instead of forty. Each check must be placed where you can say in advance what the value should be.
Verify: Notice what makes a check useful.
Why: A value you cannot predict tells you nothing, whichever half it is in. That is why the technique needs the same thing incremental development needed in lesson 6a: a test case whose intermediate values you know.
Prediction
Each check halves what is left.
Predict first
Roughly how many bisection checks does it take to narrow 1000 lines down to one?
Correct: About 10 — each check halves the range, and ten halvings take 1000 down to one.
Why: A thousand halves to five hundred, then 250, 125, 63, 32, 16, 8, 4, 2, 1 — ten steps. Compare that with a thousand checks one at a time. The saving grows as programs get bigger, which is why the book introduces the technique in the chapter that warns you your programs are getting longer.
Worked example
The book adds a practical caveat. Take it seriously.
# the ideal: the exact midpoint
# the practical rule:
# choose a spot where you think the chances are about the same
# that the bug is before or after the check| Criterion | Problem or benefit | Verdict |
|---|---|---|
| exact midpoint | not always meaningful | lines are not equal |
| easy to check | some places have no checkable value | prefer where you can check |
| equal chances | the real criterion | halve the PROBABILITY, not the lines |
Drop the arithmetic version.
Why: In practice it is not always clear what the middle of the program is, and not always possible to check it. It does not make sense to count lines and find the exact midpoint.
Use the practical rule.
Why: Think about places in the program where there might be errors and places where it is easy to put a check.
State the real criterion.
Why: Then choose a spot where you think the chances are about the same that the bug is before or after the check.
Figure (svg): A flow chart showing a bisection check dividing the search space in half and repeating
Halve the probability rather than the line count. A check placed where you can actually verify a value, and where the bug is equally likely on either side, is worth more than one at the arithmetic midpoint.
Verify: Consider what happens with a badly placed check.
Why: A check at line 3 of 40 eliminates three lines if it fails and thirty-seven if it passes — so on average it eliminates far less than half. The value of a check is highest when the two outcomes are equally likely, which is why the criterion is about probability.
Trap
A developer adds a print statement halfway through, runs the program, and looks at the number it produces.
Assume any intermediate value is a check
Why: It is information, and it came from the middle of the program.
Without knowing what the value SHOULD be, seeing it eliminates nothing. The search space is exactly as large as it was, and one more line of output has been added.
A check needs an expectation, not just a value.
Predict what the value should be before running
Why: If you cannot, choose a different place to check.
Prefer places where a hand calculation is possible
Why: Which is why the test case matters: a 3-4-5 triangle makes every intermediate value predictable.
This is the same requirement as lesson 6a's incremental development, arriving from the other direction. There you predicted values as you built; here you predict them as you search — and both fail without a test case you understand.
Ranking
Put one round of the technique in order.
Put in order
Why: Choosing the place comes first, then forming the expectation, then running, then eliminating half. The order matters because forming the expectation AFTER seeing the value is not a check at all — whatever appeared will look plausible. Predicting first is what makes the observation informative.
Estimation
For a very short program it is not. Estimate the crossover.
Predict first
Below roughly how many lines is checking one at a time as fast as bisecting?
Correct: About 5 — below that, the setup cost of choosing a check point and forming an expectation outweighs the saving.
Why: For four or five lines you can read them all faster than you can place a check, and bisecting would take two or three checks anyway. The technique earns its keep from about ten lines upward and becomes overwhelming above a hundred. Knowing where a technique stops paying is part of knowing the technique.
Real world
The same idea has many names.
Discussion prompt
Halving a search space at every step appears in several places you may already know. Name one from outside programming, and say what plays the role of the check.
Hint: Guessing a number, or finding a word.
Answer:
Guessing a number between 1 and 100 with higher-or-lower answers: seven guesses suffice, and the answer is the check.
Finding a word in a dictionary: you open it in the middle and eliminate half, which is why a dictionary of a hundred thousand words takes about seventeen openings rather than a hundred thousand.
The programming version has an extra requirement the others do not: you must be able to say what the correct value is at the point you check. In a dictionary the ordering supplies that for free, which is why the technique is easier there.
Comparison
Fill the blanks. Both are legitimate; they suit different problems.
Comparison matrix
| Question | Condition at the top | while True with break |
|---|---|---|
| Where is the test? | before the body | anywhere in the body |
| How is the condition phrased? | negatively: keep going until | affirmatively: stop when this happens |
| Can the body run zero times? | yes | no — the body always runs at least once |
| Best when | the stopping condition is known before the body runs | the body produces the thing being tested |
The bottom row is the criterion. If the thing you test is created inside the body, the test has to be inside the body too.
Pattern
Six steps, and two of them are about knowing when to stop.
Step 5 is the one that gets skipped, and it is the one that turns an occasionally-infinite loop into a reliably terminating one. Step 6's second case is what reveals whether you skipped it.
Python documentation — More Control Flow Tools More Control Flow Tools
Check
The break is before the print.
n = 0
while True:
n = n + 1
if n == 3:
break
print(n)| Pass | The test | Output |
|---|---|---|
| n = 1 | not 3 | print 1 |
| n = 2 | not 3 | print 2 |
| n = 3 | equals 3 | break, before printing |
Check your understanding
What does this print?
Answer: B
Why: break leaves the loop immediately, so the print statement after it is skipped on the pass where the break happens. Two passes print, and the third breaks before reaching the print. This placement is the whole point of break: the decision to stop is made partway through the body, and the work after it does not happen on the final pass.
Check
Both operands are computed floats.
>>> 0.1 + 0.2 == 0.3
False| Step | What happens | Result |
|---|---|---|
| 0.1 and 0.2 | neither is exactly representable | small errors |
| their sum | 0.30000000000000004 | not 0.3 |
| the comparison | False, with no error | silent |
Check your understanding
What should you write instead, to test whether two computed floats are effectively equal?
Answer: B
Why: Comparing the magnitude of the difference with a small tolerance asks the question you actually have — are these close enough — rather than demanding exact representability. The value of epsilon states how close is close enough, which is a decision the exact test had no way to express.
Check
Apply the three properties.
Check your understanding
Which of these is an algorithm?
Answer: B
Why: It is a general solution for a whole category of problems, carried out by mechanical rules that require no judgement — which is exactly the book's definition. The digits are n-1 and 10-n, and applying that needs no understanding of why it works.
Real world
Stopping conditions and tolerances are decisions everybody makes, usually without noticing.
Discussion prompt
Find a real process that stops when something is close enough rather than exactly right. What is its epsilon, who chose it, and what would happen if it were much tighter or much looser?
Hint: Manufacturing tolerances, or how precisely you measure when cooking.
Answer:
Manufacturing is the clearest: a part is made to within a stated tolerance, and that number is chosen by somebody who knows what the part is for.
Too tight and production becomes impossible or unaffordable; too loose and the parts do not fit. Exactly the two failure modes of an epsilon that is too small or too large.
The transferable point is that close enough is always a decision, and writing it as an explicit number is better than leaving it implicit. An exact-equality test in code is the equivalent of a specification demanding perfection, which is not achievable and therefore not a specification at all.
Commit first
Answer, then rate your confidence. This is the most practically important fact in the lesson.
Predict first
A loop improving a floating-point estimate uses if y == x: break. For which inputs might it fail to terminate?
Correct: Inputs whose answer cannot be represented exactly as a float — the estimates may oscillate between the two nearest representable values and never be exactly equal.
Why: Floating-point values are only approximately right, and most rational numbers like one third and irrational numbers like the square root of two cannot be represented exactly. When the target is unrepresentable, successive estimates may bounce between neighbouring floats forever, so an exact-equality test never becomes true. The book's warning is unambiguous: for most values of a this works fine, but in general it is dangerous to test float equality. The fix is one line — compare the magnitude of the difference against a small tolerance.
Explain it
The float-equality warning is the thing most worth passing on.
Discussion prompt
A classmate's loop hangs on some inputs and works on others, and it ends with if y == x: break. Explain what is happening and what to change, and give them a one-line demonstration they can type at the prompt.
Hint: The demonstration involves two very ordinary decimals.
Answer:
Say: floats are approximate, so two values that ought to be equal often differ in the last few digits, and an exact test can never become true.
Give them the demonstration: 0.1 + 0.2 == 0.3 gives False. That single line convinces people faster than any explanation, because both numbers look completely ordinary.
Then the fix: compare abs of the difference with a small number, and choose that number from how accurate the answer needs to be. The tolerance is not a workaround — it is the accuracy requirement written down, which the exact test never had a way to state.
Exit ticket
One honest answer. It decides what the next lesson opens with.
Predict first
Which of these is still least solid for you?
Correct: Whichever you picked is the right answer — this one is for you, not for a mark.
Why: break is a small piece of syntax with a good justification, and it settles quickly. Newton's method is one instance of a shape you will meet repeatedly, so the shape matters more than the formula. The float-equality warning is the highest-value item here by a distance — it is a bug people write for years, it is silent, and one line at the prompt is enough to make it stick. And the definition of an algorithm is worth revisiting whenever you write a step that quietly requires a judgement.
Connect it up
One page, from memory.
Draw it
Draw the Newton's-method loop as a flow chart, marking where the break is and why it cannot be a condition at the top. Beside it, write the exact-equality test and the tolerance test, and under each say for which inputs it terminates. Then, at the bottom, write the three properties of an algorithm and give one example that fails each property in turn.
Recap
Three pages, and chapter 7 is finished: a loop that stops when it has arrived rather than after a fixed count.
| If you remember one thing | It is this |
|---|---|
| From break | while True promises a break. Without one it really is infinite. |
| From Newton's method | A while loop is for when you cannot know the number of passes in advance. |
| From floats | 0.1 + 0.2 is not 0.3. Use abs of the difference against an epsilon. |
| From algorithms | It must be carryable-out by something that understands nothing. |
| From bisection | A check is only worth making if you know what the value should be. |
Chapter 8 goes back to a data type you have used since the first lesson and takes it apart: a string is a sequence, with a position for every character, and that single idea brings indexing, slicing, traversal, and the first encounter with something that cannot be changed.
Think Python, 2nd edition — Allen B. Downey §7.4-7.7, pp. 66-68 — everything on these slides traces back here
Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.