A system of linear equations can always be solved by row reduction. Convert the augmented matrix to echelon form, then back-substitute. Gauss-Jordan elimination goes further, reducing to reduced row echelon form where every solution reads off directly.
Systems as augmented matrices
A system of linear equations is a list of constraints, each linear in the unknowns. Writing it as an augmented matrix strips away the variable names and keeps only the coefficients. Row operations on the matrix correspond to legitimate algebraic moves on the system.
Scheme
; Represent an augmented matrix as a list of rows.; Each row is a list of numbers (coefficients | constant).
(define mat '((21-18)
(-3-12-11)
(-212-3)))
; Scale a row by a factor
(define (scale-row row k)
(map (lambda (x) (* x k)) row))
; Add two rows element-wise
(define (add-rows r1 r2)
(map + r1 r2))
; Row replacement: Ri <- Ri + k * Rj
(define (row-replace mat i j k)
(let* ((rows (list->vector mat))
(ri (vector-ref rows i))
(rj (vector-ref rows j))
(new-ri (add-rows ri (scale-row rj k))))
(vector-set! rows i new-ri)
(vector->list rows)))
; Eliminate below pivot in column 0
(define step1 (row-replace mat 103/2)) ; R2 <- R2 + (3/2)R1
(define step2 (row-replace step1 201)) ; R3 <- R3 + R1
(display "After eliminating column 0:")
(newline)
(for-each (lambda (row)
(display " ")
(display row)
(newline)) step2)
Python
# Augmented matrix as a list of lists
mat = [[2, 1, -1, 8],
[-3, -1, 2, -11],
[-2, 1, 2, -3]]
import copy
m = copy.deepcopy(mat)
# R2 <- R2 + (3/2)*R1for j inrange(4):
m[1][j] += (3/2) * m[0][j]
# R3 <- R3 + R1for j inrange(4):
m[2][j] += m[0][j]
print("After eliminating column 0:")
for row in m:
print(" ", [round(x, 2) for x in row])
Echelon form and back-substitution
A matrix is in echelon form when each leading entry is to the right of the one above it, and all entries below each leading entry are zero. Once you reach echelon form, the system solves by back-substitution: start from the bottom row and work up.
# Row reduce to echelon form
m = [[2, 1, -1, 8],
[0, 0.5, 0.5, 1],
[0, 2, 1, 5]]
# R3 <- R3 + (-4)*R2for j inrange(4):
m[2][j] += -4 * m[1][j]
print("Echelon form:")
for row in m:
print(" ", [round(x, 2) for x in row])
# Back-substitute: x3=-1, x2=3, x1=2print("Solution: x1=2, x2=3, x3=-1")
Gauss-Jordan elimination
Gauss-Jordan goes beyond echelon form: eliminate upward too, and scale each pivot to 1. The result is reduced row echelon form (RREF). Every column either contains a pivot or represents a free variable. The solution reads off without any arithmetic.
Scheme
; Full Gauss-Jordan elimination on a 3x3 system.; We'll go from raw system to RREF.
(define (scale-row row k) (map (lambda (x) (* x k)) row))
(define (add-rows r1 r2) (map + r1 r2))
(define (row-replace mat i j k)
(let* ((v (list->vector mat))
(new-ri (add-rows (vector-ref v i) (scale-row (vector-ref v j) k))))
(vector-set! v i new-ri)
(vector->list v)))
(define (scale-pivot mat i k)
(let ((v (list->vector mat)))
(vector-set! v i (scale-row (vector-ref v i) k))
(vector->list v)))
; Start: x + y = 5, 2x - y = 1
(define m '((115)
(2-11)))
; Forward: R2 <- R2 - 2*R1
(define m1 (row-replace m 10-2))
; Scale R2: R2 <- (-1/3)*R2
(define m2 (scale-pivot m1 1-1/3))
; Backward: R1 <- R1 - R2
(define m3 (row-replace m2 01-1))
(display "RREF:")
(newline)
(for-each (lambda (row)
(display " ")
(display row)
(newline)) m3)
; Should be ((1 0 2) (0 1 3)) => x=2, y=3
When a system has more unknowns than pivot positions, the non-pivot columns correspond to free variables. The solution set is a line, plane, or higher-dimensional flat through the particular solution. This is where linear algebra meets geometry.
Scheme
; A system with a free variable:; x + 2y + z = 4; 2x + 4y + 3z = 9; Two equations, three unknowns.
(define (scale-row row k) (map (lambda (x) (* x k)) row))
(define (add-rows r1 r2) (map + r1 r2))
(define m '((1214)
(2439)))
; R2 <- R2 - 2*R1
(define m1
(let ((v (list->vector m)))
(vector-set! v 1 (add-rows (vector-ref v 1) (scale-row (vector-ref v 0) -2)))
(vector->list v)))
(display "Echelon form:")
(newline)
(for-each (lambda (row)
(display " ") (display row) (newline)) m1)
; Result: ((1 2 1 4) (0 0 1 1)); z = 1 (from row 2); x + 2y + 1 = 4, so x = 3 - 2y; y is free.
(newline)
(display "z = 1, x = 3 - 2t, y = t (t is free)")
(newline)
(display "Solution is a line through (3,0,1).")
Python
# System with a free variable
m = [[1, 2, 1, 4],
[2, 4, 3, 9]]
# R2 <- R2 - 2*R1for j inrange(4):
m[1][j] -= 2 * m[0][j]
print("Echelon form:")
for row in m:
print(" ", [round(x, 2) for x in row])
# z=1, x=3-2t, y=t (free)print("z = 1, x = 3 - 2t, y = t (t is free)")
print("Solution is a line through (3, 0, 1).")
Hefferon's chapter covers additional topics: describing solution sets geometrically, general = particular + homogeneous decomposition, and a careful treatment of the uniqueness of RREF. The examples here focus on the core algorithm. For full proofs, see the original text.