Try it. Click numbers to add them to your selection; your running total updates instantly. Can you hit the target exactly?
<p class="hint">Click numbers to add them to your selection. Can you find a subset that adds up to the target <b>exactly</b>? Checking is instant — finding is the hard part.</p>
<div class="target">Target: <b id="target">15</b></div>
<div id="nums" class="nums"></div>
<div class="sumline">Your sum: <b id="sum">0</b> <span id="match"></span></div>
<div class="bar-btns">
<button id="find" type="button">Find a subset</button>
<button id="reset" type="button" class="ghost">Clear</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 .6rem; line-height: 1.45; }
.target { font-size: 1.05rem; margin: .3rem 0; }
.target b { font-size: 1.4rem; color: #457b9d; }
.nums { display: flex; flex-wrap: wrap; gap: .5rem; margin: .5rem 0; }
.num { width: 52px; height: 52px; border-radius: 10px; border: 2px solid #bbb; background: #fff; cursor: pointer; font: 700 18px ui-monospace, monospace; color: #333; transition: all .1s; }
.num.sel { background: #457b9d; color: #fff; border-color: #457b9d; transform: translateY(-2px); }
.sumline { font-size: 1rem; margin: .4rem 0; font-weight: 600; }
#match.hit { color: #0a7d33; }
#match.miss { color: #888; font-weight: 500; }
.bar-btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #457b9d; background: #457b9d; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #457b9d; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Subset Sum: is there a subset adding up to the target exactly? Checking a
// selection is one addition pass; finding one means searching up to 2^n subsets.
const NUMS = [3, 7, 1, 8, 4, 6];
const TARGET = 15;
const sel = new Set();
function render() {
const box = document.getElementById('nums'); box.innerHTML = '';
NUMS.forEach((v, i) => {
const b = document.createElement('button');
b.className = 'num' + (sel.has(i) ? ' sel' : '');
b.textContent = v;
b.addEventListener('click', () => { sel.has(i) ? sel.delete(i) : sel.add(i); render(); });
box.appendChild(b);
});
let s = 0; sel.forEach(i => s += NUMS[i]);
document.getElementById('sum').textContent = s;
const m = document.getElementById('match');
if (sel.size && s === TARGET) { m.textContent = '= target ✓'; m.className = 'hit'; }
else if (sel.size) { m.textContent = fmt('(need %TARGET%)', { TARGET: TARGET }); m.className = 'miss'; }
else { m.textContent = ''; m.className = ''; }
}
function find() {
let steps = 0, found = null;
for (let mask = 1; mask < (1 << NUMS.length); mask++) {
steps++;
let s = 0; for (let i = 0; i < NUMS.length; i++) if (mask & (1 << i)) s += NUMS[i];
if (s === TARGET) { found = mask; break; }
}
sel.clear();
if (found !== null) for (let i = 0; i < NUMS.length; i++) if (found & (1 << i)) sel.add(i);
render();
console.log(found !== null ? fmt('Found a subset after checking %steps% of %v%.', { steps: steps, v: 1 << NUMS.length }) : 'No subset hits the target.');
}
document.getElementById('find').addEventListener('click', find);
document.getElementById('reset').addEventListener('click', () => { sel.clear(); render(); });
render();
<p class="hint">Click numbers to add them to your selection. Can you find a subset that adds up to the target <b>exactly</b>? Checking is instant — finding is the hard part.</p>
<div class="target">Target: <b id="target">15</b></div>
<div id="nums" class="nums"></div>
<div class="sumline">Your sum: <b id="sum">0</b> <span id="match"></span></div>
<div class="bar-btns">
<button id="find" type="button">Find a subset</button>
<button id="reset" type="button" class="ghost">Clear</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 .6rem; line-height: 1.45; }
.target { font-size: 1.05rem; margin: .3rem 0; }
.target b { font-size: 1.4rem; color: #457b9d; }
.nums { display: flex; flex-wrap: wrap; gap: .5rem; margin: .5rem 0; }
.num { width: 52px; height: 52px; border-radius: 10px; border: 2px solid #bbb; background: #fff; cursor: pointer; font: 700 18px ui-monospace, monospace; color: #333; transition: all .1s; }
.num.sel { background: #457b9d; color: #fff; border-color: #457b9d; transform: translateY(-2px); }
.sumline { font-size: 1rem; margin: .4rem 0; font-weight: 600; }
#match.hit { color: #0a7d33; }
#match.miss { color: #888; font-weight: 500; }
.bar-btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #457b9d; background: #457b9d; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #457b9d; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Subset Sum: is there a subset adding up to the target exactly? Checking a
// selection is one addition pass; finding one means searching up to 2^n subsets.
const NUMS = [3, 7, 1, 8, 4, 6];
const TARGET = 15;
const sel = new Set();
function render() {
const box = document.getElementById('nums'); box.innerHTML = '';
NUMS.forEach((v, i) => {
const b = document.createElement('button');
b.className = 'num' + (sel.has(i) ? ' sel' : '');
b.textContent = v;
b.addEventListener('click', () => { sel.has(i) ? sel.delete(i) : sel.add(i); render(); });
box.appendChild(b);
});
let s = 0; sel.forEach(i => s += NUMS[i]);
document.getElementById('sum').textContent = s;
const m = document.getElementById('match');
if (sel.size && s === TARGET) { m.textContent = '= target ✓'; m.className = 'hit'; }
else if (sel.size) { m.textContent = fmt('(need %TARGET%)', { TARGET: TARGET }); m.className = 'miss'; }
else { m.textContent = ''; m.className = ''; }
}
function find() {
let steps = 0, found = null;
for (let mask = 1; mask < (1 << NUMS.length); mask++) {
steps++;
let s = 0; for (let i = 0; i < NUMS.length; i++) if (mask & (1 << i)) s += NUMS[i];
if (s === TARGET) { found = mask; break; }
}
sel.clear();
if (found !== null) for (let i = 0; i < NUMS.length; i++) if (found & (1 << i)) sel.add(i);
render();
console.log(found !== null ? fmt('Found a subset after checking %steps% of %v%.', { steps: steps, v: 1 << NUMS.length }) : 'No subset hits the target.');
}
document.getElementById('find').addEventListener('click', find);
document.getElementById('reset').addEventListener('click', () => { sel.clear(); render(); });
render();
Notice how effortless it is to check a selection — you just read the sum. Finding one is the hard part: press Find a subset to let the computer search. For a handful of numbers it's instant, but the number of possible subsets doubles with every number you add.
Comments
Loading comments...