5.4.25 · D4Scientific Computing (Python)

Exercises — Implementing root-finding from scratch — Newton-Raphson, bisection

3,263 words15 min readBack to topic

Level 1 — Recognition

Exercise 1.1

You are given and asked to find a root by bisection. Someone hands you three candidate intervals: , , . Which one is a valid bracket, and which method requirement decides it?

Recall Solution

What we need: a bracket is valid only if — the function changes sign, so by the Intermediate Value Theorem a continuous curve must cross zero inside.

Evaluate the endpoints:

  • , → product . Not valid (both negative — the curve may never dip to zero here).
  • , → product . Valid ✓ (negative → positive, a guaranteed crossing).
  • , → product . Not valid.

Answer: only brackets a root.

The figure below shows the curve with this valid bracket, its first bisection midpoint (plum), and the Newton tangent from Exercise 2.2 (orange) — glance at it now to see how the two methods approach the same crossing differently.

Figure — Implementing root-finding from scratch — Newton-Raphson, bisection

Notice how the plum midpoint at sits close to zero already (because the curve is nearly straight here), while the orange tangent's x-intercept from jumps almost onto the true root in one leap.

Exercise 1.2

Match each situation to the better first choice — bisection or Newton-Raphson:

  • (a) You have but no formula for , and a known sign change.
  • (b) You have , , and a starting guess already very close to the root.
  • (c) is nearly zero right where you'd start.
Recall Solution
  • (a) Bisection. Newton needs a derivative; bisection only needs and a sign change.
  • (b) Newton. Close start + derivative = quadratic convergence, few steps.
  • (c) Bisection. A tiny makes the Newton step explode — the tangent is almost flat and its x-intercept flies far away.

Level 2 — Application

Exercise 2.1

Do two bisection steps on over . Report the bracket after each step and the midpoint you'd return if you stopped.

Recall Solution

Store (negative), (positive).

Step 1: midpoint . Compute (negative). Compare with the stored : same sign on the left, so the crossing is on the right half. New bracket: , and now .

Step 2: midpoint . Compute (positive). sign change on the left, keep left half. New bracket: .

If you stopped now you'd return the midpoint . (The true root is .)

Exercise 2.2

Do two Newton steps on the same , with , starting at .

Recall Solution

Update rule: .

Step 1 (): , .

Step 2 (): ; .

Two Newton steps got us to (true root ); two bisection steps only reached . Newton pulls ahead fast once the guess is near.


Level 3 — Analysis

Exercise 3.1

Bisection on (width ). How many steps guarantee the returned midpoint is within of the true root? First derive the error bound geometrically, then solve for the step count.

Recall Solution

Deriving the bound (the geometric "why"): Start with interval width . Each bisection step cuts the live interval exactly in half, because we split at the midpoint and keep one side. So the width after steps is: Now — where inside that final interval is the true root? We return the midpoint of the surviving interval. The root is somewhere in the interval, and the midpoint sits dead-centre, so the worst the root can be from the midpoint is half the interval width (root at one end, midpoint in the middle). That is why the error carries the extra factor of two: The is not a magic constant: the counts the halvings, and the extra is the "midpoint is at most half a width away" geometry.

Solving for the step count. We require: Take : . So , meaning steps suffice. (Each step buys one bit; .)

Exercise 3.2

Newton on starting at has error . Derive the quadratic-convergence estimate from Taylor Series, then use it to predict and compare to the actual next error.

Recall Solution

Deriving from Taylor's theorem. Let be the error, where is the true root (). Taylor-expand and about , keeping terms up to : (the term vanishes because is a root), and The next Newton error is . Substitute the two expansions: Factor out of the numerator and out of the denominator: Now use for small (geometric-series approximation of the denominator). With and : And . So the constant is: This is why has that exact form — it is the ratio of the leading curvature term to the slope , straight out of the Taylor remainder.

Applying it here. For : so ; so . Predicted .

Actual: , so .

Predicted vs actual — same order, ~6% apart (the difference is the neglected higher-order terms we dropped in the expansion). The prediction confirms the squaring behaviour: became , roughly scaled — the digits of accuracy doubled.


Level 4 — Synthesis

Exercise 4.1

Write a safeguarded Newton step function: given a current bracket with a sign change and a current guess , take a Newton step; if it lands outside (or is zero), fall back to a bisection midpoint. Explain each guard. (Pseudocode + one hand trace.)

Recall Solution
def safe_step(f, df, a, b, x, fa):
    dfx = df(x)
    if dfx != 0:
        xn = x - f(x) / dfx        # try Newton
        if a < xn < b:             # accept only if it stays inside the bracket
            x_new = xn
        else:
            x_new = (a + b) / 2    # Newton escaped -> bisect
    else:
        x_new = (a + b) / 2        # flat tangent -> bisect
    # shrink the bracket using the new point's sign
    fx = f(x_new)
    if fa * fx < 0:
        b = x_new                  # sign change on the left half
    else:
        a, fa = x_new, fx          # sign change on the right half
    return a, b, x_new, fa

Why each guard:

  • dfx != 0: a flat tangent gives an infinite/undefined step — the exact failure mode of Newton. Fall back to bisection, which never divides by a slope.
  • a < xn < b: if Newton's tangent points out of the trapped region, the root can't be there — the guess is worse than a blind midpoint. Reject it.
  • Updating a/b with the sign test keeps a valid bracket at all times, so we inherit bisection's guarantee while enjoying Newton's speed when it behaves.

Hand trace on , , start , :

  • , Newton , which lies inside accept. ; → shrink right end: bracket becomes .

This is exactly the philosophy behind scipy.optimize.brentq: keep a bracket, use fast interpolation inside it, bisect when the fast step misbehaves.

Exercise 4.2

The parent note mentions the Secant method as a derivative-free cousin of Newton. Derive its update from Newton's by replacing with a finite difference using the two most recent points . State what it costs and gains versus Newton — and identify the edge case that can break the update, with a guard.

Recall Solution

Newton is . When we have no formula for , approximate the slope by the line through the last two points (a finite difference, closely related to the definition of a derivative): Substitute: Gain: no derivative needed, and only one new -evaluation per step (Newton needs and ). Cost: convergence order drops from to the golden ratio — still superlinear, faster than bisection, slower than a true Newton. It also inherits Newton's fragility (no bracket guarantee).

The edge case — vanishing denominator. The formula divides by . If the two most recent function values are equal (or nearly so), this denominator is zero (or tiny) and the step blows up or crashes with a division error. Geometrically this is a horizontal secant line: a flat line never crosses the x-axis, so "where does the secant hit zero?" has no finite answer — the exact analogue of Newton's flat-tangent failure. It happens near a local extremum, or when two iterates accidentally land at the same height.

Guard:

def secant_step(f, x_prev, x_cur, f_prev, f_cur, a, b):
    denom = f_cur - f_prev
    if denom == 0:                        # horizontal secant -> no intercept
        return (a + b) / 2                # fall back to a bracketed bisection
    return x_cur - f_cur * (x_cur - x_prev) / denom

At mastery level the same lesson from Exercise 4.1 applies: the raw secant is fragile, so production code safeguards it — check the denominator, and keep a bracket to fall back on when the secant point escapes or the denominator collapses. This denominator guard plus a bracket fallback is precisely why real solvers such as scipy.optimize.brentq never divide by a degenerate slope.


Level 5 — Mastery

Exercise 5.1 (Newton's runaway)

For (the cube root, with root ), show algebraically that Newton diverges from any nonzero start, and explain geometrically why. Then state which method you'd use instead.

Recall Solution

Here so . The Newton step: So each iteration gives : the guess doubles in magnitude and flips sign every step. From : — it flies away from forever.

Geometry: has an infinitely steep tangent at the root (a vertical inflection). Away from the tangent is so shallow that its x-intercept lands farther out than where you started. The figure below sketches this runaway — follow the three dashed tangents (orange, plum, teal): each one's x-intercept (the triangle marker) sits farther from the root than the point it came from.

Figure — Implementing root-finding from scratch — Newton-Raphson, bisection

Read the figure left-to-right: produces an intercept at , whose tangent produces an intercept at , and so on — the markers march outward in both directions, never toward the central star at .

Fix: use bisection. With any bracket like (signs and ), it marches inexorably toward . This is the parent note's headline warning: bisection is guaranteed, Newton is not.

Exercise 5.2 (Degenerate bracket)

A caller runs bisection(f, a, b) where by bad luck exactly. Trace what the parent's code does, and explain why the endpoint check must come before the sign-product test.

Recall Solution

Recall the parent code:

if fa == 0:
    return a
...
if fa * fb > 0:
    raise ValueError(...)

With : the first guard fa == 0 fires and returns a immediately — correct, it's an exact root.

Why order matters: if we had checked fa * fb > 0 first, note , which is neither nor . It fails the strict bracket test (> 0 is False, so no error raised) but the loop's termination (b-a)/2 < tol might never trigger a return of the true root, and worse, the sign-update logic assumes a genuine strict sign change. A zero endpoint breaks that assumption. Checking endpoints first sidesteps the whole degenerate case cleanly. This is exactly the second steel-man in the parent note.

Exercise 5.3 (Floating-point stopping)

Newton on reaches with while the step . Your tolerance is . Should the loop stop? What does Floating point arithmetic tell us about pushing tolerance to ?

Recall Solution

The step is larger than , so the abs(step) < tol test is False → the loop continues one more iteration. (After that next step it will almost certainly drop below tol and stop.)

On pushing to : impossible to reliably meet. Double-precision floats carry about significant decimal digits; the spacing between representable numbers near (the machine epsilon scale, times the value) is roughly . You cannot resolve a step smaller than that gap — the loop would spin to maxit without ever satisfying . Set tolerances at or above a few times machine epsilon; asking for more precision than the number system holds is a guaranteed non-termination bug.