Definition
Row · column
Each C[i][j] is a dot product of row i of A with column j of B.
Standard matrix multiplication builds each result cell as a row × column dot product: C[i][j] = sum(A[i][k] * B[k][j]), only when inner dimensions match. This tutorial covers the rule, triple nested loops, a live preview, worked Python examples, edge cases, and complexity.
Row · column
Each C[i][j] is a dot product of row i of A with column j of B.
(m×n)(n×p)
AB exists only when columns of A equal rows of B; result is m×p.
i, j, k
Outer i/j pick a cell; inner k accumulates products.
Start at 0
Every result cell is a running sum — initialize before adding.
Show 3×3
Run the classic 3×3 sample product in the browser.
Not cell×cell
This is true matrix product — not element-wise multiply.
Matrix multiplication combines a row from A with a column from B. If A is m × n and B is n × p, then AB is defined and has size m × p, with C[i][j] = sum over k of A[i][k] * B[k][j].
This is not cell-by-cell multiplication (Hadamard product). Order matters: AB is generally not equal to BA.
It is a classic interview problem that tests 2D indexing, accumulation, and understanding of linear-algebra shape rules.
Each output entry is one row dotted with one column.
cols(A) must equal rows(B).
i and j pick the cell; k walks the shared dimension.
Square case uses cubic work with the basic algorithm.
In short: if shapes are compatible, zero-init C, then for each i, j accumulate A[i][k] * B[k][j] over k.
Given compatible matrices A and B, build product C = AB using row–column dot products.
# (m x n) * (n x p) -> (m x p)
# C[i][j] = A[i][0]*B[0][j] + A[i][1]*B[1][j] + ...
# Not the same as A[i][j] * B[i][j] | Item | Type | Description |
|---|---|---|
A, B | list[list[int]] | Compatible matrices: cols(A) == rows(B). |
| Return / print | matrix / text | Product matrix of size rows(A) × cols(B). |
function multiply(A, B):
n <- size
C <- n x n matrix of zeros
for i from 0 to n - 1:
for j from 0 to n - 1:
for k from 0 to n - 1:
C[i][j] <- C[i][j] + A[i][k] * B[k][j]
return C | Method | Idea | Notes |
|---|---|---|
| Triple nested loops | Accumulate A[i][k]*B[k][j] | Interview default — clear and correct |
| Element-wise (Hadamard) | A[i][j] * B[i][j] | Different operation — same shape required |
NumPy @ / matmul | Library product | Production path; show loops in interviews |
| Goal | Pattern |
|---|---|
| Accumulate cell | result[i][j] += a[i][k] * b[k][j] |
| Triple loop | for i / for j / for k |
| Zero-init | [[0 for _ in range(n)] for _ in range(n)] |
| Shape check | len(a[0]) == len(b) (cols A == rows B) |
| Result size | m x p when A is m x n, B is n x p |
| Trace one cell | Top-left 3×3 sample: 1*9 + 2*6 + 3*3 = 30 |
Same word “multiply” — very different meanings.
sum A[i][k]*B[k][j]This page — classic interview style
A[i][j] * B[i][j]Element-wise — needs same shape
A @ BFast in apps; show loops in interviews
state shape firstSay (m×n)(n×p)→m×p before coding
Reach for true matrix products when rows must combine with columns.
Triple loops plus shape rules are a common warm-up.
Natural step up from addition/division in this chain.
Compose maps, graphics, and simple ML layers.
Practice running sums over a shared index k.
Clarify when the interviewer wants cell-by-cell multiply.
Key benefit: one short problem that locks in indexing, accumulation, and the (m×n)(n×p)→m×p rule.
Uses the same 3×3 matrices as Example 1. Click to display both inputs and the product matrix.
Three complete Python programs — classic 3×3 product, easy-to-verify 2×2, and a shape-safe general multiplier. Click View Output to reveal sample console results.
Triple nested loops with a zero-initialized result matrix.
Classic interview-style implementation with helpers for multiply and display.
N = 3
def multiply_matrices(a: list[list[int]], b: list[list[int]]) -> list[list[int]]:
result = [[0 for _ in range(N)] for _ in range(N)]
for i in range(N):
for j in range(N):
for k in range(N):
result[i][j] += a[i][k] * b[k][j]
return result
def display_matrix(matrix: list[list[int]]) -> None:
for row in matrix:
print("\t".join(str(x) for x in row))
def main() -> None:
first_matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
second_matrix = [
[9, 8, 7],
[6, 5, 4],
[3, 2, 1],
]
result = multiply_matrices(first_matrix, second_matrix)
print("First Matrix:")
display_matrix(first_matrix)
print("\nSecond Matrix:")
display_matrix(second_matrix)
print("\nResult Matrix:")
display_matrix(result)
if __name__ == "__main__":
main() The innermost loop computes one dot product for each result cell: result[i][j] += a[i][k] * b[k][j]. Top-left output is 1*9 + 2*6 + 3*3 = 30.
Same logic on a smaller matrix so you can verify by hand.
Same triple-loop pattern with values that are easy to dry-run.
N = 2
def multiply_matrices(a: list[list[int]], b: list[list[int]]) -> list[list[int]]:
result = [[0 for _ in range(N)] for _ in range(N)]
for i in range(N):
for j in range(N):
for k in range(N):
result[i][j] += a[i][k] * b[k][j]
return result
def print_matrix(title: str, m: list[list[int]]) -> None:
print(title)
for row in m:
print(*row)
def main() -> None:
a = [[1, 2], [3, 4]]
b = [[5, 6], [7, 8]]
r = multiply_matrices(a, b)
print_matrix("A", a)
print()
print_matrix("B", b)
print()
print_matrix("AB", r)
if __name__ == "__main__":
main() Top-left value is 1*5 + 2*7 = 19; top-right is 1*6 + 2*8 = 22. Matching the printed AB confirms the accumulation logic.
Drop fixed N and enforce cols(A) == rows(B).
Checks inner dimensions, then multiplies any compatible m×n by n×p.
def can_multiply(a: list[list[int]], b: list[list[int]]) -> bool:
if not a or not b or not a[0] or not b[0]:
return False
cols_a = len(a[0])
rows_b = len(b)
if cols_a != rows_b:
return False
for row in a:
if len(row) != cols_a:
return False
cols_b = len(b[0])
for row in b:
if len(row) != cols_b:
return False
return True
def multiply_safe(a: list[list[int]], b: list[list[int]]) -> list[list[int]] | None:
if not can_multiply(a, b):
return None
m, n, p = len(a), len(a[0]), len(b[0])
result = [[0 for _ in range(p)] for _ in range(m)]
for i in range(m):
for j in range(p):
for k in range(n):
result[i][j] += a[i][k] * b[k][j]
return result
ok = multiply_safe([[1, 2, 3], [4, 5, 6]], [[7, 8], [9, 10], [11, 12]])
bad = multiply_safe([[1, 2], [3, 4]], [[5, 6, 7]])
print(ok)
print(bad) First sample is 2×3 times 3×2 → 2×2. Second fails because columns of A (2) do not match rows of B (1).
Require cols(A) == rows(B); result will be rows(A) × cols(B).
Create an m×p matrix of zeros before accumulation.
For each i, j accumulate A[i][k] * B[k][j] over k.
C holds every row–column dot product.
Trace the first row of C for the Example 1 matrices.
| Cell | Dot product | Value |
|---|---|---|
C[0][0] | 1*9 + 2*6 + 3*3 | 30 |
C[0][1] | 1*8 + 2*5 + 3*2 | 24 |
C[0][2] | 1*7 + 2*4 + 3*1 | 18 |
First row of the result is [30, 24, 18] — matching Example 1 output.
Where true matrix multiplication shows up beyond the interview prompt.
2D indexing plus accumulation over k.
Example: write multiply_matrices(A, B).
Step up from addition/division to products.
Example: compare with Hadamard multiply.
Compose maps in graphics and simple ML.
Example: apply a 2×2 transform to points.
Practice (m×n)(n×p)→m×p checks.
Example: reject incompatible pairs.
State O(n³) for the classic square case.
Example: mention NumPy for large n.
Next page returns to entrywise ops.
Example: continue the matrix chain.
Pro Tip: open with “(m×n)(n×p)→m×p, C[i][j] is a row–column dot product” before writing loops.
Why the classic triple-loop approach works well in interviews.
One rule: C[i][j] = sum of A[i][k] * B[k][j].
2×2 dry-runs verify understanding in seconds.
Inner-dimension checks are a natural follow-up question.
Same idea as NumPy @ — you just write the loops by hand.
Pro Tip: lead with loops and zero-init; mention NumPy @ only as a production aside.
Small habits that keep matrix-multiplication solutions interview-ready.
Say (m×n)(n×p)→m×p before writing code.
Accumulation requires starting at zero.
Always pair A[i][k] with B[k][j].
Avoid [[0]*n]*n shared references.
Square vs rectangular complexity in one sentence.
Pro Tip: dry-run 1*5 + 2*7 = 19 aloud on the 2×2 sample — if that matches, your indexing is correct.
Mistakes that commonly break matrix-multiplication solutions.
Multiplying when cols(A) ≠ rows(B).
→ Validate shapes before looping.
Writing A[i][j] * B[i][j] instead of a dot product.
→ Use three loops and the k index.
Accumulating into uninitialized cells.
→ Start every C[i][j] at 0.
Order usually changes the result (or validity).
→ Multiply in the requested order only.
[[0]*n]*n shares row lists.
→ Build each row separately.
Common beginner mistakes — plus a few more.
Cannot multiply unless columns of A equal rows of B.
Changing order usually changes result, and sometimes makes multiplication invalid.
If result cells do not start at zero, accumulation gives incorrect values.
Validate every row length before multiplying.
Still a product: C[0][0] = A[0][0] * B[0][0].
Use m, n, p — not a single N — when shapes differ.
Handy follow-ups interviewers sometimes ask.
Try these variations to lock in the pattern.
C[i][j] = sum(A[i][k] * B[k][j]), with compatible dimensions.rowsA, colsA, and colsB with the check colsA == rowsB.Quick Takeaway: compatible shapes first, zero-init C, then accumulate A[i][k]*B[k][j] with three nested loops.
| Setting | Time | Extra space |
|---|---|---|
Two n × n matrices, classic triple loop | O(n^3) | O(1) beyond output |
m × n by n × p | O(m*n*p) | O(1) beyond output |
| Shape validation | O(m + n) row checks | O(1) |
For large matrices, production code usually switches to optimized libraries; interviews still expect the triple-loop explanation.
Matrix multiplication builds each result cell as a row–column dot product when inner dimensions match. Zero-init the result, use three nested loops, and distinguish this from element-wise multiplication.
Practice the three examples above, then continue to matrix subtraction for the next entrywise operation.
Compatible shapes first, zero-init C, then C[i][j] += A[i][k] * B[k][j].
Multiply matrices the interview-friendly way.
Row · column
Definition(m×n)(n×p)
Constrainti, j, then k
PatternStart at zero
SafetyO(n³) / O(mnp)
AnalysisMatrix multiplication pairs rows of A with columns of B. If A is m x n and B is n x p, then AB exists and has size m x p.
Learn entrywise subtraction with matching dimensions — a simpler return to cell-by-cell ops.
8 people found this page helpful