Pick values of m and n and press Compute. The display shows the final value and how many recursive calls were made. Keep m ≤ 3 to stay safe — A(4, 2) is already a number with 19,729 digits.
<div class="controls">
<label>m (0–3): <input id="mval" type="number" value="2" min="0" max="3"></label>
<label>n (0–6): <input id="nval" type="number" value="3" min="0" max="6"></label>
<button id="compute" type="button">Compute</button>
</div>
<div id="result-box" class="result-box" aria-live="polite">
<div class="result-main" id="result-main">A(2, 3) = ?</div>
<div class="result-sub" id="result-sub">Press Compute to start.</div>
</div>
<div class="table-wrap">
<table id="table" aria-label="Ackermann values">
<thead>
<tr><th>m \ n</th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th></tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<p class="note">Note: A(4,0) = 13; A(4,1) = 65533; A(4,2) has 19,729 digits — too large to display.</p>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.controls { display: flex; gap: .7rem; flex-wrap: wrap; align-items: center; margin-bottom: .8rem; }
label { font-size: .92rem; display: flex; align-items: center; gap: .35rem; }
input[type=number] { width: 52px; padding: .25rem .4rem; border: 1px solid #9aa; border-radius: 6px;
font-size: .95rem; text-align: center; }
button { font: 600 14px system-ui; padding: .4rem .9rem; background: #1d3557; color: #fff;
border: none; border-radius: 8px; cursor: pointer; }
button:hover { background: #27497a; }
.result-box { background: #f0f4f8; border: 1px solid #cdd9e3; border-radius: 10px;
padding: .7rem 1rem; margin-bottom: .9rem; }
.result-main { font: 700 1.3rem ui-monospace, monospace; color: #1d3557; }
.result-main.highlight { color: #0a7d33; }
.result-sub { font-size: .85rem; color: #555; margin-top: .25rem; }
.table-wrap { overflow-x: auto; }
table { border-collapse: collapse; font-size: .88rem; min-width: 340px; }
th, td { border: 1px solid #cdd9e3; padding: .3rem .6rem; text-align: center; }
th { background: #e8eef3; font-weight: 700; color: #1d3557; }
td { background: #fff; color: #333; min-width: 46px; }
td.active { background: #1d3557; color: #fff; font-weight: 700; }
td.computed { background: #d4edda; color: #0a5c2e; }
.note { font-size: .78rem; color: #666; margin-top: .5rem; line-height: 1.4; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Ackermann function — the two-argument version by Rózsa Péter (1935).
// A(0, n) = n + 1
// A(m, 0) = A(m-1, 1) when m > 0
// A(m, n) = A(m-1, A(m, n-1)) when m > 0, n > 0
//
// We track call count to show how recursion explodes. We also cap m≤3 and n≤6 so the browser doesn't lock up (A(4,2) has 19,729 digits).
const MAX_M = 4, MAX_N = 6;
let callCount = 0;
function ack(m, n) {
callCount++;
if (m === 0) return n + 1;
if (n === 0) return ack(m - 1, 1);
return ack(m - 1, ack(m, n - 1));
}
// Known closed forms for display:
// A(0,n)=n+1 A(1,n)=n+2 A(2,n)=2n+3 A(3,n)=2^(n+3)-3
function closedForm(m, n) {
if (m === 0) return `n+1 = ${n+1}`;
if (m === 1) return `n+2 = ${n+2}`;
if (m === 2) return `2n+3 = ${2*n+3}`;
if (m === 3) return `2^(n+3)−3 = ${Math.pow(2,n+3)-3}`;
return null;
}
function buildTable() {
const tbody = document.getElementById('tbody');
tbody.innerHTML = '';
for (let m = 0; m <= 3; m++) {
const tr = document.createElement('tr');
const th = document.createElement('th');
th.textContent = m;
tr.appendChild(th);
for (let n = 0; n <= MAX_N; n++) {
const td = document.createElement('td');
td.id = `cell-${m}-${n}`;
let val;
// Compute inline for small values, use known formula for m=3 to avoid deep stack
if (m === 3) {
val = Math.pow(2, n + 3) - 3;
} else {
callCount = 0;
val = ack(m, n);
}
td.textContent = val;
tr.appendChild(td);
}
tbody.appendChild(tr);
}
}
function doCompute() {
const mIn = document.getElementById('mval');
const nIn = document.getElementById('nval');
let m = parseInt(mIn.value, 10);
let n = parseInt(nIn.value, 10);
if (isNaN(m) || m < 0 || m > 3) { m = Math.max(0, Math.min(3, m || 0)); mIn.value = m; }
if (isNaN(n) || n < 0 || n > MAX_N) { n = Math.max(0, Math.min(MAX_N, n || 0)); nIn.value = n; }
callCount = 0;
let result;
if (m === 3) {
// use closed form to avoid JS stack overflow for large n
result = Math.pow(2, n + 3) - 3;
callCount = -1; // flag: used formula
} else {
result = ack(m, n);
}
const rm = document.getElementById('result-main');
rm.textContent = `A(${m}, ${n}) = ${result}`;
rm.className = 'result-main highlight';
const cf = closedForm(m, n);
const callStr = callCount >= 0
? fmt('%callCount% recursive calls', { callCount: callCount.toLocaleString() })
: 'computed via closed form 2^(n+3)−3';
document.getElementById('result-sub').textContent =
fmt('Closed form: %v% | %callStr%', { v: cf || '(see table)', callStr: callStr });
// highlight chosen cell
document.querySelectorAll('td').forEach(td => td.classList.remove('active'));
const cell = document.getElementById(`cell-${m}-${n}`);
if (cell) { cell.classList.add('active'); cell.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
console.log(`A(${m},${n}) = ${result}`, callCount >= 0 ? fmt('| calls: %callCount%', { callCount: callCount }) : '| via closed form');
}
document.getElementById('compute').addEventListener('click', doCompute);
buildTable();
doCompute(); // show A(2,3)=9 on load
<div class="controls">
<label>m (0–3): <input id="mval" type="number" value="2" min="0" max="3"></label>
<label>n (0–6): <input id="nval" type="number" value="3" min="0" max="6"></label>
<button id="compute" type="button">Compute</button>
</div>
<div id="result-box" class="result-box" aria-live="polite">
<div class="result-main" id="result-main">A(2, 3) = ?</div>
<div class="result-sub" id="result-sub">Press Compute to start.</div>
</div>
<div class="table-wrap">
<table id="table" aria-label="Ackermann values">
<thead>
<tr><th>m \ n</th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th></tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<p class="note">Note: A(4,0) = 13; A(4,1) = 65533; A(4,2) has 19,729 digits — too large to display.</p>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.controls { display: flex; gap: .7rem; flex-wrap: wrap; align-items: center; margin-bottom: .8rem; }
label { font-size: .92rem; display: flex; align-items: center; gap: .35rem; }
input[type=number] { width: 52px; padding: .25rem .4rem; border: 1px solid #9aa; border-radius: 6px;
font-size: .95rem; text-align: center; }
button { font: 600 14px system-ui; padding: .4rem .9rem; background: #1d3557; color: #fff;
border: none; border-radius: 8px; cursor: pointer; }
button:hover { background: #27497a; }
.result-box { background: #f0f4f8; border: 1px solid #cdd9e3; border-radius: 10px;
padding: .7rem 1rem; margin-bottom: .9rem; }
.result-main { font: 700 1.3rem ui-monospace, monospace; color: #1d3557; }
.result-main.highlight { color: #0a7d33; }
.result-sub { font-size: .85rem; color: #555; margin-top: .25rem; }
.table-wrap { overflow-x: auto; }
table { border-collapse: collapse; font-size: .88rem; min-width: 340px; }
th, td { border: 1px solid #cdd9e3; padding: .3rem .6rem; text-align: center; }
th { background: #e8eef3; font-weight: 700; color: #1d3557; }
td { background: #fff; color: #333; min-width: 46px; }
td.active { background: #1d3557; color: #fff; font-weight: 700; }
td.computed { background: #d4edda; color: #0a5c2e; }
.note { font-size: .78rem; color: #666; margin-top: .5rem; line-height: 1.4; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Ackermann function — the two-argument version by Rózsa Péter (1935).
// A(0, n) = n + 1
// A(m, 0) = A(m-1, 1) when m > 0
// A(m, n) = A(m-1, A(m, n-1)) when m > 0, n > 0
//
// We track call count to show how recursion explodes. We also cap m≤3 and n≤6 so the browser doesn't lock up (A(4,2) has 19,729 digits).
const MAX_M = 4, MAX_N = 6;
let callCount = 0;
function ack(m, n) {
callCount++;
if (m === 0) return n + 1;
if (n === 0) return ack(m - 1, 1);
return ack(m - 1, ack(m, n - 1));
}
// Known closed forms for display:
// A(0,n)=n+1 A(1,n)=n+2 A(2,n)=2n+3 A(3,n)=2^(n+3)-3
function closedForm(m, n) {
if (m === 0) return `n+1 = ${n+1}`;
if (m === 1) return `n+2 = ${n+2}`;
if (m === 2) return `2n+3 = ${2*n+3}`;
if (m === 3) return `2^(n+3)−3 = ${Math.pow(2,n+3)-3}`;
return null;
}
function buildTable() {
const tbody = document.getElementById('tbody');
tbody.innerHTML = '';
for (let m = 0; m <= 3; m++) {
const tr = document.createElement('tr');
const th = document.createElement('th');
th.textContent = m;
tr.appendChild(th);
for (let n = 0; n <= MAX_N; n++) {
const td = document.createElement('td');
td.id = `cell-${m}-${n}`;
let val;
// Compute inline for small values, use known formula for m=3 to avoid deep stack
if (m === 3) {
val = Math.pow(2, n + 3) - 3;
} else {
callCount = 0;
val = ack(m, n);
}
td.textContent = val;
tr.appendChild(td);
}
tbody.appendChild(tr);
}
}
function doCompute() {
const mIn = document.getElementById('mval');
const nIn = document.getElementById('nval');
let m = parseInt(mIn.value, 10);
let n = parseInt(nIn.value, 10);
if (isNaN(m) || m < 0 || m > 3) { m = Math.max(0, Math.min(3, m || 0)); mIn.value = m; }
if (isNaN(n) || n < 0 || n > MAX_N) { n = Math.max(0, Math.min(MAX_N, n || 0)); nIn.value = n; }
callCount = 0;
let result;
if (m === 3) {
// use closed form to avoid JS stack overflow for large n
result = Math.pow(2, n + 3) - 3;
callCount = -1; // flag: used formula
} else {
result = ack(m, n);
}
const rm = document.getElementById('result-main');
rm.textContent = `A(${m}, ${n}) = ${result}`;
rm.className = 'result-main highlight';
const cf = closedForm(m, n);
const callStr = callCount >= 0
? fmt('%callCount% recursive calls', { callCount: callCount.toLocaleString() })
: 'computed via closed form 2^(n+3)−3';
document.getElementById('result-sub').textContent =
fmt('Closed form: %v% | %callStr%', { v: cf || '(see table)', callStr: callStr });
// highlight chosen cell
document.querySelectorAll('td').forEach(td => td.classList.remove('active'));
const cell = document.getElementById(`cell-${m}-${n}`);
if (cell) { cell.classList.add('active'); cell.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
console.log(`A(${m},${n}) = ${result}`, callCount >= 0 ? fmt('| calls: %callCount%', { callCount: callCount }) : '| via closed form');
}
document.getElementById('compute').addEventListener('click', doCompute);
buildTable();
doCompute(); // show A(2,3)=9 on load
Notice how m controls the level of nesting: m = 0 is just n + 1; m = 1 is n + 2; m = 2 grows like 2n + 3; m = 3 shoots up to 2n+3−3. Each extra unit of m wraps the previous level of growth inside itself, again and again. The call count grows even faster than the result — a direct window into why no bounded loop can keep up.
Comments
Loading comments...