The key insight is that a sparse oracle gives a polynomial-time machine more power than it seems. Pick a density level and a length, and watch how a hypothetical sparse hard set interacts with a SAT query.
<p class="hint">A <b>sparse set</b> has at most a polynomial number of YES-strings per length. Set the density bound p(n) and watch how a polynomial-time machine can enumerate all candidates — making a sparse NP-hard set collapse NP into P.</p>
<div class="controls">
<label>Input length n: <input id="n-slider" type="range" min="1" max="12" value="6" step="1">
<span id="n-val">6</span></label>
<label>Density bound p(n) = n<sup><span id="k-val">2</span></sup>:
<input id="k-slider" type="range" min="1" max="4" value="2" step="1"></label>
</div>
<div class="stats" id="stats"></div>
<div class="timeline" id="timeline"></div>
<div class="btns">
<button id="run-btn" type="button">Simulate enumeration</button>
<button id="reset-btn" type="button" class="ghost">Reset</button>
</div>
<div class="verdict" id="verdict"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .85rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.controls { display: flex; flex-direction: column; gap: .5rem; margin-bottom: .8rem; }
label { font-size: .9rem; display: flex; align-items: center; gap: .5rem; }
input[type=range] { width: 160px; }
.stats { font-size: .88rem; background: #edf2f7; border-radius: 8px; padding: .55rem .8rem;
margin-bottom: .6rem; line-height: 1.7; }
.stats b { color: #1d3557; }
.timeline { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: .7rem; min-height: 32px; }
.chip { font-size: .7rem; font-family: ui-monospace, monospace; padding: 3px 7px;
border-radius: 6px; border: 1px solid #cdd9e3; background: #e8eef3; color: #334; }
.chip.yes { background: #d1fae5; border-color: #34d399; color: #065f46; }
.chip.no { background: #fee2e2; border-color: #fca5a5; color: #7f1d1d; }
.chip.skip { background: #f1f5f9; color: #94a3b8; border-color: #cbd5e1; }
.btns { display: flex; gap: .5rem; margin-bottom: .6rem; }
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; }
.verdict { font-size: .95rem; font-weight: 600; min-height: 1.4em; padding: .4rem .6rem;
border-radius: 8px; }
.verdict.collapse { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
.verdict.safe { background: #d1fae5; color: #065f46; border: 1px solid #34d399; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Mahaney’s Theorem demo
// Suppose S is a sparse set (|S ∩ {0,1}^n| <= n^k) that is NP-hard.
// A polynomial-time machine can enumerate all strings of length n up to
// the density bound and check each against the reduction output.
// If the total candidates fit in polynomial time, NP collapses to P.
//
// Demo: we simulate strings of a given length n, a density bound p(n) = n^k,
// and show how many candidates the machine must check. If the total alphabet
// size (2^n) exceeds the density bound, the sparse assumption is violated — a truly hard sparse set can only have p(n) members, so the machine checks at most p(n) candidates (polynomial!) instead of 2^n (exponential).
const nSlider = document.getElementById('n-slider');
const kSlider = document.getElementById('k-slider');
const nVal = document.getElementById('n-val');
const kVal = document.getElementById('k-val');
const runBtn = document.getElementById('run-btn');
const resetBtn= document.getElementById('reset-btn');
const stats = document.getElementById('stats');
const timeline= document.getElementById('timeline');
const verdict = document.getElementById('verdict');
let n = 6, k = 2;
function pow(base, exp) { let r = 1; for(let i=0;i<exp;i++) r *= base; return r; }
function update() {
n = +nSlider.value;
k = +kSlider.value;
nVal.textContent = n;
kVal.textContent = k;
const density = pow(n, k); // p(n) = n^k
const universe = pow(2, n); // all strings of length n
stats.innerHTML =
fmt('Input length: <b>n = %n%</b><br>Density bound: <b>p(n) = n<sup>%k%</sup> = %density%</b> (max YES-strings in a sparse set)<br>Universe size: <b>2<sup>%n%</sup> = %universe%</b> strings of length %n%<br>Enumeration cost: <b>%v% checks</b> (%v2% — %v3%)', { n: n, k: k, density: density, universe: universe, v: density <= universe ? density : universe, v2: density <= universe ? 'polynomial' : 'would exceed universe', v3: density <= universe ? 'a poly-time machine can afford this!' : 'universe too small!' });
timeline.innerHTML = '';
verdict.textContent = '';
verdict.className = 'verdict';
}
function simulate() {
const density = pow(n, k);
const universe = pow(2, n);
const toCheck = Math.min(density, universe);
const MAX_SHOW = 40;
timeline.innerHTML = '';
// Simulate: the machine checks strings 0..toCheck-1
// We mark a few as YES (in the sparse set) and the rest NO/skip
// For illustration, we seed 3 YES strings deterministically
const yesSet = new Set();
if (toCheck > 0) {
// Plant ~sqrt(density) YES strings, spread across the range
const yesCount = Math.max(1, Math.floor(Math.sqrt(density)));
const step = Math.max(1, Math.floor(toCheck / (yesCount + 1)));
for (let i = 1; i <= yesCount && i * step < toCheck; i++) {
yesSet.add(i * step);
}
}
let shown = 0;
for (let i = 0; i < toCheck && shown < MAX_SHOW; i++, shown++) {
const chip = document.createElement('div');
chip.className = 'chip ' + (yesSet.has(i) ? 'yes' : 'no');
// Show as binary string of length min(n,6)
const displayLen = Math.min(n, 6);
let bits = i.toString(2).padStart(displayLen, '0');
if (n > 6) bits = bits + '…';
chip.textContent = bits;
timeline.appendChild(chip);
}
if (toCheck > MAX_SHOW) {
const chip = document.createElement('div');
chip.className = 'chip skip';
chip.textContent = fmt('+%v% more', { v: toCheck - MAX_SHOW });
timeline.appendChild(chip);
}
// Verdict
verdict.className = 'verdict collapse';
if (n <= 20 && density >= universe) {
// Density bound exceeds universe: every string is "candidate" — not really sparse
verdict.className = 'verdict safe';
verdict.textContent =
fmt('With n=%n% and p(n)=%density% ≥ 2^n=%universe%, the set is not truly sparse at this length — density matches the universe. Mahaney’s argument requires p(n) << 2^n.', { n: n, density: density, universe: universe });
} else {
verdict.textContent =
fmt('The machine checks %toCheck% strings (= n^ n^%k% = %density%), which is polynomial in n. If S were NP-hard and sparse, this enumeration would let a poly-time machine solve all of NP → P = NP!', { toCheck: toCheck, k: k, density: density });
}
console.log(fmt('Mahaney demo result: n=%n%, k=%k%, density(p(n))=%density%, universe=2^n=%universe%, checks=%toCheck%, YES count=%size%', { n: n, k: k, density: density, universe: universe, toCheck: toCheck, size: yesSet.size }));
}
nSlider.addEventListener('input', update);
kSlider.addEventListener('input', update);
runBtn.addEventListener('click', simulate);
resetBtn.addEventListener('click', () => { nSlider.value=6; kSlider.value=2; update(); });
update();
<p class="hint">A <b>sparse set</b> has at most a polynomial number of YES-strings per length. Set the density bound p(n) and watch how a polynomial-time machine can enumerate all candidates — making a sparse NP-hard set collapse NP into P.</p>
<div class="controls">
<label>Input length n: <input id="n-slider" type="range" min="1" max="12" value="6" step="1">
<span id="n-val">6</span></label>
<label>Density bound p(n) = n<sup><span id="k-val">2</span></sup>:
<input id="k-slider" type="range" min="1" max="4" value="2" step="1"></label>
</div>
<div class="stats" id="stats"></div>
<div class="timeline" id="timeline"></div>
<div class="btns">
<button id="run-btn" type="button">Simulate enumeration</button>
<button id="reset-btn" type="button" class="ghost">Reset</button>
</div>
<div class="verdict" id="verdict"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .85rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.controls { display: flex; flex-direction: column; gap: .5rem; margin-bottom: .8rem; }
label { font-size: .9rem; display: flex; align-items: center; gap: .5rem; }
input[type=range] { width: 160px; }
.stats { font-size: .88rem; background: #edf2f7; border-radius: 8px; padding: .55rem .8rem;
margin-bottom: .6rem; line-height: 1.7; }
.stats b { color: #1d3557; }
.timeline { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: .7rem; min-height: 32px; }
.chip { font-size: .7rem; font-family: ui-monospace, monospace; padding: 3px 7px;
border-radius: 6px; border: 1px solid #cdd9e3; background: #e8eef3; color: #334; }
.chip.yes { background: #d1fae5; border-color: #34d399; color: #065f46; }
.chip.no { background: #fee2e2; border-color: #fca5a5; color: #7f1d1d; }
.chip.skip { background: #f1f5f9; color: #94a3b8; border-color: #cbd5e1; }
.btns { display: flex; gap: .5rem; margin-bottom: .6rem; }
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; }
.verdict { font-size: .95rem; font-weight: 600; min-height: 1.4em; padding: .4rem .6rem;
border-radius: 8px; }
.verdict.collapse { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
.verdict.safe { background: #d1fae5; color: #065f46; border: 1px solid #34d399; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Mahaney’s Theorem demo
// Suppose S is a sparse set (|S ∩ {0,1}^n| <= n^k) that is NP-hard.
// A polynomial-time machine can enumerate all strings of length n up to
// the density bound and check each against the reduction output.
// If the total candidates fit in polynomial time, NP collapses to P.
//
// Demo: we simulate strings of a given length n, a density bound p(n) = n^k,
// and show how many candidates the machine must check. If the total alphabet
// size (2^n) exceeds the density bound, the sparse assumption is violated — a truly hard sparse set can only have p(n) members, so the machine checks at most p(n) candidates (polynomial!) instead of 2^n (exponential).
const nSlider = document.getElementById('n-slider');
const kSlider = document.getElementById('k-slider');
const nVal = document.getElementById('n-val');
const kVal = document.getElementById('k-val');
const runBtn = document.getElementById('run-btn');
const resetBtn= document.getElementById('reset-btn');
const stats = document.getElementById('stats');
const timeline= document.getElementById('timeline');
const verdict = document.getElementById('verdict');
let n = 6, k = 2;
function pow(base, exp) { let r = 1; for(let i=0;i<exp;i++) r *= base; return r; }
function update() {
n = +nSlider.value;
k = +kSlider.value;
nVal.textContent = n;
kVal.textContent = k;
const density = pow(n, k); // p(n) = n^k
const universe = pow(2, n); // all strings of length n
stats.innerHTML =
fmt('Input length: <b>n = %n%</b><br>Density bound: <b>p(n) = n<sup>%k%</sup> = %density%</b> (max YES-strings in a sparse set)<br>Universe size: <b>2<sup>%n%</sup> = %universe%</b> strings of length %n%<br>Enumeration cost: <b>%v% checks</b> (%v2% — %v3%)', { n: n, k: k, density: density, universe: universe, v: density <= universe ? density : universe, v2: density <= universe ? 'polynomial' : 'would exceed universe', v3: density <= universe ? 'a poly-time machine can afford this!' : 'universe too small!' });
timeline.innerHTML = '';
verdict.textContent = '';
verdict.className = 'verdict';
}
function simulate() {
const density = pow(n, k);
const universe = pow(2, n);
const toCheck = Math.min(density, universe);
const MAX_SHOW = 40;
timeline.innerHTML = '';
// Simulate: the machine checks strings 0..toCheck-1
// We mark a few as YES (in the sparse set) and the rest NO/skip
// For illustration, we seed 3 YES strings deterministically
const yesSet = new Set();
if (toCheck > 0) {
// Plant ~sqrt(density) YES strings, spread across the range
const yesCount = Math.max(1, Math.floor(Math.sqrt(density)));
const step = Math.max(1, Math.floor(toCheck / (yesCount + 1)));
for (let i = 1; i <= yesCount && i * step < toCheck; i++) {
yesSet.add(i * step);
}
}
let shown = 0;
for (let i = 0; i < toCheck && shown < MAX_SHOW; i++, shown++) {
const chip = document.createElement('div');
chip.className = 'chip ' + (yesSet.has(i) ? 'yes' : 'no');
// Show as binary string of length min(n,6)
const displayLen = Math.min(n, 6);
let bits = i.toString(2).padStart(displayLen, '0');
if (n > 6) bits = bits + '…';
chip.textContent = bits;
timeline.appendChild(chip);
}
if (toCheck > MAX_SHOW) {
const chip = document.createElement('div');
chip.className = 'chip skip';
chip.textContent = fmt('+%v% more', { v: toCheck - MAX_SHOW });
timeline.appendChild(chip);
}
// Verdict
verdict.className = 'verdict collapse';
if (n <= 20 && density >= universe) {
// Density bound exceeds universe: every string is "candidate" — not really sparse
verdict.className = 'verdict safe';
verdict.textContent =
fmt('With n=%n% and p(n)=%density% ≥ 2^n=%universe%, the set is not truly sparse at this length — density matches the universe. Mahaney’s argument requires p(n) << 2^n.', { n: n, density: density, universe: universe });
} else {
verdict.textContent =
fmt('The machine checks %toCheck% strings (= n^ n^%k% = %density%), which is polynomial in n. If S were NP-hard and sparse, this enumeration would let a poly-time machine solve all of NP → P = NP!', { toCheck: toCheck, k: k, density: density });
}
console.log(fmt('Mahaney demo result: n=%n%, k=%k%, density(p(n))=%density%, universe=2^n=%universe%, checks=%toCheck%, YES count=%size%', { n: n, k: k, density: density, universe: universe, toCheck: toCheck, size: yesSet.size }));
}
nSlider.addEventListener('input', update);
kSlider.addEventListener('input', update);
runBtn.addEventListener('click', simulate);
resetBtn.addEventListener('click', () => { nSlider.value=6; kSlider.value=2; update(); });
update();
Notice how as density drops below polynomial, the machine can enumerate all candidates at a given length and check each one in polynomial time — effectively simulating an NP oracle. If the hard set were truly sparse, that enumeration would collapse NP to P. The demo makes the counting argument vivid: sparseness and hardness pull in opposite directions.
Comments
Loading comments...