25. Dot product, norms, angles between vectors

📖 Reading · 9 min
💡 Every code box below is live — edit it and hit Run.

Everything since §16.0 has used addition and scalar multiplication — Modules 17–19 never once asked how "long" a vector is or how "far apart" two vectors point. This module adds that missing geometry, starting with the single operation everything else in it builds from: the dot product.

The dot product

For \vec u,\vec v\in\mathbb{R}^n,

\vec u\cdot\vec v=u_1v_1+u_2v_2+\cdots+u_nv_n

Equivalently, in matrix form, \vec u\cdot\vec v=\vec u^T\vec v (a 1\times n times an n\times1, producing a 1\times1 — a scalar, §16.2). The dot product is symmetric (\vec u\cdot\vec v=\vec v\cdot\vec u, obviously, since ordinary multiplication commutes) and bilinear — linear in each argument separately: \vec u\cdot(\vec v+\vec w)=\vec u\cdot\vec v+\vec u\cdot\vec w and \vec u\cdot(c\vec v)=c(\vec u\cdot\vec v), both immediate from distributing the sum defining it.

Norm (length)

\|\vec v\|=\sqrt{\vec v\cdot\vec v}=\sqrt{v_1^2+\cdots+v_n^2}

— §16.0's length formula, now expressed through the dot product (the Pythagorean theorem applied n-1 times, extending the familiar \sqrt{x^2+y^2} to any dimension). Properties: \|\vec v\|\ge0, with equality only for \vec v=\vec0; \|c\vec v\|=|c|\,\|\vec v\| (scaling a vector by c scales its length by |c|, matching §16.0's geometric picture of scalar multiplication exactly); and the triangle inequality \|\vec u+\vec v\|\le\|\vec u\|+\|\vec v\| (the straight path is never longer than a detour — not derived here, but used freely).

A unit vector has \|\vec v\|=1; normalizing means replacing \vec v with \vec v/\|\vec v\|, a unit vector in the same direction (§16.0 previewed both).

The Cauchy-Schwarz inequality and angle

|\vec u\cdot\vec v|\le\|\vec u\|\,\|\vec v\|

for all \vec u,\vec vCauchy-Schwarz, arguably the single most load-bearing inequality in this course (§20.1 restates it for general inner products; §21.2's positive-definiteness and §22.2's matrix norms both lean on the same idea). It guarantees -1\le\frac{\vec u\cdot\vec v}{\|\vec u\|\|\vec v\|}\le1, so this ratio is always a valid cosine, defining the angle \theta between two nonzero vectors:

\vec u\cdot\vec v=\|\vec u\|\,\|\vec v\|\cos\theta

This is the Law of Cosines in disguise, and it recovers the familiar 2D dot-product-from-angle formula while extending "angle between two vectors" to any dimension, even where no picture is available.

Orthogonality

\vec u and \vec v are orthogonal (\vec u\perp\vec v) if \vec u\cdot\vec v=0 — from the formula above, exactly \theta=90° (or one of the vectors is \vec0, trivially orthogonal to everything). Orthogonality is this module's central organizing idea: §20.2 studies whole orthogonal subspaces, §20.3–20.4 build orthogonal bases, and §20.5's least squares is, at its core, a single orthogonality condition.

Doing it in Python

import numpy as np

u = np.array([3., -1., 2.])
v = np.array([1., 4., -2.])

dot = u @ v   # @ computes the dot product for 1-D arrays
print("u . v =", dot)
print("||u|| =", round(float(np.linalg.norm(u)), 4))
print("||v|| =", round(float(np.linalg.norm(v)), 4))

cos_theta = dot / (np.linalg.norm(u) * np.linalg.norm(v))
theta = np.arccos(cos_theta)
print("cos(theta) =", round(float(cos_theta), 4))
print("theta (degrees) =", round(float(np.degrees(theta)), 2))
u . v = -5.0
||u|| = 3.7417
||v|| = 4.5826
cos(theta) = -0.2916
theta (degrees) = 106.95

Verifying Cauchy-Schwarz and orthogonality:

import numpy as np

u = np.array([2., 1.])
v = np.array([-1., 2.])   # chosen to be perpendicular to u

print("u . v =", u @ v, " (orthogonal if 0)")

a, b = np.array([3., 4.]), np.array([1., 2.])
print("\n|a.b| =", abs(a @ b))
print("||a||*||b|| =", round(float(np.linalg.norm(a) * np.linalg.norm(b)), 4))
print("Cauchy-Schwarz holds:", abs(a @ b) <= np.linalg.norm(a) * np.linalg.norm(b))
u . v = 0.0  (orthogonal if 0)

|a.b| = 11.0
||a||*||b|| = 11.1803
Cauchy-Schwarz holds: True

Worked example

Find the angle between \vec u=(1,1,0) and \vec v=(1,0,1).

\vec u\cdot\vec v=1(1)+1(0)+0(1)=1. \|\vec u\|=\sqrt{1+1+0}=\sqrt2. \|\vec v\|=\sqrt{1+0+1}=\sqrt2.

\cos\theta=\frac1{\sqrt2\cdot\sqrt2}=\frac12\ \Longrightarrow\ \theta=60°

\boxed{\theta=60°}

Sanity check. \vec u and \vec v are two edges of a cube meeting at a shared vertex (both length \sqrt2, the face diagonals) — 60° matches the well-known fact that a cube's face diagonals from a shared vertex meet at exactly this angle, a useful independent geometric confirmation beyond just re-checking the arithmetic.

Your turn

1. Compute \vec u\cdot\vec v for \vec u=(2,-3,1), \vec v=(4,1,-2), and state whether they're orthogonal.

2. Find a unit vector in the direction of \vec w=(6,-8).

3. True or false: if \|\vec u+\vec v\|=\|\vec u\|+\|\vec v\|, then \vec u and \vec v point in the same direction.

Solutions

1. 2(4)+(-3)(1)+1(-2)=8-3-2=3\neq0not orthogonal.

2. \|\vec w\|=\sqrt{36+64}=\sqrt{100}=10. Unit vector: \vec w/10=(0.6,-0.8) — check: \sqrt{0.36+0.64}=\sqrt1=1 ✓.

3. True. This is the equality case of the triangle inequality: it holds exactly when \vec u,\vec v are nonnegative multiples of each other (both zero, or both pointing the same way) — any deviation in direction makes the "detour" through separate vectors strictly longer than a straight combined path, which is the geometric content of the inequality being strict except in this one case.

Check yourself in code

For \vec u=(4,0,3) and \vec v=(0,5,0), compute the dot product, both norms, and the angle between them in degrees (rounded to 2 dp).

Print exactly this:

u . v = 0.0
||u|| = 5.0
||v|| = 5.0
angle (deg) = 90.0
import numpy as np

u = np.array([4., 0., 3.])
v = np.array([0., 5., 0.])

print("u . v =", u @ v)
# print ||u||, ||v||, and the angle between them in degrees, all rounded appropriately
import numpy as np

u = np.array([4., 0., 3.])
v = np.array([0., 5., 0.])

dot = u @ v
print("u . v =", dot)
print("||u|| =", round(float(np.linalg.norm(u)), 4))
print("||v|| =", round(float(np.linalg.norm(v)), 4))

theta = np.degrees(np.arccos(dot / (np.linalg.norm(u) * np.linalg.norm(v))))
print("angle (deg) =", round(float(theta), 2))

The dot product \vec u\cdot\vec v=\sum u_iv_i gives length (\|\vec v\|=\sqrt{\vec v\cdot\vec v}) and angle (\cos\theta=\vec u\cdot\vec v/\|\vec u\|\|\vec v\|), guaranteed to be a valid cosine by Cauchy-Schwarz. Orthogonality — \vec u\cdot\vec v=0 — is the special case \theta=90°, and this module's central idea from here on.

Next: stripping the dot product down to its essential properties and generalizing — an inner product on any vector space, not just \mathbb{R}^n.