Alice holds a secret 8-bit string; Bob holds his own. They want to know if the two strings are equal, but Alice may only send Bob a short message of k bits. Pick how many bits she is allowed to send and see whether the protocol can ever be trusted.
<p class="hint">Alice holds a secret <b>8-bit</b> string. To decide if Bob's string equals hers, she may send only a <b>k-bit</b> message. Lower k to make her "compress" — and watch two different strings collide onto the same message, so Bob is fooled.</p>
<div class="ctl">
<label>Bits Alice may send: <b id="kval">8</b> / 8</label>
<input id="k" type="range" min="1" max="8" value="8" />
</div>
<div class="wire">
<div class="party"><div class="who">ALICE</div><div id="ax" class="bits"></div></div>
<div class="arrow"><span id="msg" class="msg">--------</span><div class="lab">message (k bits)</div></div>
<div class="party"><div class="who">BOB</div><div id="bx" class="bits"></div></div>
</div>
<div class="status" id="status">Press Test to run the protocol.</div>
<div class="btns">
<button id="shuffle" type="button">New random strings</button>
<button id="test" type="button">Test the protocol</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 .8rem; line-height: 1.45; }
.ctl { margin: .4rem 0 1rem; font-size: .95rem; }
.ctl input { width: 100%; margin-top: .3rem; }
.wire { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin: .6rem 0; flex-wrap: wrap; }
.party { text-align: center; flex: 1; min-width: 120px; }
.who { font: 700 12px system-ui; letter-spacing: .08em; color: #1d3557; margin-bottom: .35rem; }
.bits { display: flex; gap: 3px; justify-content: center; }
.bit { width: 22px; height: 26px; display: flex; align-items: center; justify-content: center;
font: 700 14px ui-monospace, monospace; border-radius: 5px; background: #e8eef3;
color: #1d3557; border: 1px solid #cdd9e3; }
.arrow { flex: 1; text-align: center; min-width: 120px; }
.arrow .msg { display: inline-block; font: 700 16px ui-monospace, monospace; letter-spacing: 2px;
background: #1d3557; color: #fff; padding: .3rem .6rem; border-radius: 6px; }
.arrow .lab { font-size: .72rem; color: #667; margin-top: .3rem; }
.status { font-size: .98rem; font-weight: 600; margin: .8rem 0 .6rem; min-height: 2.6em; line-height: 1.4; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.collide { background: #e63946 !important; color: #fff !important; border-color: #c92f3c !important; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
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#shuffle { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// EQUALITY communication complexity. Alice has an 8-bit string x; she may send
// only a k-bit deterministic message. We model her message as a fixed k-bit
// "fingerprint" of x. The point: if k < 8, by pigeonhole two distinct strings
// collide onto the same message, so Bob cannot decide EQUALITY reliably.
const N = 8;
let k = 8;
let alice = randStr();
function randStr(){ return Array.from({length:N}, () => Math.random()<0.5?0:1); }
function toStr(a){ return a.join(''); }
// Message Alice sends: the first k bits of a fixed scramble of x.
// (Any deterministic map to k<N bits has the same fatal property.)
function message(bits){
// XOR-fold the 8 bits down to k bits deterministically.
const m = new Array(k).fill(0);
for (let i=0;i<N;i++) m[i%k] ^= bits[i];
return m.join('');
}
// Search all 256 possible strings Alice could hold for two distinct ones that
// share Alice's current message -> a collision that breaks the protocol.
function findCollision(){
const mine = message(alice);
for (let v=0; v<(1<<N); v++){
const b = []; for(let i=0;i<N;i++) b.push((v>>i)&1);
if (toStr(b) === toStr(alice)) continue;
if (message(b) === mine) return b;
}
return null;
}
function renderBits(id, bits, mark){
const el = document.getElementById(id); el.innerHTML='';
bits.forEach(bt => { const d=document.createElement('div');
d.className='bit'+(mark?' collide':''); d.textContent=bt; el.appendChild(d); });
}
function setStatus(t,cls){ const s=document.getElementById('status'); s.innerHTML=t; s.className='status '+cls; }
function render(){
document.getElementById('kval').textContent = k;
renderBits('ax', alice, false);
// Bob shown as a copy of Alice for the "honest equal" case; reset to neutral.
renderBits('bx', alice, false);
document.getElementById('msg').textContent = message(alice);
setStatus(fmt('k = %k% bits. Press Test to run the protocol.', { k: k }),'');
}
function test(){
const mine = message(alice);
document.getElementById('msg').textContent = mine;
if (k >= N){
setStatus('✓ With k = 8, Alice’s message <b>is</b> her whole string. Bob compares exactly and EQUALITY is always correct. Cost: n+1 bits.','ok');
renderBits('bx', alice, false);
return;
}
const twin = findCollision();
if (twin){
renderBits('ax', alice, true);
renderBits('bx', twin, true);
setStatus(fmt('✗ Collision! Alice could hold <b><b>%toStr%</b></b> or <b><b>%toStr2%</b></b> — both send the same message <b><b>%mine%</b></b>. Bob can’t tell them apart, so for some inputs his EQUALITY answer is wrong. k = %k% < 8 is not enough.', { toStr: toStr(alice), toStr2: toStr(twin), mine: mine, k: k }),'bad');
console.log('Collision:', toStr(alice), '&', toStr(twin), '-> message', mine);
} else {
setStatus(fmt('No collision for this particular string at k = %k% — try New random strings; with k<8 one always exists somewhere.', { k: k }),'');
}
}
document.getElementById('k').addEventListener('input', e => { k = +e.target.value; render(); });
document.getElementById('shuffle').addEventListener('click', () => { alice = randStr(); render(); });
document.getElementById('test').addEventListener('click', test);
render();
<p class="hint">Alice holds a secret <b>8-bit</b> string. To decide if Bob's string equals hers, she may send only a <b>k-bit</b> message. Lower k to make her "compress" — and watch two different strings collide onto the same message, so Bob is fooled.</p>
<div class="ctl">
<label>Bits Alice may send: <b id="kval">8</b> / 8</label>
<input id="k" type="range" min="1" max="8" value="8" />
</div>
<div class="wire">
<div class="party"><div class="who">ALICE</div><div id="ax" class="bits"></div></div>
<div class="arrow"><span id="msg" class="msg">--------</span><div class="lab">message (k bits)</div></div>
<div class="party"><div class="who">BOB</div><div id="bx" class="bits"></div></div>
</div>
<div class="status" id="status">Press Test to run the protocol.</div>
<div class="btns">
<button id="shuffle" type="button">New random strings</button>
<button id="test" type="button">Test the protocol</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 .8rem; line-height: 1.45; }
.ctl { margin: .4rem 0 1rem; font-size: .95rem; }
.ctl input { width: 100%; margin-top: .3rem; }
.wire { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin: .6rem 0; flex-wrap: wrap; }
.party { text-align: center; flex: 1; min-width: 120px; }
.who { font: 700 12px system-ui; letter-spacing: .08em; color: #1d3557; margin-bottom: .35rem; }
.bits { display: flex; gap: 3px; justify-content: center; }
.bit { width: 22px; height: 26px; display: flex; align-items: center; justify-content: center;
font: 700 14px ui-monospace, monospace; border-radius: 5px; background: #e8eef3;
color: #1d3557; border: 1px solid #cdd9e3; }
.arrow { flex: 1; text-align: center; min-width: 120px; }
.arrow .msg { display: inline-block; font: 700 16px ui-monospace, monospace; letter-spacing: 2px;
background: #1d3557; color: #fff; padding: .3rem .6rem; border-radius: 6px; }
.arrow .lab { font-size: .72rem; color: #667; margin-top: .3rem; }
.status { font-size: .98rem; font-weight: 600; margin: .8rem 0 .6rem; min-height: 2.6em; line-height: 1.4; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.collide { background: #e63946 !important; color: #fff !important; border-color: #c92f3c !important; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
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#shuffle { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// EQUALITY communication complexity. Alice has an 8-bit string x; she may send
// only a k-bit deterministic message. We model her message as a fixed k-bit
// "fingerprint" of x. The point: if k < 8, by pigeonhole two distinct strings
// collide onto the same message, so Bob cannot decide EQUALITY reliably.
const N = 8;
let k = 8;
let alice = randStr();
function randStr(){ return Array.from({length:N}, () => Math.random()<0.5?0:1); }
function toStr(a){ return a.join(''); }
// Message Alice sends: the first k bits of a fixed scramble of x.
// (Any deterministic map to k<N bits has the same fatal property.)
function message(bits){
// XOR-fold the 8 bits down to k bits deterministically.
const m = new Array(k).fill(0);
for (let i=0;i<N;i++) m[i%k] ^= bits[i];
return m.join('');
}
// Search all 256 possible strings Alice could hold for two distinct ones that
// share Alice's current message -> a collision that breaks the protocol.
function findCollision(){
const mine = message(alice);
for (let v=0; v<(1<<N); v++){
const b = []; for(let i=0;i<N;i++) b.push((v>>i)&1);
if (toStr(b) === toStr(alice)) continue;
if (message(b) === mine) return b;
}
return null;
}
function renderBits(id, bits, mark){
const el = document.getElementById(id); el.innerHTML='';
bits.forEach(bt => { const d=document.createElement('div');
d.className='bit'+(mark?' collide':''); d.textContent=bt; el.appendChild(d); });
}
function setStatus(t,cls){ const s=document.getElementById('status'); s.innerHTML=t; s.className='status '+cls; }
function render(){
document.getElementById('kval').textContent = k;
renderBits('ax', alice, false);
// Bob shown as a copy of Alice for the "honest equal" case; reset to neutral.
renderBits('bx', alice, false);
document.getElementById('msg').textContent = message(alice);
setStatus(fmt('k = %k% bits. Press Test to run the protocol.', { k: k }),'');
}
function test(){
const mine = message(alice);
document.getElementById('msg').textContent = mine;
if (k >= N){
setStatus('✓ With k = 8, Alice’s message <b>is</b> her whole string. Bob compares exactly and EQUALITY is always correct. Cost: n+1 bits.','ok');
renderBits('bx', alice, false);
return;
}
const twin = findCollision();
if (twin){
renderBits('ax', alice, true);
renderBits('bx', twin, true);
setStatus(fmt('✗ Collision! Alice could hold <b><b>%toStr%</b></b> or <b><b>%toStr2%</b></b> — both send the same message <b><b>%mine%</b></b>. Bob can’t tell them apart, so for some inputs his EQUALITY answer is wrong. k = %k% < 8 is not enough.', { toStr: toStr(alice), toStr2: toStr(twin), mine: mine, k: k }),'bad');
console.log('Collision:', toStr(alice), '&', toStr(twin), '-> message', mine);
} else {
setStatus(fmt('No collision for this particular string at k = %k% — try New random strings; with k<8 one always exists somewhere.', { k: k }),'');
}
}
document.getElementById('k').addEventListener('input', e => { k = +e.target.value; render(); });
document.getElementById('shuffle').addEventListener('click', () => { alice = randStr(); render(); });
document.getElementById('test').addEventListener('click', test);
render();
Slide k below 8 and the demo hunts for a collision: two different strings Alice might hold that produce the same message. When that happens, Bob can no longer tell them apart, so for some inputs his answer is simply wrong. Only when Alice is allowed to send all 8 bits — essentially her whole string — does EQUALITY become reliable. That stubborn wall is the lower bound in action.
Comments
Loading comments...