Choose a value of n (the number of pairs of brackets). The demo lists every balanced string of length 2n and counts them. Watch the count match the Catalan number Cn exactly.
<p class="hint">Pick <b>n</b> to see every balanced bracket string of length 2n listed below.
The count at the top always matches the Catalan number <b>C<sub>n</sub></b>.</p>
<div class="controls">
<label>n =
<input id="nInput" type="range" min="0" max="6" value="3" />
<span id="nVal">3</span>
</label>
</div>
<div class="stats" id="stats"></div>
<ul id="list" class="str-list"></ul>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.controls { display: flex; align-items: center; gap: .6rem; margin-bottom: .7rem; font-size: .95rem; }
label { display: flex; align-items: center; gap: .5rem; font-weight: 600; }
input[type=range] { accent-color: #1d3557; width: 180px; }
.stats { font-size: 1rem; font-weight: 700; margin-bottom: .6rem; color: #1d3557; min-height: 1.4em; }
.str-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; max-height: 300px; overflow-y: auto; }
.str-list li { font-family: ui-monospace, monospace; font-size: .92rem; background: #e8eef3; color: #1d3557; border: 1px solid #c3d0dc; border-radius: 6px; padding: .2rem .5rem; white-space: nowrap; }
.str-list li.highlight { background: #1d3557; color: #fff; border-color: #1d3557; }
.cap-note { font-size: .82rem; color: #666; margin-top: .5rem; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Catalan number demo: generate all balanced bracket strings of length 2n.
// C(n) = (1/(n+1)) * C(2n, n) — the count always matches the Catalan number.
// Algorithm: recursive generation ensuring at no prefix do closing brackets
// exceed opening brackets.
const MAX_LIST = 6; // max n for listing (C(7)=429 would be too slow to render)
function catalan(n) {
// Use the recurrence: C(0)=1, C(n) = sum_{i=0}^{n-1} C(i)*C(n-1-i)
const C = new Array(n + 1).fill(0n);
C[0] = 1n;
for (let k = 1; k <= n; k++) {
for (let i = 0; i < k; i++) C[k] += C[i] * C[k - 1 - i];
}
return C[n];
}
// Generate all balanced strings of n pairs using backtracking.
function generate(n) {
const results = [];
function bt(s, open, close) {
if (s.length === 2 * n) { results.push(s); return; }
if (open < n) bt(s + '(', open + 1, close);
if (close < open) bt(s + ')', open, close + 1);
}
bt('', 0, 0);
return results;
}
const nInput = document.getElementById('nInput');
const nVal = document.getElementById('nVal');
const stats = document.getElementById('stats');
const list = document.getElementById('list');
function render() {
const n = parseInt(nInput.value, 10);
nVal.textContent = n;
const cn = catalan(n);
stats.textContent = `C${n} = ${cn} ← ${n === 0 ? 'empty string' : fmt('balanced strings of length %v%', { v: 2 * n })}`;
list.innerHTML = '';
if (n === 0) {
const li = document.createElement('li');
li.className = 'highlight';
li.textContent = '(empty)';
list.appendChild(li);
return;
}
if (n > MAX_LIST) {
const note = document.createElement('p');
note.className = 'cap-note';
note.textContent = fmt('Listing %cn% strings would be slow — showing count only for n > %MAX_LIST%.', { cn: cn, MAX_LIST: MAX_LIST });
list.appendChild(note);
return;
}
const strings = generate(n);
strings.forEach((s, idx) => {
const li = document.createElement('li');
if (idx === 0) li.className = 'highlight';
li.textContent = s;
list.appendChild(li);
});
// Verify: count must equal Catalan number
console.assert(
BigInt(strings.length) === cn,
`BUG: generated ${strings.length} strings but C(${n}) = ${cn}`
);
console.log(fmt('n=%n%: generated %length% strings, C(%n%)=%cn% ✓', { n: n, length: strings.length, cn: cn }));
}
nInput.addEventListener('input', render);
render();
<p class="hint">Pick <b>n</b> to see every balanced bracket string of length 2n listed below.
The count at the top always matches the Catalan number <b>C<sub>n</sub></b>.</p>
<div class="controls">
<label>n =
<input id="nInput" type="range" min="0" max="6" value="3" />
<span id="nVal">3</span>
</label>
</div>
<div class="stats" id="stats"></div>
<ul id="list" class="str-list"></ul>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.controls { display: flex; align-items: center; gap: .6rem; margin-bottom: .7rem; font-size: .95rem; }
label { display: flex; align-items: center; gap: .5rem; font-weight: 600; }
input[type=range] { accent-color: #1d3557; width: 180px; }
.stats { font-size: 1rem; font-weight: 700; margin-bottom: .6rem; color: #1d3557; min-height: 1.4em; }
.str-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; max-height: 300px; overflow-y: auto; }
.str-list li { font-family: ui-monospace, monospace; font-size: .92rem; background: #e8eef3; color: #1d3557; border: 1px solid #c3d0dc; border-radius: 6px; padding: .2rem .5rem; white-space: nowrap; }
.str-list li.highlight { background: #1d3557; color: #fff; border-color: #1d3557; }
.cap-note { font-size: .82rem; color: #666; margin-top: .5rem; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Catalan number demo: generate all balanced bracket strings of length 2n.
// C(n) = (1/(n+1)) * C(2n, n) — the count always matches the Catalan number.
// Algorithm: recursive generation ensuring at no prefix do closing brackets
// exceed opening brackets.
const MAX_LIST = 6; // max n for listing (C(7)=429 would be too slow to render)
function catalan(n) {
// Use the recurrence: C(0)=1, C(n) = sum_{i=0}^{n-1} C(i)*C(n-1-i)
const C = new Array(n + 1).fill(0n);
C[0] = 1n;
for (let k = 1; k <= n; k++) {
for (let i = 0; i < k; i++) C[k] += C[i] * C[k - 1 - i];
}
return C[n];
}
// Generate all balanced strings of n pairs using backtracking.
function generate(n) {
const results = [];
function bt(s, open, close) {
if (s.length === 2 * n) { results.push(s); return; }
if (open < n) bt(s + '(', open + 1, close);
if (close < open) bt(s + ')', open, close + 1);
}
bt('', 0, 0);
return results;
}
const nInput = document.getElementById('nInput');
const nVal = document.getElementById('nVal');
const stats = document.getElementById('stats');
const list = document.getElementById('list');
function render() {
const n = parseInt(nInput.value, 10);
nVal.textContent = n;
const cn = catalan(n);
stats.textContent = `C${n} = ${cn} ← ${n === 0 ? 'empty string' : fmt('balanced strings of length %v%', { v: 2 * n })}`;
list.innerHTML = '';
if (n === 0) {
const li = document.createElement('li');
li.className = 'highlight';
li.textContent = '(empty)';
list.appendChild(li);
return;
}
if (n > MAX_LIST) {
const note = document.createElement('p');
note.className = 'cap-note';
note.textContent = fmt('Listing %cn% strings would be slow — showing count only for n > %MAX_LIST%.', { cn: cn, MAX_LIST: MAX_LIST });
list.appendChild(note);
return;
}
const strings = generate(n);
strings.forEach((s, idx) => {
const li = document.createElement('li');
if (idx === 0) li.className = 'highlight';
li.textContent = s;
list.appendChild(li);
});
// Verify: count must equal Catalan number
console.assert(
BigInt(strings.length) === cn,
`BUG: generated ${strings.length} strings but C(${n}) = ${cn}`
);
console.log(fmt('n=%n%: generated %length% strings, C(%n%)=%cn% ✓', { n: n, length: strings.length, cn: cn }));
}
nInput.addEventListener('input', render);
render();
Notice: for n = 4 the list already has 14 strings and for n = 5 it jumps to 42. The count grows quickly — roughly four times larger each step. Yet computing the count (via the formula or recurrence) takes only a moment; listing all strings gets expensive fast, which is why large n values are intentionally capped in the demo.
Comments
Loading comments...