Below is the optimal sorting network for 4 wires: exactly 5 comparators, arranged in 3 depth-layers. Press Step to fire one comparator at a time — it lights up the two wires it touches and turns red when it actually swaps. Press New input to scramble the values and watch the same fixed sequence sort them again.
<p class="hint">A <b>sorting network</b> is a fixed list of compare-and-swap steps. Each step picks two wires; if the top value is bigger than the bottom one, it swaps them. The magic: one fixed sequence sorts <b>every</b> possible input. This is the optimal 4-wire network — 5 comparators, depth 3.</p>
<div class="wires" id="wires"></div>
<div class="status" id="status">Press Step to run the next comparator.</div>
<div class="btns">
<button id="step" type="button">Step</button>
<button id="run" type="button">Run all</button>
<button id="shuffle" type="button" class="ghost">New input</button>
<button id="reset" type="button" class="ghost">Reset</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; }
.wires { display: flex; flex-direction: column; gap: 10px; margin: .6rem 0; }
.wire { display: flex; align-items: center; gap: 8px; }
.wlabel { width: 22px; font: 600 13px ui-monospace, monospace; color: #888; }
.cell { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
font: 700 18px ui-monospace, monospace; border-radius: 8px; background: #e8eef3;
color: #1d3557; border: 1px solid #cdd9e3; transition: all .18s; }
.cell.active { background: #1d3557; color: #fff; border-color: #1d3557; transform: scale(1.08); }
.cell.swapped { background: #e63946; color: #fff; border-color: #c92f3c; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.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.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .45; cursor: default; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// A sorting network is a fixed list of comparators (i,j): compare wire i and wire j, swap so the smaller ends on the lower index. This is the provably OPTIMAL network for n=4: 5 comparators, depth 3 (no 4-comparator network can sort all 4! = 24 inputs). It sorts ANY input with the same fixed steps.
const NET = [[0,1],[2,3],[0,2],[1,3],[1,2]];
let data = [3,1,4,2];
let step = 0;
function render(active, swapped){
const w = document.getElementById('wires'); w.innerHTML = '';
for(let i=0;i<data.length;i++){
const row = document.createElement('div'); row.className = 'wire';
const lab = document.createElement('div'); lab.className = 'wlabel'; lab.textContent = 'w'+i;
const c = document.createElement('div'); c.className = 'cell'; c.textContent = data[i];
if(active && active.includes(i)) c.classList.add('active');
if(swapped && swapped.includes(i)) c.classList.add('swapped');
row.appendChild(lab); row.appendChild(c); w.appendChild(row);
}
}
function setStatus(t,cls){ const s=document.getElementById('status'); s.textContent=t; s.className='status '+cls; }
function sortedMsg(){
const ok = data.every((v,i)=> i===0 || data[i-1] <= v);
if(ok){ setStatus('✓ Sorted — every input reaches this same order.','ok'); document.getElementById('step').disabled=true; document.getElementById('run').disabled=true; }
}
function doStep(){
if(step >= NET.length){ sortedMsg(); return; }
const [i,j] = NET[step];
let swapped = null;
if(data[i] > data[j]){ [data[i],data[j]] = [data[j],data[i]]; swapped = [i,j]; }
step++;
render([i,j], swapped);
setStatus(fmt('Comparator %step%/%length%: compare w%i% & w%j%%v%', { step: step, length: NET.length, i: i, j: j, v: ' → ' + (swapped?'swap':'keep') }),'');
if(step >= NET.length) sortedMsg();
}
function runAll(){ while(step < NET.length) { const [i,j]=NET[step]; if(data[i]>data[j])[data[i],data[j]]=[data[j],data[i]]; step++; } render(); sortedMsg(); }
function shuffle(){
const base=[1,2,3,4];
for(let k=base.length-1;k>0;k--){ const r=Math.floor(Math.random()*(k+1)); [base[k],base[r]]=[base[r],base[k]]; }
data=base; step=0; document.getElementById('step').disabled=false; document.getElementById('run').disabled=false;
render(); setStatus('New input. Press Step to run the next comparator.','');
}
function reset(){ step=0; document.getElementById('step').disabled=false; document.getElementById('run').disabled=false; render(); setStatus('Press Step to run the next comparator.',''); }
document.getElementById('step').addEventListener('click',doStep);
document.getElementById('run').addEventListener('click',runAll);
document.getElementById('shuffle').addEventListener('click',shuffle);
document.getElementById('reset').addEventListener('click',reset);
render();
<p class="hint">A <b>sorting network</b> is a fixed list of compare-and-swap steps. Each step picks two wires; if the top value is bigger than the bottom one, it swaps them. The magic: one fixed sequence sorts <b>every</b> possible input. This is the optimal 4-wire network — 5 comparators, depth 3.</p>
<div class="wires" id="wires"></div>
<div class="status" id="status">Press Step to run the next comparator.</div>
<div class="btns">
<button id="step" type="button">Step</button>
<button id="run" type="button">Run all</button>
<button id="shuffle" type="button" class="ghost">New input</button>
<button id="reset" type="button" class="ghost">Reset</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; }
.wires { display: flex; flex-direction: column; gap: 10px; margin: .6rem 0; }
.wire { display: flex; align-items: center; gap: 8px; }
.wlabel { width: 22px; font: 600 13px ui-monospace, monospace; color: #888; }
.cell { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
font: 700 18px ui-monospace, monospace; border-radius: 8px; background: #e8eef3;
color: #1d3557; border: 1px solid #cdd9e3; transition: all .18s; }
.cell.active { background: #1d3557; color: #fff; border-color: #1d3557; transform: scale(1.08); }
.cell.swapped { background: #e63946; color: #fff; border-color: #c92f3c; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.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.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .45; cursor: default; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// A sorting network is a fixed list of comparators (i,j): compare wire i and wire j, swap so the smaller ends on the lower index. This is the provably OPTIMAL network for n=4: 5 comparators, depth 3 (no 4-comparator network can sort all 4! = 24 inputs). It sorts ANY input with the same fixed steps.
const NET = [[0,1],[2,3],[0,2],[1,3],[1,2]];
let data = [3,1,4,2];
let step = 0;
function render(active, swapped){
const w = document.getElementById('wires'); w.innerHTML = '';
for(let i=0;i<data.length;i++){
const row = document.createElement('div'); row.className = 'wire';
const lab = document.createElement('div'); lab.className = 'wlabel'; lab.textContent = 'w'+i;
const c = document.createElement('div'); c.className = 'cell'; c.textContent = data[i];
if(active && active.includes(i)) c.classList.add('active');
if(swapped && swapped.includes(i)) c.classList.add('swapped');
row.appendChild(lab); row.appendChild(c); w.appendChild(row);
}
}
function setStatus(t,cls){ const s=document.getElementById('status'); s.textContent=t; s.className='status '+cls; }
function sortedMsg(){
const ok = data.every((v,i)=> i===0 || data[i-1] <= v);
if(ok){ setStatus('✓ Sorted — every input reaches this same order.','ok'); document.getElementById('step').disabled=true; document.getElementById('run').disabled=true; }
}
function doStep(){
if(step >= NET.length){ sortedMsg(); return; }
const [i,j] = NET[step];
let swapped = null;
if(data[i] > data[j]){ [data[i],data[j]] = [data[j],data[i]]; swapped = [i,j]; }
step++;
render([i,j], swapped);
setStatus(fmt('Comparator %step%/%length%: compare w%i% & w%j%%v%', { step: step, length: NET.length, i: i, j: j, v: ' → ' + (swapped?'swap':'keep') }),'');
if(step >= NET.length) sortedMsg();
}
function runAll(){ while(step < NET.length) { const [i,j]=NET[step]; if(data[i]>data[j])[data[i],data[j]]=[data[j],data[i]]; step++; } render(); sortedMsg(); }
function shuffle(){
const base=[1,2,3,4];
for(let k=base.length-1;k>0;k--){ const r=Math.floor(Math.random()*(k+1)); [base[k],base[r]]=[base[r],base[k]]; }
data=base; step=0; document.getElementById('step').disabled=false; document.getElementById('run').disabled=false;
render(); setStatus('New input. Press Step to run the next comparator.','');
}
function reset(){ step=0; document.getElementById('step').disabled=false; document.getElementById('run').disabled=false; render(); setStatus('Press Step to run the next comparator.',''); }
document.getElementById('step').addEventListener('click',doStep);
document.getElementById('run').addEventListener('click',runAll);
document.getElementById('shuffle').addEventListener('click',shuffle);
document.getElementById('reset').addEventListener('click',reset);
render();
Here is the punchline you can feel by clicking: the steps never change. The network does not look at your numbers to decide what to do — it runs the identical 5 comparators no matter what, and the output is always sorted. That is why these circuits are called oblivious, and it is exactly what makes them easy to bake into hardware and parallel machines. Sorting 4 items optimally takes 5 comparators; for larger sizes, the optimal count is the hard part.
Comments
Loading comments...