Visual Tools
Calculators
Tables
Mathematical Keyboard
Converters
Other Tools


Gram Schmidt Process






Converting Any Basis into an Orthogonal One

The Gram-Schmidt process takes a set of linearly independent vectors and produces an orthogonal (or orthonormal) set spanning the same subspace. It works by sequentially stripping each vector of its components along previously computed directions, leaving only the perpendicular remainder. The result is a constructive proof that orthonormal bases always exist — and the matrix version of this process is the QR decomposition.



The Goal

The input is a set of linearly independent vectors {v1,v2,…,vk}\{\mathbf{v}_1, \mathbf{v}_2, \dots, \mathbf{v}_k\} in an inner product space. The output is an orthogonal set {u1,u2,…,uk}\{\mathbf{u}_1, \mathbf{u}_2, \dots, \mathbf{u}_k\} satisfying two conditions: the vectors are pairwise perpendicular (ui⋅uj=0\mathbf{u}_i \cdot \mathbf{u}_j = 0 for i≠ji \neq j), and they span the same subspace (Span{u1,…,uj}=Span{v1,…,vj}\text{Span}\{\mathbf{u}_1, \dots, \mathbf{u}_j\} = \text{Span}\{\mathbf{v}_1, \dots, \mathbf{v}_j\} at every step jj).

Optionally, each ui\mathbf{u}_i is normalized to unit length, producing an orthonormal set {q1,…,qk}\{\mathbf{q}_1, \dots, \mathbf{q}_k\}.

The process is the constructive proof that every finite-dimensional inner product space has an orthonormal basis.

THEOREM
Given any basis, Gram-Schmidt produces an orthonormal one for the same space.

The Algorithm: Two Vectors

Start with two independent vectors v1\mathbf{v}_1 and v2\mathbf{v}_2.

Set u1=v1\mathbf{u}_1 = \mathbf{v}_1.

Subtract the projection of v2\mathbf{v}_2 onto u1\mathbf{u}_1:

The Algorithm: Two Vectors
u2=v2−u1⋅v2u1⋅u1 u1\mathbf{u}_2 = \mathbf{v}_2 - \frac{\mathbf{u}_1 \cdot \mathbf{v}_2}{\mathbf{u}_1 \cdot \mathbf{u}_1}\,\mathbf{u}_1

The subtracted term is the component of v2\mathbf{v}_2 along u1\mathbf{u}_1. Removing it leaves only the component perpendicular to u1\mathbf{u}_1, so u1⋅u2=0\mathbf{u}_1 \cdot \mathbf{u}_2 = 0.

Worked Example
v1=(1,1,0)\mathbf{v}_1 = (1, 1, 0), v2=(1,0,1)\mathbf{v}_2 = (1, 0, 1).
u1=(1,1,0)\mathbf{u}_1 = (1, 1, 0). u1⋅v2u1⋅u1=12\frac{\mathbf{u}_1 \cdot \mathbf{v}_2}{\mathbf{u}_1 \cdot \mathbf{u}_1} = \frac{1}{2}. u2=(1,0,1)−12(1,1,0)=(12,−12,1)\mathbf{u}_2 = (1, 0, 1) - \frac{1}{2}(1, 1, 0) = (\frac{1}{2}, -\frac{1}{2}, 1).
Check: u1⋅u2=12−12+0=0\mathbf{u}_1 \cdot \mathbf{u}_2 = \frac{1}{2} - \frac{1}{2} + 0 = 0.

Pitfall: Dropping the denominator
Subtracting (u1⋅v2) u1=1⋅(1,1,0)(\mathbf{u}_1 \cdot \mathbf{v}_2)\,\mathbf{u}_1 = 1 \cdot (1, 1, 0) gives (0,−1,1)(0, -1, 1), whose dot product with u1\mathbf{u}_1 is −1-1, not 00. Dividing by u1⋅u1=2\mathbf{u}_1 \cdot \mathbf{u}_1 = 2 gives u2=(12,−12,1)\mathbf{u}_2 = (\frac{1}{2}, -\frac{1}{2}, 1).

The coefficient in front of u1\mathbf{u}_1 is a ratio of two dot products. The numerator u1⋅v2\mathbf{u}_1 \cdot \mathbf{v}_2 measures how far v2\mathbf{v}_2 reaches along u1\mathbf{u}_1, and the denominator u1⋅u1=∥u1∥2\mathbf{u}_1 \cdot \mathbf{u}_1 = \|\mathbf{u}_1\|^2 accounts for the length of u1\mathbf{u}_1 itself, so rescaling u1\mathbf{u}_1 leaves the subtracted vector unchanged. When u1\mathbf{u}_1 is a unit vector the denominator equals 11, which is why the formula becomes shorter once the vectors are normalized.
V3×3v1,1​v1,2​v1,3​v2,1​v2,2​v2,3​v3,1​v3,2​v3,3​→U3×3v1,1​v1,2​v1,3​v2,1​−c2,1​u1,1​v2,2​−c2,1​u1,2​v2,3​−c2,1​u1,3​v3,1​−c3,1​u1,1​−c3,2​u2,1​v3,2​−c3,1​u1,2​−c3,2​u2,2​v3,3​−c3,1​u1,3​−c3,2​u2,3​
The overlap removed from the second vector

The projection of this vector onto the direction already fixed is being subtracted away. What survives is orthogonal to that direction by construction, not by luck — the subtraction removes precisely the component that would have made the dot product non-zero. Watch the next vector go through the same treatment on the Gram-Schmidt visualizer.

The general case below repeats this step once per vector already fixed, which is the only way the algorithm grows.

The Algorithm: General Case

For kk independent vectors v1,…,vk\mathbf{v}_1, \dots, \mathbf{v}_k:

The Algorithm: General Case
u1=v1\mathbf{u}_1 = \mathbf{v}_1

Gram-Schmidt Process
uj=vj−∑i=1j−1ui⋅vjui⋅ui uifor j=2,3,…,k\mathbf{u}_j = \mathbf{v}_j - \sum_{i=1}^{j-1} \frac{\mathbf{u}_i \cdot \mathbf{v}_j}{\mathbf{u}_i \cdot \mathbf{u}_i}\,\mathbf{u}_i \quad \text{for } j = 2, 3, \dots, k

Pitfall: Projecting onto the original vectors
Projecting v3=(0,1,1)\mathbf{v}_3 = (0, 1, 1) onto v1=(1,1,0)\mathbf{v}_1 = (1, 1, 0) and v2=(1,0,1)\mathbf{v}_2 = (1, 0, 1) instead of onto u1,u2\mathbf{u}_1, \mathbf{u}_2 gives (−1,12,12)(-1, \frac{1}{2}, \frac{1}{2}), whose dot product with u1\mathbf{u}_1 is −12-\frac{1}{2}. Projecting onto the ui\mathbf{u}_i gives u3=(−23,23,23)\mathbf{u}_3 = (-\frac{2}{3}, \frac{2}{3}, \frac{2}{3}).

At each step, vj\mathbf{v}_j has its projections onto all previously computed orthogonal vectors subtracted. What remains is the component of vj\mathbf{v}_j perpendicular to Span{u1,…,uj−1}\text{Span}\{\mathbf{u}_1, \dots, \mathbf{u}_{j-1}\}.

Because vj\mathbf{v}_j is independent of {v1,…,vj−1}\{\mathbf{v}_1, \dots, \mathbf{v}_{j-1}\} — and therefore not in Span{u1,…,uj−1}\text{Span}\{\mathbf{u}_1, \dots, \mathbf{u}_{j-1}\} — this perpendicular component is nonzero. So each uj≠0\mathbf{u}_j \neq \mathbf{0}, and the process never breaks down.

At every stage, Span{u1,…,uj}=Span{v1,…,vj}\text{Span}\{\mathbf{u}_1, \dots, \mathbf{u}_j\} = \text{Span}\{\mathbf{v}_1, \dots, \mathbf{v}_j\}. The span is preserved because each uj\mathbf{u}_j is a linear combination of vj\mathbf{v}_j and the earlier ui\mathbf{u}_i's (which are themselves combinations of v1,…,vj−1\mathbf{v}_1, \dots, \mathbf{v}_{j-1}).

Gram-Schmidt Notation

Notation

Gram-Schmidt Notation

An algorithm's notation has to record not just what the objects are but the order they were built in — so this page runs three letters for three stages of the same vector, and a sum whose upper limit moves with the step it appears in. All catalogued among the linear algebra symbols.
proj⁡\operatorname{proj} and its subscript come from projection notation; the dot from dot product notation; ∥v∥\|\mathbf{v}\| and the unit hat from norm notation; Span⁡\operatorname{Span} from span notation; δij\delta_{ij} and QQ from orthogonal set notation.
vj→uj→ej\mathbf{v}_j \to \mathbf{u}_j \to \mathbf{e}_j
input, orthogonalised, normalised
Three letters for three stages of the same vector: vj\mathbf{v}_j is what you started with, uj\mathbf{u}_j what survives after the projections are removed, and ej\mathbf{e}_j the unit version — Normalization below performs that last step. The subscript stays fixed across all three; only the letter records how far the vector has been processed.
CasesSources that skip the orthogonal stage write qj\mathbf{q}_j directly for the normalised output, matching the QQ of the decomposition below; others reuse vj\mathbf{v}_j throughout and overwrite in place, which is how the algorithm is actually implemented but loses the ability to refer to an earlier stage.
Do not confuseej\mathbf{e}_j as a standard basis vector. Here it is the jj-th vector this process produced, not the jj-th axis — the letter is borrowed for "unit vector", and only the surrounding construction says which unit vector is meant.
uj=vj−∑i=1j−1proj⁡ui(vj)\mathbf{u}_j = \mathbf{v}_j - \sum_{i=1}^{j-1} \operatorname{proj}_{\mathbf{u}_i}(\mathbf{v}_j)
subtract the projections onto everything built so far
The upper limit is the notational content: j−1j-1 means everything already constructed, so the sum's length grows as the algorithm proceeds and the formula refers to its own earlier outputs. This is a recursive definition written as a formula, not a closed expression.
CasesThe first step is the empty case — at j=1j = 1 the sum has no terms, which is why u1=v1\mathbf{u}_1 = \mathbf{v}_1 needs no separate rule; and the projections are onto the ui\mathbf{u}_i, never the vi\mathbf{v}_i, which is the one substitution that silently breaks the algorithm.
Do not confuseA fixed sum over all kk vectors. Reading the limit as kk rather than j−1j-1 asks the formula to use vectors that do not exist yet — the range is the whole reason the construction terminates.
A=QRA = QR
A factors into an orthonormal Q and an upper-triangular R
Letters as a contract, in the family pattern decomposition notation describes: QQ carries the orthonormal columns this process produces, RR collects the coefficients it discarded along the way — The QR Decomposition below reads them off directly.
CasesRR comes out upper triangular for a reason the notation encodes: vj\mathbf{v}_j is built only from u1,…,uj\mathbf{u}_1, \dots, \mathbf{u}_j, so every entry below the diagonal is a coefficient that was never used.
Do not confuseRR as the real numbers. Blackboard R\mathbb{R} is the field; italic RR here is one specific triangular matrix, and the two sit close together in exactly the equations where both appear.

Normalization

After computing the orthogonal set {u1,…,uk}\{\mathbf{u}_1, \dots, \mathbf{u}_k\}, normalization produces an orthonormal set:

Normalization
qi=ui∥ui∥\mathbf{q}_i = \frac{\mathbf{u}_i}{\|\mathbf{u}_i\|}

Each vector is divided by its length, making ∥qi∥=1\|\mathbf{q}_i\| = 1 while preserving direction. The resulting set satisfies qi⋅qj=δij\mathbf{q}_i \cdot \mathbf{q}_j = \delta_{ij}.

Pitfall: Normalizing the inputs first
Scaling v1,v2\mathbf{v}_1, \mathbf{v}_2 to 12(1,1,0)\frac{1}{\sqrt{2}}(1, 1, 0) and 12(1,0,1)\frac{1}{\sqrt{2}}(1, 0, 1) before orthogonalizing gives 12(12,−12,1)\frac{1}{\sqrt{2}}(\frac{1}{2}, -\frac{1}{2}, 1), of length 32\frac{\sqrt{3}}{2}, not 11. Normalizing after the subtraction gives q2=16(1,−1,2)\mathbf{q}_2 = \frac{1}{\sqrt{6}}(1, -1, 2).

Normalization can be done at each step (normalize uj\mathbf{u}_j immediately before moving to vj+1\mathbf{v}_{j+1}) or all at the end. The result is the same in exact arithmetic. In practice, normalizing at each step is slightly preferable for numerical stability, as it keeps the vectors well-scaled throughout the computation.
U3×3v1,1​v1,2​v1,3​v2,1​−c2,1​u1,1​v2,2​−c2,1​u1,2​v2,3​−c2,1​u1,3​v3,1​−c3,1​u1,1​−c3,2​u2,1​v3,2​−c3,1​u1,2​−c3,2​u2,2​v3,3​−c3,1​u1,3​−c3,2​u2,3​→E3×3u1,1​/‖u1​‖u1,2​/‖u1​‖u1,3​/‖u1​‖??????
Scaling the survivor to unit length

Orthogonality was settled by the subtraction; this step only fixes the length. Splitting the two concerns is why the algorithm can be stated for an orthogonal basis and then upgraded to an orthonormal one without changing its structure. Toggle normalisation on and off on the Gram-Schmidt visualizer.

Dividing by a very small length is where the classical algorithm loses accuracy, which is the subject of the stability section later.

Worked Example: Three Vectors in R³

Orthogonalize v1=(1,1,1)\mathbf{v}_1 = (1, 1, 1), v2=(1,0,1)\mathbf{v}_2 = (1, 0, 1), v3=(0,1,1)\mathbf{v}_3 = (0, 1, 1).

u1=(1,1,1)\mathbf{u}_1 = (1, 1, 1).

For u2\mathbf{u}_2: u1⋅v2u1⋅u1=1+0+13=23\frac{\mathbf{u}_1 \cdot \mathbf{v}_2}{\mathbf{u}_1 \cdot \mathbf{u}_1} = \frac{1 + 0 + 1}{3} = \frac{2}{3}. u2=(1,0,1)−23(1,1,1)=(13,−23,13)\mathbf{u}_2 = (1, 0, 1) - \frac{2}{3}(1, 1, 1) = (\frac{1}{3}, -\frac{2}{3}, \frac{1}{3}).

Check: u1⋅u2=13−23+13=0\mathbf{u}_1 \cdot \mathbf{u}_2 = \frac{1}{3} - \frac{2}{3} + \frac{1}{3} = 0.

For u3\mathbf{u}_3: u1⋅v3u1⋅u1=0+1+13=23\frac{\mathbf{u}_1 \cdot \mathbf{v}_3}{\mathbf{u}_1 \cdot \mathbf{u}_1} = \frac{0 + 1 + 1}{3} = \frac{2}{3} and u2⋅v3u2⋅u2=0−23+1319+49+19=−1369=−12\frac{\mathbf{u}_2 \cdot \mathbf{v}_3}{\mathbf{u}_2 \cdot \mathbf{u}_2} = \frac{0 - \frac{2}{3} + \frac{1}{3}}{\frac{1}{9} + \frac{4}{9} + \frac{1}{9}} = \frac{-\frac{1}{3}}{\frac{6}{9}} = \frac{-1}{2}.

u3=(0,1,1)−23(1,1,1)−(−12)(13,−23,13)=(0,1,1)−(23,23,23)+(16,−13,16)=(−12,0,12)\mathbf{u}_3 = (0, 1, 1) - \frac{2}{3}(1, 1, 1) - (-\frac{1}{2})(\frac{1}{3}, -\frac{2}{3}, \frac{1}{3}) = (0, 1, 1) - (\frac{2}{3}, \frac{2}{3}, \frac{2}{3}) + (\frac{1}{6}, -\frac{1}{3}, \frac{1}{6}) = (-\frac{1}{2}, 0, \frac{1}{2}).

Check: u1⋅u3=−12+0+12=0\mathbf{u}_1 \cdot \mathbf{u}_3 = -\frac{1}{2} + 0 + \frac{1}{2} = 0 and u2⋅u3=−16+0+16=0\mathbf{u}_2 \cdot \mathbf{u}_3 = -\frac{1}{6} + 0 + \frac{1}{6} = 0.

Normalizing: ∥u1∥=3\|\mathbf{u}_1\| = \sqrt{3}, ∥u2∥=6/3\|\mathbf{u}_2\| = \sqrt{6}/3, ∥u3∥=1/2\|\mathbf{u}_3\| = 1/\sqrt{2}. The orthonormal basis is {u1/3,  3u2/6,  2 u3}\{\mathbf{u}_1/\sqrt{3}, \; 3\mathbf{u}_2/\sqrt{6}, \; \sqrt{2}\,\mathbf{u}_3\}.

To run Gram-Schmidt on your own set of vectors, use the vector calculator.

Why It Works

At each step, uj\mathbf{u}_j is constructed as vj\mathbf{v}_j minus everything in vj\mathbf{v}_j that lies in the subspace Wj−1=Span{u1,…,uj−1}W_{j-1} = \text{Span}\{\mathbf{u}_1, \dots, \mathbf{u}_{j-1}\}. Since {u1,…,uj−1}\{\mathbf{u}_1, \dots, \mathbf{u}_{j-1}\} is orthogonal, the projection formula decomposes cleanly into independent terms — one projection per basis vector.

What remains after subtraction is the component of vj\mathbf{v}_j orthogonal to Wj−1W_{j-1}. This component is nonzero because vj∉Wj−1\mathbf{v}_j \notin W_{j-1} — guaranteed by the independence of the original set.

The span is preserved at each step. Each uj\mathbf{u}_j is a linear combination of vj\mathbf{v}_j and u1,…,uj−1\mathbf{u}_1, \dots, \mathbf{u}_{j-1}, and each earlier ui\mathbf{u}_i is a combination of v1,…,vi\mathbf{v}_1, \dots, \mathbf{v}_i. So uj∈Span{v1,…,vj}\mathbf{u}_j \in \text{Span}\{\mathbf{v}_1, \dots, \mathbf{v}_j\}, and the reverse inclusion follows because vj\mathbf{v}_j can be recovered from uj\mathbf{u}_j and the earlier ui\mathbf{u}_i's.

Independence guarantees that something survives each subtraction, and the subtraction guarantees that what survives is perpendicular.

The QR Decomposition

Applying Gram-Schmidt to the columns a1,…,an\mathbf{a}_1, \dots, \mathbf{a}_n of an m×nm \times n matrix AA (with independent columns) produces the QR decomposition:

QR Decomposition
A=QRA = QR
Learn more about this formula: QR Decomposition →


QQ is m×nm \times n with orthonormal columns (the normalized qi\mathbf{q}_i's). RR is n×nn \times n upper triangular with positive diagonal entries.

The entries of RR are the dot products computed during Gram-Schmidt: Rij=qi⋅ajR_{ij} = \mathbf{q}_i \cdot \mathbf{a}_j for i≤ji \leq j, and Rij=0R_{ij} = 0 for i>ji > j (because aj\mathbf{a}_j lies in the span of q1,…,qj\mathbf{q}_1, \ldots, \mathbf{q}_j, so it is perpendicular to qi\mathbf{q}_i when i>ji > j).

The factorization captures two complementary pieces of information. QQ stores the orthonormal directions. RR stores the coefficients that express the original columns in terms of those directions: aj=R1jq1+R2jq2+⋯+Rjjqj\mathbf{a}_j = R_{1j}\mathbf{q}_1 + R_{2j}\mathbf{q}_2 + \cdots + R_{jj}\mathbf{q}_j.
Orthogonality · QR

Where each factor comes from

Running Gram–Schmidt on the columns of AA produces both factors at once. QQ is the output and RR is the bookkeeping — the coefficients that were subtracted along the way, kept rather than discarded.

2factors
TypeConditionentriespropertieswhat it records
The output
Q — orthonormal columns§ 7m×nm \times nqj=uj/∥uj∥\mathbf{q}_j = \mathbf{u}_j / \|\mathbf{u}_j\|QTQ=InQ^{\mathsf{T}}Q = I_nan orthonormal basis for Col⁡(A)\operatorname{Col}(A)
The bookkeeping
R — upper triangular§ 7n×nn \times nrij=qi⋅vjr_{ij} = \mathbf{q}_i \cdot \mathbf{v}_jdiagonal rjj=∥uj∥>0r_{jj} = \|\mathbf{u}_j\|>0the coefficients discarded during orthogonalization
This is why QR is not a separate algorithm from Gram–Schmidt so much as its record. Every projection coefficient computed during orthogonalization is an entry of RR, and RR is triangular precisely because vj\mathbf{v}_j is only ever projected onto vectors that came before it.
Where each factor comes from·/linear-algebra/orthogonality/gram-schmidtLearn Math Class
The triangularity of RR is worth seeing as a consequence rather than a condition. At step jj the algorithm projects vj\mathbf{v}_j onto q1,…,qj−1\mathbf{q}_1, \ldots, \mathbf{q}_{j-1} and no others, so every coefficient with i>ji > j is zero because that projection was never computed. Nothing was imposed on the shape — it fell out of processing the columns in order.
A3×3a1,1​a1,2​a1,3​a2,1​a2,2​a2,3​a3,1​a3,2​a3,3​=Q3×3u1,1​/r1,1​u1,2​/r2,2​u1,3​/r3,3​u2,1​/r1,1​u2,2​/r2,2​u2,3​/r3,3​u3,1​/r1,1​u3,2​/r2,2​u3,3​/r3,3​·R3×3‖u1​‖q1​·a2​q1​·a3​0‖u2​‖q2​·a3​00‖u3​‖
The same run, recorded as two factors

The orthonormal vectors produced have become the columns of Q, and the coefficients subtracted along the way have become the entries of R. The triangularity of R is not imposed: it reflects the fact that each vector was only ever corrected against the ones before it. Compare the two views on the QR decomposition visualizer.

Gram-Schmidt and QR are therefore one computation with two names, depending on whether the vectors or the factors are what you are after.

QR and Least Squares

The QR decomposition provides a numerically superior method for solving least-squares problems.

The normal equations ATAx^=ATbA^TA\hat{\mathbf{x}} = A^T\mathbf{b} can be rewritten using A=QRA = QR. Since ATA=RTQTQR=RTRA^TA = R^TQ^TQR = R^TR and ATb=RTQTbA^T\mathbf{b} = R^TQ^T\mathbf{b}, the normal equations become RTRx^=RTQTbR^TR\hat{\mathbf{x}} = R^TQ^T\mathbf{b}. Canceling RTR^T (which is invertible since RR has positive diagonal):

QR and Least Squares
Rx^=QTbR\hat{\mathbf{x}} = Q^T\mathbf{b}

This is an upper triangular system, solved by back substitution. The computation QTbQ^T\mathbf{b} is just nn dot products (one per column of QQ).

The QR approach avoids forming ATAA^TA explicitly. This matters because the condition number of ATAA^TA is the square of the condition number of AA — squaring amplifies rounding errors. Working with QQ and RR directly preserves the original conditioning and is the standard method for least squares in numerical software.

Numerical Stability

Fact: Perpendicular on paper, 60∘60^\circ on a computer
Run classical Gram-Schmidt in double precision on (1,ε,0,0)(1, \varepsilon, 0, 0), (1,0,ε,0)(1, 0, \varepsilon, 0), (1,0,0,ε)(1, 0, 0, \varepsilon) with ε=10−8\varepsilon = 10^{-8}: since 1+ε21 + \varepsilon^2 rounds to 11, the last two outputs come out with q2⋅q3=12\mathbf{q}_2 \cdot \mathbf{q}_3 = \frac{1}{2}, an angle of 60∘60^\circ. The modified version, on the same input, gives q2⋅q3≈10−16\mathbf{q}_2 \cdot \mathbf{q}_3 \approx 10^{-16}.

Classical Gram-Schmidt (as presented above) can lose orthogonality in floating-point arithmetic. When the input vectors are nearly dependent, the computed uj\mathbf{u}_j's may fail to be perpendicular to machine precision, and the errors accumulate with each step.

Modified Gram-Schmidt addresses this by reorganizing the computation. Instead of computing all projections using the original vj\mathbf{v}_j, modified Gram-Schmidt updates vj\mathbf{v}_j in place after each projection is subtracted. At step jj, first subtract the projection onto u1\mathbf{u}_1 from vj\mathbf{v}_j, then subtract the projection onto u2\mathbf{u}_2 from the updated vj\mathbf{v}_j, and so on. The mathematical result is identical in exact arithmetic, but the modified version is significantly more stable numerically.

Householder reflections provide an even more robust alternative for computing the QR factorization. Householder-based QR achieves backward stability — the gold standard in numerical linear algebra — and is the default algorithm in most software libraries.
Method How it computes the orthogonal set / QR Numerical stability Typical use
Classical Gram–Schmidt at step j, all projections of the original vj onto u1, …, uj−1 are computed and subtracted at once poor — orthogonality can be lost when input vectors are nearly dependent; errors accumulate textbook / illustrative use; abstract inner product spaces where exact arithmetic is available
Modified Gram–Schmidt vj is updated in place after each individual projection is subtracted significantly better than classical; identical result in exact arithmetic educational software, moderate-precision floating-point work
Householder reflections a sequence of orthogonal reflections is applied to A to zero out below-diagonal entries column by column backward stable — the gold standard in numerical linear algebra default QR routine in LAPACK, NumPy, MATLAB, and most numerical libraries
Givens rotations a sequence of 2 × 2 plane rotations zeros out individual entries one at a time backward stable; lets the algorithm target nonzero entries only sparse matrices with structured zeros; updating an existing QR factorization
Formulas that agree in exact arithmetic can behave very differently on a computer: the order of the subtractions matters.

Gram-Schmidt on Abstract Inner Product Spaces

The algorithm works in any inner product space — the dot product is replaced by the general inner product ⟨⋅,⋅⟩\langle \cdot, \cdot \rangle, and the formulas are otherwise identical:

Gram-Schmidt on Abstract Inner Product Spaces
uj=vj−∑i=1j−1⟨ui,vj⟩⟨ui,ui⟩ ui\mathbf{u}_j = \mathbf{v}_j - \sum_{i=1}^{j-1} \frac{\langle \mathbf{u}_i, \mathbf{v}_j \rangle}{\langle \mathbf{u}_i, \mathbf{u}_i \rangle}\,\mathbf{u}_i

Pitfall: Using the dot product of coefficients
Treating 11 and x2x^2 as coefficient lists (1,0,0)(1, 0, 0) and (0,0,1)(0, 0, 1) gives dot product 00, so x2x^2 is kept unchanged, although ∫−111⋅x2 dx=23≠0\int_{-1}^{1} 1 \cdot x^2\,dx = \frac{2}{3} \neq 0. With the integral inner product the third vector is x2−13x^2 - \frac{1}{3}.

Orthogonalizing {1,x,x2}\{1, x, x^2\} in the polynomial space P2\mathcal{P}_2 with the inner product ⟨p,q⟩=∫−11p(x)q(x) dx\langle p, q \rangle = \int_{-1}^{1} p(x)q(x)\,dx produces the Legendre polynomials (up to normalization): P0(x)=1P_0(x) = 1, P1(x)=xP_1(x) = x, P2(x)=12(3x2−1)P_2(x) = \frac{1}{2}(3x^2 - 1). The polynomial xx is already orthogonal to 11 under this inner product (the integral of an odd function over a symmetric interval is zero), so the first subtraction has no effect.

On the function space C[0,2π]C[0, 2\pi] with ⟨f,g⟩=∫02πf(x)g(x) dx\langle f, g \rangle = \int_0^{2\pi} f(x)g(x)\,dx, orthogonalizing appropriate function sets produces Fourier bases. The algorithm is identical in structure to the Rn\mathbb{R}^n version — only the inner product changes.

Summary: Gram-Schmidt Across Settings

Gram-Schmidt operates uniformly across many settings — ℝⁿ with the standard dot product, polynomial spaces with integral inner products, function spaces in Fourier analysis, and abstract inner product spaces in general. The table below collects the main contexts in which the process is applied, alongside the input, the inner product used, and what the algorithm produces in each case.
What follows is one algorithm listed six times. The steps are identical in every row — subtract the projections onto what has already been processed, then optionally divide by the length. The only thing that varies is which inner product those projections are computed with, and the grouping below separates the familiar dot product from the integrals that replace it elsewhere.
Orthogonality · Gram–Schmidt

The same algorithm across settings

The procedure never changes — subtract projections, optionally normalise. What changes from row to row is only the inner product being used, and with it what the word "orthogonal" turns out to mean.

6settings
The dot product on ℝⁿ3
1
input independent
uj=vj−∑i<jproj⁡ui(vj)\mathbf{u}_j = \mathbf{v}_j - \sum_{i<j} \operatorname{proj}_{\mathbf{u}_i}(\mathbf{v}_j)
Each vector loses everything it shares with the ones already processed, leaving the perpendicular remainder. The span is preserved at every prefix, so the output describes the same subspace as the input — differently oriented, not differently sized.
2
one extra division per vector
qj=uj/∥uj∥\mathbf{q}_j = \mathbf{u}_j / \|\mathbf{u}_j\|
Normalisation is a separate step and strictly optional — orthogonality is already achieved. It is worth doing because projection onto an orthonormal set is a bare dot product, with no denominator to carry.
3
AA of full column rank
A=QRA = QR, RR upper triangular
Run the algorithm on the columns of AA and the outputs are the columns of QQ; the projection coefficients discarded along the way are exactly the entries of RR. The factorization is the algorithm with its bookkeeping written down — see QR.
Other inner products3
4
applied to {1,x,x2,…}\{1, x, x^2, \ldots\}
⟨p,q⟩=∫−11p(x)q(x) dx\langle p, q \rangle = \int_{-1}^{1} p(x)q(x)\,dx
The monomials are independent but not orthogonal under this product. Orthogonalizing them produces the Legendre polynomials — which is to say Legendre polynomials are not a discovery so much as what Gram–Schmidt returns when handed the obvious basis.
5
sines and cosines
⟨f,g⟩=∫02πf(x)g(x) dx\langle f, g \rangle = \int_0^{2\pi} f(x)g(x)\,dx
Here the algorithm has almost nothing to do: distinct frequencies are already orthogonal under this product, so it only normalises. That is the fact the Fourier basis rests on, and the reason a Fourier coefficient is a single integral rather than a linear system.
6
finite-dimensional, any ⟨⋅,⋅⟩\langle \cdot, \cdot \rangle
orthonormal {q1,…,qn}\{\mathbf{q}_1, \ldots, \mathbf{q}_n\}
The general statement the rows above are instances of. Because the algorithm terminates and uses only the axioms, it proves the existence of an orthonormal basis rather than assuming it — a constructive proof, not an abstract one.
The algorithm only ever calls two things: the inner product and scalar multiplication. It never inspects what a vector is, which is why the same code orthogonalizes tuples, polynomials and functions without modification — and why it constitutes a constructive proof that every finite-dimensional inner product space has an orthonormal basis.
The same algorithm across settings·/linear-algebra/orthogonality/gram-schmidtLearn Math Class
Fact: x8x^8 and x10x^{10} sit 6∘6^\circ apart
Under ⟨p,q⟩=∫−11p(x)q(x) dx\langle p, q \rangle = \int_{-1}^{1} p(x)q(x)\,dx, the cosine of the angle between x8x^8 and x10x^{10} is 35719≈0.994\frac{\sqrt{357}}{19} \approx 0.994, about 6∘6^\circ. High powers look almost alike on [−1,1][-1, 1], so the monomials are close to dependent and Gram-Schmidt removes most of each higher power.
The second group is where the generality earns its keep. Under the integral inner product on [−1,1][-1, 1] the monomials 1,x,x21, x, x^2 are independent but not orthogonal, and orthogonalizing them yields the Legendre polynomials — so those are not a separate construction but simply what this algorithm returns when handed the obvious basis. Under the inner product on [0,2π][0, 2\pi], sines and cosines of distinct frequencies are already orthogonal, and the algorithm finds nothing to subtract.
That last observation is why a Fourier coefficient is a single integral rather than the solution of a linear system: the basis was orthogonal before anyone asked. The algorithm never inspects what a vector is — it calls the inner product and scalar multiplication and nothing else — which is what lets the same procedure serve tuples, polynomials and functions, and what makes it a constructive proof that every finite-dimensional inner product space has an orthonormal basis.

Common Pitfalls

Four slips account for many wrong Gram-Schmidt computations. Each is marked with a box where it happens in the sections above. Here each is worked through in full, with the wrong and the right working side by side.

Dropping the denominator

The coefficient of u1\mathbf{u}_1 is taken as the dot product u1⋅v2\mathbf{u}_1 \cdot \mathbf{v}_2 alone, as if u1\mathbf{u}_1 had length 11. The subtracted vector then has the wrong length, and what remains is not perpendicular to u1\mathbf{u}_1.

Wrong working: with v1=(1,1,0)\mathbf{v}_1 = (1, 1, 0) and v2=(1,0,1)\mathbf{v}_2 = (1, 0, 1),
u2=v2−(u1⋅v2) u1=(1,0,1)−1⋅(1,1,0)=(0,−1,1)\mathbf{u}_2 = \mathbf{v}_2 - (\mathbf{u}_1 \cdot \mathbf{v}_2)\,\mathbf{u}_1 = (1, 0, 1) - 1 \cdot (1, 1, 0) = (0, -1, 1)

and u1⋅u2=0−1+0=−1\mathbf{u}_1 \cdot \mathbf{u}_2 = 0 - 1 + 0 = -1.

Right working: u1⋅u1=2\mathbf{u}_1 \cdot \mathbf{u}_1 = 2, so
u2=(1,0,1)−12(1,1,0)=(12,−12,1)\mathbf{u}_2 = (1, 0, 1) - \frac{1}{2}(1, 1, 0) = \left(\frac{1}{2}, -\frac{1}{2}, 1\right)

and u1⋅u2=12−12+0=0\mathbf{u}_1 \cdot \mathbf{u}_2 = \frac{1}{2} - \frac{1}{2} + 0 = 0.

Why it happens: the orthonormal version of the formula, (qi⋅vj) qi(\mathbf{q}_i \cdot \mathbf{v}_j)\,\mathbf{q}_i, has no denominator, and it is used for vectors that have not been normalized.

How to check: take the dot product of the new vector with every earlier one. Each product must be 00.

Projecting onto the original vectors

At step jj, the projections are taken onto the inputs v1,…,vj−1\mathbf{v}_1, \dots, \mathbf{v}_{j-1} instead of onto the orthogonal vectors u1,…,uj−1\mathbf{u}_1, \dots, \mathbf{u}_{j-1} already built. At j=2j = 2 nothing changes, because u1=v1\mathbf{u}_1 = \mathbf{v}_1, so the error first shows at j=3j = 3.

Wrong working: with v1=(1,1,0)\mathbf{v}_1 = (1, 1, 0), v2=(1,0,1)\mathbf{v}_2 = (1, 0, 1) and v3=(0,1,1)\mathbf{v}_3 = (0, 1, 1),
v3−v1⋅v3v1⋅v1 v1−v2⋅v3v2⋅v2 v2=(0,1,1)−12(1,1,0)−12(1,0,1)=(−1,12,12)\mathbf{v}_3 - \frac{\mathbf{v}_1 \cdot \mathbf{v}_3}{\mathbf{v}_1 \cdot \mathbf{v}_1}\,\mathbf{v}_1 - \frac{\mathbf{v}_2 \cdot \mathbf{v}_3}{\mathbf{v}_2 \cdot \mathbf{v}_2}\,\mathbf{v}_2 = (0, 1, 1) - \frac{1}{2}(1, 1, 0) - \frac{1}{2}(1, 0, 1) = \left(-1, \frac{1}{2}, \frac{1}{2}\right)

Its dot product with u1=(1,1,0)\mathbf{u}_1 = (1, 1, 0) is −1+12=−12-1 + \frac{1}{2} = -\frac{1}{2}, so it is not perpendicular to u1\mathbf{u}_1.

Right working: with u2=(12,−12,1)\mathbf{u}_2 = (\frac{1}{2}, -\frac{1}{2}, 1), the dot products are u2⋅v3=12\mathbf{u}_2 \cdot \mathbf{v}_3 = \frac{1}{2} and u2⋅u2=32\mathbf{u}_2 \cdot \mathbf{u}_2 = \frac{3}{2}, so
u3=(0,1,1)−12(1,1,0)−13(12,−12,1)=(−23,23,23)\mathbf{u}_3 = (0, 1, 1) - \frac{1}{2}(1, 1, 0) - \frac{1}{3}\left(\frac{1}{2}, -\frac{1}{2}, 1\right) = \left(-\frac{2}{3}, \frac{2}{3}, \frac{2}{3}\right)

Then u1⋅u3=−23+23+0=0\mathbf{u}_1 \cdot \mathbf{u}_3 = -\frac{2}{3} + \frac{2}{3} + 0 = 0 and u2⋅u3=−13−13+23=0\mathbf{u}_2 \cdot \mathbf{u}_3 = -\frac{1}{3} - \frac{1}{3} + \frac{2}{3} = 0.

Why it happens: the inputs are the vectors the problem states, so they are the ones at hand. But a sum of projections onto single vectors gives the projection onto their span only when those vectors are orthogonal, and v1⋅v2=1≠0\mathbf{v}_1 \cdot \mathbf{v}_2 = 1 \neq 0.

How to check: test the new vector against all earlier ui\mathbf{u}_i, not only the last one. The wrong vector above fails against u1\mathbf{u}_1.

Normalizing the inputs first

The inputs are scaled to unit length before the process runs, in the hope that the output will then be orthonormal. The subtractions change the lengths again: each uj\mathbf{u}_j with j≥2j \geq 2 is shorter than the vector it came from, unless that vector was already perpendicular to the earlier ones.

Wrong working: scale to q1=12(1,1,0)\mathbf{q}_1 = \frac{1}{\sqrt{2}}(1, 1, 0) and 12(1,0,1)\frac{1}{\sqrt{2}}(1, 0, 1). The dot product of the two is 12\frac{1}{2}, so the second output is
12(1,0,1)−12⋅12(1,1,0)=12(12,−12,1)\frac{1}{\sqrt{2}}(1, 0, 1) - \frac{1}{2} \cdot \frac{1}{\sqrt{2}}(1, 1, 0) = \frac{1}{\sqrt{2}}\left(\frac{1}{2}, -\frac{1}{2}, 1\right)

of length 12⋅32=32\frac{1}{\sqrt{2}} \cdot \sqrt{\frac{3}{2}} = \frac{\sqrt{3}}{2}, not 11.

Right working: orthogonalize first, u2=(12,−12,1)\mathbf{u}_2 = (\frac{1}{2}, -\frac{1}{2}, 1) with ∥u2∥=32\|\mathbf{u}_2\| = \sqrt{\frac{3}{2}}, then divide:
q2=u2∥u2∥=16(1,−1,2)\mathbf{q}_2 = \frac{\mathbf{u}_2}{\|\mathbf{u}_2\|} = \frac{1}{\sqrt{6}}(1, -1, 2)

which has length 1+1+46=1\frac{\sqrt{1 + 1 + 4}}{\sqrt{6}} = 1.

Why it happens: normalization looks like a preparation step that can be done at any point, but subtracting a projection shortens a vector.

How to check: compute the length of every output vector. In an orthonormal set each length is exactly 11, and here the second one is about 0.870.87.

Using the dot product of coefficients

In a polynomial space with an integral inner product, the polynomials are written as lists of coefficients and the ordinary dot product is used. Under that dot product 1,x,x21, x, x^2 are already orthonormal, so the process changes nothing.

Wrong working: 1→(1,0,0)1 \to (1, 0, 0), x→(0,1,0)x \to (0, 1, 0) and x2→(0,0,1)x^2 \to (0, 0, 1). All dot products are 00, so the output is 1,x,x21, x, x^2 unchanged. But
⟨1,x2⟩=∫−11x2 dx=23≠0\langle 1, x^2 \rangle = \int_{-1}^{1} x^2\,dx = \frac{2}{3} \neq 0

so 11 and x2x^2 are not orthogonal under the inner product of the problem.

Right working: ⟨1,1⟩=2\langle 1, 1 \rangle = 2, ⟨1,x2⟩=23\langle 1, x^2 \rangle = \frac{2}{3} and ⟨x,x2⟩=∫−11x3 dx=0\langle x, x^2 \rangle = \int_{-1}^{1} x^3\,dx = 0, so the third vector is
x2−2/32⋅1−0⋅x=x2−13x^2 - \frac{2/3}{2} \cdot 1 - 0 \cdot x = x^2 - \frac{1}{3}

which is 23P2(x)\frac{2}{3}P_2(x).

Why it happens: in Rn\mathbb{R}^n the dot product is the only inner product in use, and a coefficient list looks exactly like a vector in R3\mathbb{R}^3.

How to check: compute one inner product of the result with an earlier vector, using the integral. Here ∫−11(x2−13)dx=23−23=0\int_{-1}^{1} \left(x^2 - \frac{1}{3}\right) dx = \frac{2}{3} - \frac{2}{3} = 0.

Gram-Schmidt FAQ

Why does the sum stop at j−1j-1 rather than running over all the vectors?

+
Because the later vectors have not been produced yet. At step jj only u1\mathbf{u}_1 through uj−1\mathbf{u}_{j-1} exist, so a fixed upper limit would ask the formula to use things that do not exist. The moving limit is also why the construction terminates, and at j=1j = 1 the empty sum makes u1=v1\mathbf{u}_1 = \mathbf{v}_1 automatic.Read more →

Are the ej\mathbf{e}_j here the standard basis vectors?

+
No, they are the unit vectors this process produced, not the coordinate axes. The letter is borrowed for its association with unit length, and only the surrounding construction says which unit vector is meant. Watch the projections too: they must be onto the ui\mathbf{u}_i, never the original vi\mathbf{v}_i, which is the substitution that silently breaks the algorithm.Read more →

Why does RR come out upper triangular?

+
Because of what the algorithm builds. Each vj\mathbf{v}_j is assembled from only the first jj orthogonal vectors, so any coefficient below the diagonal would refer to a vector that never contributed. Those entries are zero not by construction but by absence. Note that italic RR here is one triangular matrix, unrelated to blackboard R\mathbb{R}.Read more →