Below is the curve f(x) = x2 − 2, whose positive root is √2 ≈ 1.41421356. Drag the slider to choose a starting guess, then press Step to take one tangent jump at a time, or Run to watch it converge.
<p class="hint">Curve <b>f(x) = x² − 2</b>. The positive root is √2 ≈ 1.41421356.
Pick a starting guess, then press <b>Step</b> to slide down one tangent at a time, or
<b>Run</b> to watch it converge. Finding the root is easy; what is striking is the speed.</p>
<canvas id="cv" width="460" height="260"></canvas>
<div class="row">
<label>Start guess x₀: <b id="x0lbl">3.00</b></label>
<input id="x0" type="range" min="0.4" max="4" step="0.05" value="3">
</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>
<table id="log"><thead><tr><th>n</th><th>xₙ</th><th>correct digits</th></tr></thead><tbody></tbody></table>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
canvas { width: 100%; max-width: 460px; height: auto; background: #f5f8fb;
border: 1px solid #cdd9e3; border-radius: 8px; display: block; }
.row { display: flex; align-items: center; gap: .6rem; margin: .7rem 0 .3rem; font-size: .9rem; }
.row input { flex: 1; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin: .4rem 0; }
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; }
button:disabled { opacity: .5; cursor: default; }
table { border-collapse: collapse; width: 100%; max-width: 460px; margin-top: .6rem; font-size: .85rem; }
th, td { border: 1px solid #cdd9e3; padding: .3rem .5rem; text-align: right; font-variant-numeric: tabular-nums; }
th { background: #e8eef3; color: #1d3557; }
td:first-child, th:first-child { text-align: center; }
// Newton's method on f(x) = x^2 - 2. One step: x <- x - f(x)/f'(x).
// For this f that is x <- x - (x*x - 2)/(2x) = (x + 2/x)/2 (the "Babylonian" mean).
// Near a simple root the error squares each step => correct digits roughly double.
const ROOT = Math.sqrt(2);
const f = x => x * x - 2;
const df = x => 2 * x;
function newtonStep(x) { return x - f(x) / df(x); }
function correctDigits(x) {
const err = Math.abs(x - ROOT);
if (err === 0) return 16;
return Math.max(0, -Math.log10(err / Math.abs(ROOT)));
}
const cv = document.getElementById('cv'), ctx = cv.getContext('2d');
const X0 = -0.2, X1 = 4.2, Y0 = -3, Y1 = 8; // world window
const sx = x => (x - X0) / (X1 - X0) * cv.width;
const sy = y => cv.height - (y - Y0) / (Y1 - Y0) * cv.height;
function drawAxes() {
ctx.clearRect(0, 0, cv.width, cv.height);
ctx.strokeStyle = '#cdd9e3'; ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(0, sy(0)); ctx.lineTo(cv.width, sy(0)); // x-axis
ctx.moveTo(sx(0), 0); ctx.lineTo(sx(0), cv.height); ctx.stroke(); // y-axis
// root marker
ctx.strokeStyle = '#0a7d33'; ctx.setLineDash([4, 3]);
ctx.beginPath(); ctx.moveTo(sx(ROOT), 0); ctx.lineTo(sx(ROOT), cv.height); ctx.stroke();
ctx.setLineDash([]);
// curve
ctx.strokeStyle = '#1d3557'; ctx.lineWidth = 2; ctx.beginPath();
for (let i = 0; i <= 200; i++) { const x = X0 + (X1 - X0) * i / 200;
const px = sx(x), py = sy(f(x)); i ? ctx.lineTo(px, py) : ctx.moveTo(px, py); }
ctx.stroke();
}
function drawStep(x) {
const xn = newtonStep(x);
// tangent at x, drawn down to where it hits y = 0 (that point is xn)
ctx.strokeStyle = '#e63946'; ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.moveTo(sx(x), sy(f(x))); ctx.lineTo(sx(xn), sy(0)); ctx.stroke();
// current point on curve
ctx.fillStyle = '#e63946';
ctx.beginPath(); ctx.arc(sx(x), sy(f(x)), 4, 0, 7); ctx.fill();
// landing point on the axis
ctx.fillStyle = '#0a7d33';
ctx.beginPath(); ctx.arc(sx(xn), sy(0), 4, 0, 7); ctx.fill();
}
let x = 3, n = 0, timer = null;
function logRow() {
const tb = document.querySelector('#log tbody');
const tr = document.createElement('tr');
tr.innerHTML = '<td>' + n + '</td><td>' + x.toFixed(8) + '</td><td>' +
correctDigits(x).toFixed(1) + '</td>';
tb.appendChild(tr);
}
function redraw() { drawAxes(); drawStep(x); }
function reset() {
if (timer) { clearInterval(timer); timer = null; }
x = parseFloat(document.getElementById('x0').value); n = 0;
document.querySelector('#log tbody').innerHTML = '';
logRow(); redraw();
}
function step() {
if (Math.abs(x - ROOT) < 1e-12) return;
x = newtonStep(x); n++; logRow(); redraw();
}
function run() {
if (timer) return;
timer = setInterval(() => {
if (n >= 8 || Math.abs(x - ROOT) < 1e-12) { clearInterval(timer); timer = null; return; }
step();
}, 650);
}
document.getElementById('x0').addEventListener('input', e => {
document.getElementById('x0lbl').textContent = parseFloat(e.target.value).toFixed(2);
reset();
});
document.getElementById('step').addEventListener('click', step);
document.getElementById('run').addEventListener('click', run);
document.getElementById('reset').addEventListener('click', reset);
reset();
console.log('root', ROOT, 'from x0=3:', [0,1,2,3,4].reduce((a)=>a, 0));
<p class="hint">Curve <b>f(x) = x² − 2</b>. The positive root is √2 ≈ 1.41421356.
Pick a starting guess, then press <b>Step</b> to slide down one tangent at a time, or
<b>Run</b> to watch it converge. Finding the root is easy; what is striking is the speed.</p>
<canvas id="cv" width="460" height="260"></canvas>
<div class="row">
<label>Start guess x₀: <b id="x0lbl">3.00</b></label>
<input id="x0" type="range" min="0.4" max="4" step="0.05" value="3">
</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>
<table id="log"><thead><tr><th>n</th><th>xₙ</th><th>correct digits</th></tr></thead><tbody></tbody></table>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
canvas { width: 100%; max-width: 460px; height: auto; background: #f5f8fb;
border: 1px solid #cdd9e3; border-radius: 8px; display: block; }
.row { display: flex; align-items: center; gap: .6rem; margin: .7rem 0 .3rem; font-size: .9rem; }
.row input { flex: 1; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin: .4rem 0; }
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; }
button:disabled { opacity: .5; cursor: default; }
table { border-collapse: collapse; width: 100%; max-width: 460px; margin-top: .6rem; font-size: .85rem; }
th, td { border: 1px solid #cdd9e3; padding: .3rem .5rem; text-align: right; font-variant-numeric: tabular-nums; }
th { background: #e8eef3; color: #1d3557; }
td:first-child, th:first-child { text-align: center; }
// Newton's method on f(x) = x^2 - 2. One step: x <- x - f(x)/f'(x).
// For this f that is x <- x - (x*x - 2)/(2x) = (x + 2/x)/2 (the "Babylonian" mean).
// Near a simple root the error squares each step => correct digits roughly double.
const ROOT = Math.sqrt(2);
const f = x => x * x - 2;
const df = x => 2 * x;
function newtonStep(x) { return x - f(x) / df(x); }
function correctDigits(x) {
const err = Math.abs(x - ROOT);
if (err === 0) return 16;
return Math.max(0, -Math.log10(err / Math.abs(ROOT)));
}
const cv = document.getElementById('cv'), ctx = cv.getContext('2d');
const X0 = -0.2, X1 = 4.2, Y0 = -3, Y1 = 8; // world window
const sx = x => (x - X0) / (X1 - X0) * cv.width;
const sy = y => cv.height - (y - Y0) / (Y1 - Y0) * cv.height;
function drawAxes() {
ctx.clearRect(0, 0, cv.width, cv.height);
ctx.strokeStyle = '#cdd9e3'; ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(0, sy(0)); ctx.lineTo(cv.width, sy(0)); // x-axis
ctx.moveTo(sx(0), 0); ctx.lineTo(sx(0), cv.height); ctx.stroke(); // y-axis
// root marker
ctx.strokeStyle = '#0a7d33'; ctx.setLineDash([4, 3]);
ctx.beginPath(); ctx.moveTo(sx(ROOT), 0); ctx.lineTo(sx(ROOT), cv.height); ctx.stroke();
ctx.setLineDash([]);
// curve
ctx.strokeStyle = '#1d3557'; ctx.lineWidth = 2; ctx.beginPath();
for (let i = 0; i <= 200; i++) { const x = X0 + (X1 - X0) * i / 200;
const px = sx(x), py = sy(f(x)); i ? ctx.lineTo(px, py) : ctx.moveTo(px, py); }
ctx.stroke();
}
function drawStep(x) {
const xn = newtonStep(x);
// tangent at x, drawn down to where it hits y = 0 (that point is xn)
ctx.strokeStyle = '#e63946'; ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.moveTo(sx(x), sy(f(x))); ctx.lineTo(sx(xn), sy(0)); ctx.stroke();
// current point on curve
ctx.fillStyle = '#e63946';
ctx.beginPath(); ctx.arc(sx(x), sy(f(x)), 4, 0, 7); ctx.fill();
// landing point on the axis
ctx.fillStyle = '#0a7d33';
ctx.beginPath(); ctx.arc(sx(xn), sy(0), 4, 0, 7); ctx.fill();
}
let x = 3, n = 0, timer = null;
function logRow() {
const tb = document.querySelector('#log tbody');
const tr = document.createElement('tr');
tr.innerHTML = '<td>' + n + '</td><td>' + x.toFixed(8) + '</td><td>' +
correctDigits(x).toFixed(1) + '</td>';
tb.appendChild(tr);
}
function redraw() { drawAxes(); drawStep(x); }
function reset() {
if (timer) { clearInterval(timer); timer = null; }
x = parseFloat(document.getElementById('x0').value); n = 0;
document.querySelector('#log tbody').innerHTML = '';
logRow(); redraw();
}
function step() {
if (Math.abs(x - ROOT) < 1e-12) return;
x = newtonStep(x); n++; logRow(); redraw();
}
function run() {
if (timer) return;
timer = setInterval(() => {
if (n >= 8 || Math.abs(x - ROOT) < 1e-12) { clearInterval(timer); timer = null; return; }
step();
}, 650);
}
document.getElementById('x0').addEventListener('input', e => {
document.getElementById('x0lbl').textContent = parseFloat(e.target.value).toFixed(2);
reset();
});
document.getElementById('step').addEventListener('click', step);
document.getElementById('run').addEventListener('click', run);
document.getElementById('reset').addEventListener('click', reset);
reset();
console.log('root', ROOT, 'from x0=3:', [0,1,2,3,4].reduce((a)=>a, 0));
Watch the error column. After the first couple of steps the number of correct digits roughly doubles every time — 1 digit, then 2, then 4, then 8. That explosive accuracy is called quadratic convergence, and it is why a handful of steps is enough to nail almost any decimal you want.
Comments
Loading comments...