Module 3 · Lesson 5
Outer Operations & Pairwise Computation via Broadcasting
np.outer(a, b) computes every product a[i] * b[j] and arranges them into
a matrix of shape (len(a), len(b)). More generally, you can build any
pairwise table — differences, distances, comparisons — by reshaping one
array to a column (n, 1) and the other to a row (1, m), then letting
broadcasting fill in the full (n, m) grid. This avoids a nested Python
loop entirely.
a = np.array([1, 2, 3])
b = np.array([10, 20])
print(np.outer(a, b)) # every a[i] * b[j]
# Pairwise absolute differences, without np.outer:
diffs = np.abs(a[:, np.newaxis] - b[np.newaxis, :])
print(diffs)
print(diffs.shape) # (3, 2): one row per element of a
Output:
[[10 20]
[20 40]
[30 60]]
[[ 9 19]
[ 8 18]
[ 7 17]]
(3, 2)
Pairwise squared distances
Write pairwise_sq_dist(a, b) that takes two 1-D arrays and returns a 2-D
array of shape (len(a), len(b)) where entry [i, j] is
(a[i] - b[j]) ** 2. No nested loop — use broadcasting with np.newaxis.
💡 Hint
a[:, np.newaxis] has shape (len(a), 1); b[np.newaxis, :] has shape (1, len(b)). Subtract them, then square.
✅ Solution
def pairwise_sq_dist(a, b):
return (a[:, np.newaxis] - b[np.newaxis, :]) ** 2
Multiplication table
Write multiplication_table(n) that returns the n x n multiplication
table (entry [i, j] equals (i+1) * (j+1)) using np.outer on
np.arange(1, n+1) — not a loop.
💡 Hint
np.arange(1, n + 1) gives [1, 2, ..., n]; pass it to np.outer twice (as both arguments).
✅ Solution
def multiplication_table(n):
r = np.arange(1, n + 1)
return np.outer(r, r)