Enter any two 2×2 matrices below. Click Multiply (Strassen) to compute the product using exactly 7 scalar multiplications — M1 through M7 — plus some additions and subtractions. Then click Multiply (Schoolbook) to see all 8 multiplications used the naive way.
<div class="demo-wrap">
<div class="matrices">
<div class="mat-block">
<div class="mat-label">Matrix A</div>
<div class="mat-grid">
<input id="a00" type="number" value="1" /><input id="a01" type="number" value="2" />
<input id="a10" type="number" value="3" /><input id="a11" type="number" value="4" />
</div>
</div>
<div class="times-sign">×</div>
<div class="mat-block">
<div class="mat-label">Matrix B</div>
<div class="mat-grid">
<input id="b00" type="number" value="5" /><input id="b01" type="number" value="6" />
<input id="b10" type="number" value="7" /><input id="b11" type="number" value="8" />
</div>
</div>
<div class="equals-sign">=</div>
<div class="mat-block result-block">
<div class="mat-label">Result C</div>
<div class="mat-grid">
<div class="res-cell" id="c00">?</div><div class="res-cell" id="c01">?</div>
<div class="res-cell" id="c10">?</div><div class="res-cell" id="c11">?</div>
</div>
</div>
</div>
<div class="btns">
<button id="btn-strassen" type="button">Multiply (Strassen) — 7 mults</button>
<button id="btn-schoolbook" type="button" class="ghost">Multiply (Schoolbook) — 8 mults</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
<div class="counters">
<div class="counter-box" id="box-strassen">
<div class="counter-label">Strassen multiplications</div>
<div class="counter-num" id="cnt-strassen">–</div>
</div>
<div class="counter-box" id="box-schoolbook">
<div class="counter-label">Schoolbook multiplications</div>
<div class="counter-num" id="cnt-schoolbook">–</div>
</div>
</div>
<div id="steps-panel" class="steps-panel"></div>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.demo-wrap { max-width: 540px; margin: 0 auto; }
.matrices { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .8rem; }
.mat-block { display: flex; flex-direction: column; align-items: center; gap: .3rem; }
.mat-label { font-size: .78rem; font-weight: 700; color: #1d3557; text-transform: uppercase; letter-spacing: .05em; }
.mat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.mat-grid input { width: 52px; height: 44px; font: 700 16px ui-monospace, monospace; text-align: center;
border: 1.5px solid #c0cad4; border-radius: 8px; background: #f4f7fa; color: #1d3557;
padding: 0; -moz-appearance: textfield; }
.mat-grid input::-webkit-outer-spin-button,
.mat-grid input::-webkit-inner-spin-button { -webkit-appearance: none; }
.mat-grid input:focus { outline: none; border-color: #1d3557; background: #fff; }
.times-sign, .equals-sign { font-size: 1.6rem; font-weight: 300; color: #5a7088; align-self: center; }
.result-block .mat-grid { gap: 4px; }
.res-cell { width: 52px; height: 44px; display: flex; align-items: center; justify-content: center;
font: 700 16px ui-monospace, monospace; background: #e8eef3; border-radius: 8px;
border: 1.5px solid #c0cad4; color: #1d3557; transition: background .3s; }
.res-cell.lit { background: #d4edda; border-color: #0a7d33; color: #0a7d33; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; }
button { font: 600 13px system-ui, sans-serif; padding: .45rem .85rem; border: 1.5px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; white-space: nowrap; }
button.ghost { background: #fff; color: #1d3557; }
button:hover { opacity: .88; }
.counters { display: flex; gap: .6rem; margin-bottom: .8rem; }
.counter-box { flex: 1; background: #f4f7fa; border-radius: 8px; padding: .5rem .7rem; text-align: center;
border: 1.5px solid #c0cad4; transition: border-color .3s; }
.counter-box.active { border-color: #1d3557; }
.counter-label { font-size: .73rem; font-weight: 600; color: #5a7088; margin-bottom: .25rem; }
.counter-num { font: 700 2rem ui-monospace, monospace; color: #1d3557; }
.counter-num.highlight { color: #0a7d33; }
.steps-panel { background: #f4f7fa; border-radius: 8px; padding: .6rem .8rem; font-size: .82rem;
line-height: 1.7; min-height: 2rem; border: 1.5px solid #e0e6ec; display: none; }
.steps-panel.visible { display: block; }
.step-row { display: flex; gap: .4rem; align-items: baseline; }
.step-name { font-weight: 700; color: #1d3557; min-width: 2.4rem; }
.step-val { font: 600 .82rem ui-monospace, monospace; color: #333; }
.method-title { font-weight: 700; font-size: .85rem; color: #1d3557; margin-bottom: .3rem; }
.divider { border: none; border-top: 1px solid #d0d8e0; margin: .5rem 0; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Strassen 2x2 matrix multiply:
// Given A = [[a,b],[c,d]] and B = [[e,f],[g,h]],
// compute 7 scalar multiplications M1..M7, then assemble C.
//
// M1 = (a+d)*(e+h)
// M2 = (c+d)*e
// M3 = a*(f-h)
// M4 = d*(g-e)
// M5 = (a+b)*h
// M6 = (c-a)*(e+f)
// M7 = (b-d)*(g+h)
//
// C[0][0] = M1+M4-M5+M7
// C[0][1] = M3+M5
// C[1][0] = M2+M4
// C[1][1] = M1-M2+M3+M6
function getMatrix() {
const v = id => parseFloat(document.getElementById(id).value) || 0;
return {
a: v('a00'), b: v('a01'), c: v('a10'), d: v('a11'),
e: v('b00'), f: v('b01'), g: v('b10'), h: v('b11')
};
}
function setResult(c00, c01, c10, c11, lit) {
['c00','c01','c10','c11'].forEach((id,i)=>{
const el = document.getElementById(id);
el.textContent = [c00,c01,c10,c11][i];
el.className = 'res-cell' + (lit ? ' lit' : '');
});
}
function showSteps(html) {
const p = document.getElementById('steps-panel');
p.innerHTML = html;
p.className = 'steps-panel visible';
}
function setCounters(strassen, schoolbook) {
const cs = document.getElementById('cnt-strassen');
const cb = document.getElementById('cnt-schoolbook');
const bs = document.getElementById('box-strassen');
const bb = document.getElementById('box-schoolbook');
cs.textContent = strassen !== null ? strassen : '–';
cb.textContent = schoolbook !== null ? schoolbook : '–';
cs.className = 'counter-num' + (strassen !== null && strassen < (schoolbook||99) ? ' highlight' : '');
cb.className = 'counter-num';
bs.className = 'counter-box' + (strassen !== null ? ' active' : '');
bb.className = 'counter-box' + (schoolbook !== null ? ' active' : '');
}
document.getElementById('btn-strassen').addEventListener('click', () => {
const {a,b,c,d,e,f,g,h} = getMatrix();
const M1 = (a+d)*(e+h);
const M2 = (c+d)*e;
const M3 = a*(f-h);
const M4 = d*(g-e);
const M5 = (a+b)*h;
const M6 = (c-a)*(e+f);
const M7 = (b-d)*(g+h);
const c00 = M1+M4-M5+M7;
const c01 = M3+M5;
const c10 = M2+M4;
const c11 = M1-M2+M3+M6;
setResult(c00,c01,c10,c11,true);
setCounters(7, null);
const fmt = v => (v<0?'('+v+')':String(v));
showSteps(
i18nFmt('<div class="method-title">Strassen’s 7 products</div>%map%<hr class="divider"><div class="step-row"><span class="step-name">C₀₀</span><span class="step-val">= M1+M4−M5+M7 = %c00%</span></div><div class="step-row"><span class="step-name">C₀₁</span><span class="step-val">= M3+M5 = %c01%</span></div><div class="step-row"><span class="step-name">C₁₀</span><span class="step-val">= M2+M4 = %c10%</span></div><div class="step-row"><span class="step-name">C₁₁</span><span class="step-val">= M1−M2+M3+M6 = %c11%</span></div>', { map: ['M1=(a+d)·(e+h)','M2=(c+d)·e','M3=a·(f−h)','M4=d·(g−e)',
'M5=(a+b)·h','M6=(c−a)·(e+f)','M7=(b−d)·(g+h)']
.map((label,i)=>{
const vals=[M1,M2,M3,M4,M5,M6,M7];
return '<div class="step-row"><span class="step-name">'+label.split('=')[0]+'</span>' +
'<span class="step-val">= '+label.split('=')[1]+' = '+vals[i]+'</span></div>';
}).join(''), c00: c00, c01: c01, c10: c10, c11: c11 })
);
console.log('Strassen result:', c00, c01, c10, c11);
});
document.getElementById('btn-schoolbook').addEventListener('click', () => {
const {a,b,c,d,e,f,g,h} = getMatrix();
// 8 explicit multiplications
const p1=a*e, p2=b*g, p3=a*f, p4=b*h, p5=c*e, p6=d*g, p7=c*f, p8=d*h;
const c00=p1+p2, c01=p3+p4, c10=p5+p6, c11=p7+p8;
setResult(c00,c01,c10,c11,false);
setCounters(null, 8);
showSteps(
i18nFmt('<div class="method-title">Schoolbook — 8 products</div>%map%<hr class="divider"><div class="step-row"><span class="step-name">C₀₀</span><span class="step-val">= P1+P2 = %c00%</span></div><div class="step-row"><span class="step-name">C₀₁</span><span class="step-val">= P3+P4 = %c01%</span></div><div class="step-row"><span class="step-name">C₁₀</span><span class="step-val">= P5+P6 = %c10%</span></div><div class="step-row"><span class="step-name">C₁₁</span><span class="step-val">= P7+P8 = %c11%</span></div>', { map: [['a·e',p1],['b·g',p2],['a·f',p3],['b·h',p4],
['c·e',p5],['d·g',p6],['c·f',p7],['d·h',p8]]
.map(([label,v],i)=>'<div class="step-row"><span class="step-name">P'+(i+1)+'</span>' +
'<span class="step-val">= '+label+' = '+v+'</span></div>').join(''), c00: c00, c01: c01, c10: c10, c11: c11 })
);
console.log('Schoolbook result:', c00, c01, c10, c11);
});
document.getElementById('btn-reset').addEventListener('click', () => {
['a00','a01','a10','a11'].forEach((id,i)=>{ document.getElementById(id).value=[1,2,3,4][i]; });
['b00','b01','b10','b11'].forEach((id,i)=>{ document.getElementById(id).value=[5,6,7,8][i]; });
setResult('?','?','?','?',false);
setCounters(null, null);
document.getElementById('steps-panel').className='steps-panel';
});
<div class="demo-wrap">
<div class="matrices">
<div class="mat-block">
<div class="mat-label">Matrix A</div>
<div class="mat-grid">
<input id="a00" type="number" value="1" /><input id="a01" type="number" value="2" />
<input id="a10" type="number" value="3" /><input id="a11" type="number" value="4" />
</div>
</div>
<div class="times-sign">×</div>
<div class="mat-block">
<div class="mat-label">Matrix B</div>
<div class="mat-grid">
<input id="b00" type="number" value="5" /><input id="b01" type="number" value="6" />
<input id="b10" type="number" value="7" /><input id="b11" type="number" value="8" />
</div>
</div>
<div class="equals-sign">=</div>
<div class="mat-block result-block">
<div class="mat-label">Result C</div>
<div class="mat-grid">
<div class="res-cell" id="c00">?</div><div class="res-cell" id="c01">?</div>
<div class="res-cell" id="c10">?</div><div class="res-cell" id="c11">?</div>
</div>
</div>
</div>
<div class="btns">
<button id="btn-strassen" type="button">Multiply (Strassen) — 7 mults</button>
<button id="btn-schoolbook" type="button" class="ghost">Multiply (Schoolbook) — 8 mults</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
<div class="counters">
<div class="counter-box" id="box-strassen">
<div class="counter-label">Strassen multiplications</div>
<div class="counter-num" id="cnt-strassen">–</div>
</div>
<div class="counter-box" id="box-schoolbook">
<div class="counter-label">Schoolbook multiplications</div>
<div class="counter-num" id="cnt-schoolbook">–</div>
</div>
</div>
<div id="steps-panel" class="steps-panel"></div>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.demo-wrap { max-width: 540px; margin: 0 auto; }
.matrices { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .8rem; }
.mat-block { display: flex; flex-direction: column; align-items: center; gap: .3rem; }
.mat-label { font-size: .78rem; font-weight: 700; color: #1d3557; text-transform: uppercase; letter-spacing: .05em; }
.mat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.mat-grid input { width: 52px; height: 44px; font: 700 16px ui-monospace, monospace; text-align: center;
border: 1.5px solid #c0cad4; border-radius: 8px; background: #f4f7fa; color: #1d3557;
padding: 0; -moz-appearance: textfield; }
.mat-grid input::-webkit-outer-spin-button,
.mat-grid input::-webkit-inner-spin-button { -webkit-appearance: none; }
.mat-grid input:focus { outline: none; border-color: #1d3557; background: #fff; }
.times-sign, .equals-sign { font-size: 1.6rem; font-weight: 300; color: #5a7088; align-self: center; }
.result-block .mat-grid { gap: 4px; }
.res-cell { width: 52px; height: 44px; display: flex; align-items: center; justify-content: center;
font: 700 16px ui-monospace, monospace; background: #e8eef3; border-radius: 8px;
border: 1.5px solid #c0cad4; color: #1d3557; transition: background .3s; }
.res-cell.lit { background: #d4edda; border-color: #0a7d33; color: #0a7d33; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; }
button { font: 600 13px system-ui, sans-serif; padding: .45rem .85rem; border: 1.5px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; white-space: nowrap; }
button.ghost { background: #fff; color: #1d3557; }
button:hover { opacity: .88; }
.counters { display: flex; gap: .6rem; margin-bottom: .8rem; }
.counter-box { flex: 1; background: #f4f7fa; border-radius: 8px; padding: .5rem .7rem; text-align: center;
border: 1.5px solid #c0cad4; transition: border-color .3s; }
.counter-box.active { border-color: #1d3557; }
.counter-label { font-size: .73rem; font-weight: 600; color: #5a7088; margin-bottom: .25rem; }
.counter-num { font: 700 2rem ui-monospace, monospace; color: #1d3557; }
.counter-num.highlight { color: #0a7d33; }
.steps-panel { background: #f4f7fa; border-radius: 8px; padding: .6rem .8rem; font-size: .82rem;
line-height: 1.7; min-height: 2rem; border: 1.5px solid #e0e6ec; display: none; }
.steps-panel.visible { display: block; }
.step-row { display: flex; gap: .4rem; align-items: baseline; }
.step-name { font-weight: 700; color: #1d3557; min-width: 2.4rem; }
.step-val { font: 600 .82rem ui-monospace, monospace; color: #333; }
.method-title { font-weight: 700; font-size: .85rem; color: #1d3557; margin-bottom: .3rem; }
.divider { border: none; border-top: 1px solid #d0d8e0; margin: .5rem 0; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Strassen 2x2 matrix multiply:
// Given A = [[a,b],[c,d]] and B = [[e,f],[g,h]],
// compute 7 scalar multiplications M1..M7, then assemble C.
//
// M1 = (a+d)*(e+h)
// M2 = (c+d)*e
// M3 = a*(f-h)
// M4 = d*(g-e)
// M5 = (a+b)*h
// M6 = (c-a)*(e+f)
// M7 = (b-d)*(g+h)
//
// C[0][0] = M1+M4-M5+M7
// C[0][1] = M3+M5
// C[1][0] = M2+M4
// C[1][1] = M1-M2+M3+M6
function getMatrix() {
const v = id => parseFloat(document.getElementById(id).value) || 0;
return {
a: v('a00'), b: v('a01'), c: v('a10'), d: v('a11'),
e: v('b00'), f: v('b01'), g: v('b10'), h: v('b11')
};
}
function setResult(c00, c01, c10, c11, lit) {
['c00','c01','c10','c11'].forEach((id,i)=>{
const el = document.getElementById(id);
el.textContent = [c00,c01,c10,c11][i];
el.className = 'res-cell' + (lit ? ' lit' : '');
});
}
function showSteps(html) {
const p = document.getElementById('steps-panel');
p.innerHTML = html;
p.className = 'steps-panel visible';
}
function setCounters(strassen, schoolbook) {
const cs = document.getElementById('cnt-strassen');
const cb = document.getElementById('cnt-schoolbook');
const bs = document.getElementById('box-strassen');
const bb = document.getElementById('box-schoolbook');
cs.textContent = strassen !== null ? strassen : '–';
cb.textContent = schoolbook !== null ? schoolbook : '–';
cs.className = 'counter-num' + (strassen !== null && strassen < (schoolbook||99) ? ' highlight' : '');
cb.className = 'counter-num';
bs.className = 'counter-box' + (strassen !== null ? ' active' : '');
bb.className = 'counter-box' + (schoolbook !== null ? ' active' : '');
}
document.getElementById('btn-strassen').addEventListener('click', () => {
const {a,b,c,d,e,f,g,h} = getMatrix();
const M1 = (a+d)*(e+h);
const M2 = (c+d)*e;
const M3 = a*(f-h);
const M4 = d*(g-e);
const M5 = (a+b)*h;
const M6 = (c-a)*(e+f);
const M7 = (b-d)*(g+h);
const c00 = M1+M4-M5+M7;
const c01 = M3+M5;
const c10 = M2+M4;
const c11 = M1-M2+M3+M6;
setResult(c00,c01,c10,c11,true);
setCounters(7, null);
const fmt = v => (v<0?'('+v+')':String(v));
showSteps(
i18nFmt('<div class="method-title">Strassen’s 7 products</div>%map%<hr class="divider"><div class="step-row"><span class="step-name">C₀₀</span><span class="step-val">= M1+M4−M5+M7 = %c00%</span></div><div class="step-row"><span class="step-name">C₀₁</span><span class="step-val">= M3+M5 = %c01%</span></div><div class="step-row"><span class="step-name">C₁₀</span><span class="step-val">= M2+M4 = %c10%</span></div><div class="step-row"><span class="step-name">C₁₁</span><span class="step-val">= M1−M2+M3+M6 = %c11%</span></div>', { map: ['M1=(a+d)·(e+h)','M2=(c+d)·e','M3=a·(f−h)','M4=d·(g−e)',
'M5=(a+b)·h','M6=(c−a)·(e+f)','M7=(b−d)·(g+h)']
.map((label,i)=>{
const vals=[M1,M2,M3,M4,M5,M6,M7];
return '<div class="step-row"><span class="step-name">'+label.split('=')[0]+'</span>' +
'<span class="step-val">= '+label.split('=')[1]+' = '+vals[i]+'</span></div>';
}).join(''), c00: c00, c01: c01, c10: c10, c11: c11 })
);
console.log('Strassen result:', c00, c01, c10, c11);
});
document.getElementById('btn-schoolbook').addEventListener('click', () => {
const {a,b,c,d,e,f,g,h} = getMatrix();
// 8 explicit multiplications
const p1=a*e, p2=b*g, p3=a*f, p4=b*h, p5=c*e, p6=d*g, p7=c*f, p8=d*h;
const c00=p1+p2, c01=p3+p4, c10=p5+p6, c11=p7+p8;
setResult(c00,c01,c10,c11,false);
setCounters(null, 8);
showSteps(
i18nFmt('<div class="method-title">Schoolbook — 8 products</div>%map%<hr class="divider"><div class="step-row"><span class="step-name">C₀₀</span><span class="step-val">= P1+P2 = %c00%</span></div><div class="step-row"><span class="step-name">C₀₁</span><span class="step-val">= P3+P4 = %c01%</span></div><div class="step-row"><span class="step-name">C₁₀</span><span class="step-val">= P5+P6 = %c10%</span></div><div class="step-row"><span class="step-name">C₁₁</span><span class="step-val">= P7+P8 = %c11%</span></div>', { map: [['a·e',p1],['b·g',p2],['a·f',p3],['b·h',p4],
['c·e',p5],['d·g',p6],['c·f',p7],['d·h',p8]]
.map(([label,v],i)=>'<div class="step-row"><span class="step-name">P'+(i+1)+'</span>' +
'<span class="step-val">= '+label+' = '+v+'</span></div>').join(''), c00: c00, c01: c01, c10: c10, c11: c11 })
);
console.log('Schoolbook result:', c00, c01, c10, c11);
});
document.getElementById('btn-reset').addEventListener('click', () => {
['a00','a01','a10','a11'].forEach((id,i)=>{ document.getElementById(id).value=[1,2,3,4][i]; });
['b00','b01','b10','b11'].forEach((id,i)=>{ document.getElementById(id).value=[5,6,7,8][i]; });
setResult('?','?','?','?',false);
setCounters(null, null);
document.getElementById('steps-panel').className='steps-panel';
});
Watch the multiplication counter on each side. The schoolbook needs 8 multiplications; Strassen uses only 7. For a single 2×2 case the savings look modest, but recurse this on n×n blocks and the gap grows to O(n2.807) vs O(n3).
Comments
Loading comments...