Pick a function, choose where Newton starts, and press Step to advance both methods one iteration at a time — or Run to let them race. The table shows each method's current estimate and how far it still is from the true root.
<p class="hint">Find where the curve crosses zero. <b>Bisection</b> (blue) traps the root in an interval and halves it — slow but it never fails. <b>Newton</b> (red) follows the tangent — fast, but it can overshoot and diverge. Race them and watch.</p>
<div class="controls">
<label>Function:
<select id="fn">
<option value="cos">cos(x) − x (root ≈ 0.739)</option>
<option value="cubic">x³ − 2x − 5 (root ≈ 2.095)</option>
<option value="atan">arctan(x) (root at 0, Newton can diverge)</option>
</select>
</label>
<label>Newton start x₀:
<select id="x0">
<option value="1">1.0 (safe)</option>
<option value="1.3">1.3 (close call)</option>
<option value="2">2.0 (danger!)</option>
</select>
</label>
</div>
<table class="race">
<thead><tr><th>step</th><th>Bisection x</th><th>error</th><th>Newton x</th><th>error</th></tr></thead>
<tbody id="rows"></tbody>
</table>
<div class="status" id="status">Press Step or Run.</div>
<div class="btns">
<button id="step" type="button">Step</button>
<button id="run" type="button">Run</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.controls { display: flex; gap: 1rem; flex-wrap: wrap; margin: .4rem 0 .7rem; font-size: .88rem; }
.controls label { display: flex; flex-direction: column; gap: .2rem; font-weight: 600; color: #1d3557; }
select { font: 500 13px system-ui, sans-serif; padding: .3rem; border: 1px solid #cdd9e3; border-radius: 6px; background: #fff; }
table.race { border-collapse: collapse; width: 100%; font: 500 13px ui-monospace, monospace; }
table.race th, table.race td { border: 1px solid #cdd9e3; padding: .3rem .5rem; text-align: right; }
table.race th { background: #e8eef3; color: #1d3557; }
table.race td:nth-child(2), table.race td:nth-child(3) { color: #1d4ed8; }
table.race td:nth-child(4), table.race td:nth-child(5) { color: #c92f3c; }
table.race tr.win-n td:nth-child(4) { background: #fde8ea; font-weight: 700; }
table.race tr.win-b td:nth-child(2) { background: #e3ecfb; font-weight: 700; }
.status { font-size: .95rem; font-weight: 600; margin: .6rem 0; min-height: 1.3em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Root-finding race: BISECTION always converges (linearly, ~1 bit/step) but is
// slow; NEWTON converges quadratically (doubles correct digits) near a simple
// root but can diverge if started badly. Both are classical, fully-proven methods.
const FUNCS = {
cos: { f: x => Math.cos(x) - x, df: x => -Math.sin(x) - 1, a: 0, b: 1, root: 0.7390851332151607 },
cubic: { f: x => x*x*x - 2*x - 5, df: x => 3*x*x - 2, a: 2, b: 3, root: 2.0945514815423265 },
atan: { f: x => Math.atan(x), df: x => 1 / (1 + x*x), a: -1, b: 2, root: 0.0 },
};
let cur, lo, hi, bx, nx, n, dead;
function reset(){
const fn = document.getElementById('fn').value;
cur = FUNCS[fn];
lo = cur.a; hi = cur.b;
bx = (lo + hi) / 2;
nx = parseFloat(document.getElementById('x0').value);
n = 0; dead = false;
document.getElementById('rows').innerHTML = '';
setStatus('Press Step or Run.', '');
addRow();
}
function setStatus(t, cls){ const s = document.getElementById('status'); s.textContent = t; s.className = 'status ' + cls; }
function fmt(v){ return Number.isFinite(v) ? v.toFixed(8) : '∞ diverged'; }
function addRow(){
const eb = Math.abs(bx - cur.root);
const en = Number.isFinite(nx) ? Math.abs(nx - cur.root) : Infinity;
const tr = document.createElement('tr');
// mark whichever method is currently closer
if (Number.isFinite(en) && en < eb) tr.className = 'win-n';
else if (eb < en) tr.className = 'win-b';
tr.innerHTML = '<td>' + n + '</td><td>' + fmt(bx) + '</td><td>' + (eb<1e-9?'0':eb.toExponential(2)) +
'</td><td>' + fmt(nx) + '</td><td>' + (!Number.isFinite(en)?'∞':(en<1e-9?'0':en.toExponential(2))) + '</td>';
document.getElementById('rows').appendChild(tr);
}
function step(){
if (dead) return false;
// --- Bisection: keep the sign-change half ---
const mid = (lo + hi) / 2;
if (cur.f(lo) * cur.f(mid) <= 0) hi = mid; else lo = mid;
bx = (lo + hi) / 2;
// --- Newton: x <- x - f(x)/f'(x) ---
if (Number.isFinite(nx)) {
const d = cur.df(nx);
if (Math.abs(d) < 1e-12) { nx = Infinity; } // flat: tangent shoots to infinity
else { nx = nx - cur.f(nx) / d; if (Math.abs(nx) > 1e6) nx = Infinity; }
}
n++;
addRow();
const eb = Math.abs(bx - cur.root);
const en = Number.isFinite(nx) ? Math.abs(nx - cur.root) : Infinity;
if (!Number.isFinite(nx)) { setStatus('Newton diverged! Bisection keeps going safely.', 'bad'); }
else if (en < 1e-9 && eb < 1e-9) { setStatus(i18nFmt('Both reached the root in %n% steps.', { n: n }), 'ok'); dead = true; return false; }
else if (en < 1e-9) { setStatus(i18nFmt('Newton nailed it in %n% steps — bisection still grinding.', { n: n }), 'ok'); }
if (n >= 40) dead = true;
return !dead;
}
function run(){ let guard = 0; while (step() && guard++ < 60) {} }
document.getElementById('step').addEventListener('click', step);
document.getElementById('run').addEventListener('click', run);
document.getElementById('reset').addEventListener('click', reset);
document.getElementById('fn').addEventListener('change', reset);
document.getElementById('x0').addEventListener('change', reset);
reset();
<p class="hint">Find where the curve crosses zero. <b>Bisection</b> (blue) traps the root in an interval and halves it — slow but it never fails. <b>Newton</b> (red) follows the tangent — fast, but it can overshoot and diverge. Race them and watch.</p>
<div class="controls">
<label>Function:
<select id="fn">
<option value="cos">cos(x) − x (root ≈ 0.739)</option>
<option value="cubic">x³ − 2x − 5 (root ≈ 2.095)</option>
<option value="atan">arctan(x) (root at 0, Newton can diverge)</option>
</select>
</label>
<label>Newton start x₀:
<select id="x0">
<option value="1">1.0 (safe)</option>
<option value="1.3">1.3 (close call)</option>
<option value="2">2.0 (danger!)</option>
</select>
</label>
</div>
<table class="race">
<thead><tr><th>step</th><th>Bisection x</th><th>error</th><th>Newton x</th><th>error</th></tr></thead>
<tbody id="rows"></tbody>
</table>
<div class="status" id="status">Press Step or Run.</div>
<div class="btns">
<button id="step" type="button">Step</button>
<button id="run" type="button">Run</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.controls { display: flex; gap: 1rem; flex-wrap: wrap; margin: .4rem 0 .7rem; font-size: .88rem; }
.controls label { display: flex; flex-direction: column; gap: .2rem; font-weight: 600; color: #1d3557; }
select { font: 500 13px system-ui, sans-serif; padding: .3rem; border: 1px solid #cdd9e3; border-radius: 6px; background: #fff; }
table.race { border-collapse: collapse; width: 100%; font: 500 13px ui-monospace, monospace; }
table.race th, table.race td { border: 1px solid #cdd9e3; padding: .3rem .5rem; text-align: right; }
table.race th { background: #e8eef3; color: #1d3557; }
table.race td:nth-child(2), table.race td:nth-child(3) { color: #1d4ed8; }
table.race td:nth-child(4), table.race td:nth-child(5) { color: #c92f3c; }
table.race tr.win-n td:nth-child(4) { background: #fde8ea; font-weight: 700; }
table.race tr.win-b td:nth-child(2) { background: #e3ecfb; font-weight: 700; }
.status { font-size: .95rem; font-weight: 600; margin: .6rem 0; min-height: 1.3em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Root-finding race: BISECTION always converges (linearly, ~1 bit/step) but is
// slow; NEWTON converges quadratically (doubles correct digits) near a simple
// root but can diverge if started badly. Both are classical, fully-proven methods.
const FUNCS = {
cos: { f: x => Math.cos(x) - x, df: x => -Math.sin(x) - 1, a: 0, b: 1, root: 0.7390851332151607 },
cubic: { f: x => x*x*x - 2*x - 5, df: x => 3*x*x - 2, a: 2, b: 3, root: 2.0945514815423265 },
atan: { f: x => Math.atan(x), df: x => 1 / (1 + x*x), a: -1, b: 2, root: 0.0 },
};
let cur, lo, hi, bx, nx, n, dead;
function reset(){
const fn = document.getElementById('fn').value;
cur = FUNCS[fn];
lo = cur.a; hi = cur.b;
bx = (lo + hi) / 2;
nx = parseFloat(document.getElementById('x0').value);
n = 0; dead = false;
document.getElementById('rows').innerHTML = '';
setStatus('Press Step or Run.', '');
addRow();
}
function setStatus(t, cls){ const s = document.getElementById('status'); s.textContent = t; s.className = 'status ' + cls; }
function fmt(v){ return Number.isFinite(v) ? v.toFixed(8) : '∞ diverged'; }
function addRow(){
const eb = Math.abs(bx - cur.root);
const en = Number.isFinite(nx) ? Math.abs(nx - cur.root) : Infinity;
const tr = document.createElement('tr');
// mark whichever method is currently closer
if (Number.isFinite(en) && en < eb) tr.className = 'win-n';
else if (eb < en) tr.className = 'win-b';
tr.innerHTML = '<td>' + n + '</td><td>' + fmt(bx) + '</td><td>' + (eb<1e-9?'0':eb.toExponential(2)) +
'</td><td>' + fmt(nx) + '</td><td>' + (!Number.isFinite(en)?'∞':(en<1e-9?'0':en.toExponential(2))) + '</td>';
document.getElementById('rows').appendChild(tr);
}
function step(){
if (dead) return false;
// --- Bisection: keep the sign-change half ---
const mid = (lo + hi) / 2;
if (cur.f(lo) * cur.f(mid) <= 0) hi = mid; else lo = mid;
bx = (lo + hi) / 2;
// --- Newton: x <- x - f(x)/f'(x) ---
if (Number.isFinite(nx)) {
const d = cur.df(nx);
if (Math.abs(d) < 1e-12) { nx = Infinity; } // flat: tangent shoots to infinity
else { nx = nx - cur.f(nx) / d; if (Math.abs(nx) > 1e6) nx = Infinity; }
}
n++;
addRow();
const eb = Math.abs(bx - cur.root);
const en = Number.isFinite(nx) ? Math.abs(nx - cur.root) : Infinity;
if (!Number.isFinite(nx)) { setStatus('Newton diverged! Bisection keeps going safely.', 'bad'); }
else if (en < 1e-9 && eb < 1e-9) { setStatus(i18nFmt('Both reached the root in %n% steps.', { n: n }), 'ok'); dead = true; return false; }
else if (en < 1e-9) { setStatus(i18nFmt('Newton nailed it in %n% steps — bisection still grinding.', { n: n }), 'ok'); }
if (n >= 40) dead = true;
return !dead;
}
function run(){ let guard = 0; while (step() && guard++ < 60) {} }
document.getElementById('step').addEventListener('click', step);
document.getElementById('run').addEventListener('click', run);
document.getElementById('reset').addEventListener('click', reset);
document.getElementById('fn').addEventListener('change', reset);
document.getElementById('x0').addEventListener('change', reset);
reset();
Watch the asymmetry. Bisection chips away steadily: the error is cut roughly in half every step, so it gains about one decimal digit every three or four iterations — and it always lands. Newton can double the number of correct digits per step, reaching machine precision in a handful of iterations. But start it near a flat spot (a point where the slope is tiny) and the tangent flings it far away — try x0 = 0 on the cubic and watch it diverge.
Comments
Loading comments...