AnyLearn
All lessons
Mathintermediate

Modular Arithmetic: Doing Maths on a Clock

Wrap the number line into a circle and addition and multiplication survive intact while division mostly does not. This lesson builds congruences, shows why you can reduce early to avoid overflow, works through Euclid's algorithm and modular inverses, and explains how a million-digit exponent becomes twenty multiplications.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
Progress1 / 10

The arithmetic you already do

If it is 10 o'clock and you wait 5 hours, it is 3 o'clock. You did not compute 15 and then apologise. You worked in a system where 15 and 3 are the same number, because only the remainder after dividing by 12 carries meaning.

Definition: Two integers are congruent modulo nn when they differ by a multiple of nn, written ab(modn)    n(ab)a \equiv b \pmod{n} \iff n \mid (a - b)

So 153(mod12)15 \equiv 3 \pmod{12}, and so do 27, 39 and 9-9. The notation is deliberately not an equals sign: these are genuinely different integers that this system declines to distinguish.

Everything that wraps around works this way: array indices in a ring buffer, hash buckets, days of the week, angles, fixed-width integer overflow, and the phase of a periodic signal.

Full lesson text

All 10 steps on one page, for reading, reference, and search.

Show

1. The arithmetic you already do

If it is 10 o'clock and you wait 5 hours, it is 3 o'clock. You did not compute 15 and then apologise. You worked in a system where 15 and 3 are the same number, because only the remainder after dividing by 12 carries meaning.

Definition: Two integers are congruent modulo nn when they differ by a multiple of nn, written ab(modn)    n(ab)a \equiv b \pmod{n} \iff n \mid (a - b)

So 153(mod12)15 \equiv 3 \pmod{12}, and so do 27, 39 and 9-9. The notation is deliberately not an equals sign: these are genuinely different integers that this system declines to distinguish.

Everything that wraps around works this way: array indices in a ring buffer, hash buckets, days of the week, angles, fixed-width integer overflow, and the phase of a periodic signal.

2. Congruence carves the integers into classes

Congruence mod nn is an equivalence relation: reflexive, symmetric, and transitive. Every equivalence relation partitions its set, and here the partition is into nn residue classes, one per possible remainder.

Working mod 5, every integer belongs to exactly one of five classes:

{,5,0,5,10,}, {,4,1,6,11,}, , {,1,4,9,14,}\{\dots, -5, 0, 5, 10, \dots\},\ \{\dots, -4, 1, 6, 11, \dots\},\ \dots,\ \{\dots, -1, 4, 9, 14, \dots\}

The set of those classes is written Zn\mathbb{Z}_n, and it is a finite number system with nn elements in which you can add, subtract and multiply exactly as usual.

Key idea: This is why modular arithmetic is useful in computing. It replaces an infinite set with a finite one while keeping the arithmetic that matters, which is precisely what a fixed-width register does to the integers.

3. Reduce early, and often

The operations respect congruence, which is the fact everything practical rests on:

(a+b)modn=((amodn)+(bmodn))modn(a + b) \bmod n = ((a \bmod n) + (b \bmod n)) \bmod n (a×b)modn=((amodn)×(bmodn))modn(a \times b) \bmod n = ((a \bmod n) \times (b \bmod n)) \bmod n

You may reduce at any point without changing the answer. That is not a convenience, it is what makes the arithmetic possible at all on bounded hardware.

# Overflows a 64-bit integer long before finishing, in C or Rust
result = 1
for x in values:
    result = result * x        # grows without bound
result %= n

# Stays below n^2 throughout, so it fits
result = 1
for x in values:
    result = (result * x) % n  # reduce every step

In practice: Python's arbitrary-precision integers hide this problem and then hand you a different one, since multiplying million-digit numbers is slow. Reducing every step is correct and fast in every language.

4. Where this actually turns up

ApplicationThe modulusWhat congruence buys
Hash table buckettable sizemaps an unbounded key space onto a fixed array
Ring buffer indexbuffer lengthwraparound with no branch
ISBN-10 check digit11detects any single-digit error and any transposition
IBAN validation97one remainder check catches most typos
Day-of-week arithmetic7date offsets without a calendar
Fixed-width integer overflow2322^{32} or 2642^{64}unsigned overflow is defined as mod 2k2^k
Cyclic redundancy checksa polynomialthe same idea over polynomials rather than integers

The ISBN case shows why the modulus is chosen rather than convenient. Eleven is prime, and that is exactly what makes every weighted-position error detectable: with a composite modulus, certain digit substitutions cancel out and slip through. Check-digit schemes with a prime modulus are strictly stronger, and that is the whole reason ISBN-10 needed an X for the value 10.

5. The negative-number trap

Mathematics defines the residue of 7-7 mod 3 as 2, because 7=(3)×3+2-7 = (-3) \times 3 + 2 and residues are taken in {0,1,2}\{0, 1, 2\}. Programming languages disagree with each other about this.

-7 % 3        # Python:  2   (sign follows the divisor)
-7 % 3        /* C, Java, Go, Rust:  -1  (sign follows the dividend) */

Gotcha: In C-family languages, i = (i - 1) % n on a ring buffer produces 1-1 when i is 0, and indexing with it is undefined behaviour or an exception rather than the wraparound you wanted. The portable fix is ((i - 1) % n + n) % n, which costs one extra operation and works in every language including Python.

This is not a language defect so much as two defensible conventions, but it is a reliable source of off-by-one bugs at exactly the boundary where they are least likely to be tested.

6. Division is where it gets interesting

Addition, subtraction and multiplication transfer to Zn\mathbb{Z}_n without incident. Division does not. Dividing by aa means multiplying by an inverse a1a^{-1} with aa11(modn)a \cdot a^{-1} \equiv 1 \pmod n, and such an element need not exist.

Mod 12, the number 3 has no inverse: 3×k3 \times k cycles through 3,6,9,03, 6, 9, 0 forever and never hits 1. Mod 26, however, 31=93^{-1} = 9, since 27127 \equiv 1.

a1 exists mod n    gcd(a,n)=1a^{-1} \text{ exists mod } n \iff \gcd(a, n) = 1

The condition is that aa and nn share no factor. Two immediate consequences: when nn is prime every non-zero element is invertible, so Zp\mathbb{Z}_p is a field and you can do ordinary algebra in it; when nn is composite it is not, and that asymmetry is why prime moduli appear wherever the arithmetic has to be well behaved.

7. Euclid's algorithm, still unbeaten after 2300 years

Finding gcd(a,b)\gcd(a, b) by factoring both numbers is hopeless for large inputs. Euclid's method never factors anything. It repeatedly replaces the pair with the smaller number and the remainder, using the fact that gcd(a,b)=gcd(b,amodb)\gcd(a, b) = \gcd(b, a \bmod b):

1071=2×462+1471071 = 2 \times 462 + 147 462=3×147+21462 = 3 \times 147 + 21 147=7×21+0147 = 7 \times 21 + 0

The last non-zero remainder, 21, is the gcd. Three steps on six-digit inputs.

It is fast because the remainder at least halves every two steps, giving a step count logarithmic in the input. The worst case is exactly consecutive Fibonacci numbers, a result of Lame from 1844:

Euclid division steps on the worst-case inputs
division steps010203091419242989, 55987, 61010946, 6765121393, 750251346269, 832040
Source: Computed: step count of the Euclidean algorithm on consecutive Fibonacci pairs, its proven worst case

Inputs grow by a factor of 15,000 across that chart and the work barely triples.

8. Getting the inverse out of it

The extended Euclidean algorithm tracks the coefficients as it goes, producing xx and yy with

ax+by=gcd(a,b)ax + by = \gcd(a, b)

When gcd(a,n)=1\gcd(a, n) = 1 this reads ax+ny=1ax + ny = 1, so ax1(modn)ax \equiv 1 \pmod n, and xx is the inverse you wanted. One algorithm answers both questions at once: whether an inverse exists, and what it is.

def egcd(a, b):
    if b == 0:
        return a, 1, 0
    g, x, y = egcd(b, a % b)
    return g, y, x - (a // b) * y

def inverse(a, n):
    g, x, _ = egcd(a, n)
    if g != 1:
        raise ValueError(f"{a} has no inverse mod {n}")
    return x % n

inverse(3, 26)      # 9
pow(3, -1, 26)      # 9  -- Python 3.8+ has this built in

9. Enormous exponents, small amounts of work

Predict first

How many multiplications does it take to compute 7^1000000 mod 13?

Square-and-multiply works by reading the exponent in binary: repeatedly square the base, and multiply into the accumulator wherever the exponent has a 1 bit. A 2048-bit exponent costs about 3,000 modular multiplications rather than 220482^{2048} of them.

Fermat's little theorem is the second lever:

ap11(modp)for prime p and paa^{p-1} \equiv 1 \pmod p \quad \text{for prime } p \text{ and } p \nmid a

In practice: pow(base, exp, mod) in Python does square-and-multiply for you and is the only correct way to write this. Computing base ** exp % mod builds the full integer first and will hang the process for any realistic exponent.

10. Why cryptography lives here

Modular arithmetic supplies something rare: operations that are cheap forwards and, as far as anyone knows, expensive backwards.

  • Computing gxmodpg^x \bmod p takes a few thousand multiplications.
  • Recovering xx from gxmodpg^x \bmod p, the discrete logarithm, has no known efficient method for well-chosen pp.
  • Multiplying two large primes is instant. Factoring the product back is not.

That gap is the entire basis of public-key cryptography, and it is a gap in what we currently know how to compute rather than a proven impossibility. If someone found a fast factoring algorithm tomorrow, the mathematics in this lesson would be unchanged and a great deal of infrastructure would not be.

How those hard problems become working key exchange, signatures and certificates is the subject of the separate Applied Modern Cryptography course in this catalogue. What this lesson gives you is the arithmetic underneath it: congruences, inverses, Euclid, and exponentiation that finishes.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. Why can you reduce mod n after every multiplication instead of only at the end?
    • Because multiplication distributes over the modulus
    • Because congruence is preserved by multiplication, so the reduced product is congruent to the full one
    • Because the modulus is always prime in practice
    • You cannot: reducing early changes the answer for large inputs
  2. Does 6 have a multiplicative inverse modulo 15?
    • No, because gcd(6, 15) = 3, not 1
    • Yes, it is 13
    • Yes, since every non-zero element of Z_15 is invertible
    • Only if 15 is treated as prime
  3. In C, what does (i - 1) % n evaluate to when i is 0 and n is 8?
    • 7, the wraparound value
    • 0
    • -1, because C takes the sign from the dividend
    • Undefined behaviour
  4. Euclid's algorithm on a pair of 1,000,000-ish numbers takes about 30 steps. Why so few?
    • Because it factors both numbers first and factoring is fast
    • Because it only works on Fibonacci numbers
    • Because most pairs are coprime, which terminates immediately
    • Because the remainder at least halves every two steps, so the step count is logarithmic in the input
  5. What is the correct way to compute a^b mod n for a 2048-bit exponent in Python?
    • pow(a, b, n), which uses square-and-multiply
    • (a ** b) % n
    • a ** (b % n)
    • math.pow(a, b) % n

Related lessons

Math
intermediate

Counting Without Listing

Combinatorics answers how many arrangements exist without producing any of them, which is what makes password strength, hash collisions and search-space size computable at all. This lesson builds the product rule, permutations, combinations, inclusion-exclusion and the pigeonhole principle, then applies them to problems where intuition is reliably wrong.

10 steps·~15 min
Math
intermediate

Graphs: A Language for Relationships

A graph is two sets and an incidence relation, and that austerity is why the same object models build dependencies, social networks, register allocation and road maps. This lesson covers the structural properties worth knowing, the special families that make hard problems easy, and the line where a small change to a question makes it intractable.

10 steps·~15 min
Math
intermediate

Proof and Induction: Covering Infinitely Many Cases

Testing checks the cases you thought of; a proof covers all of them at once, including the ones nobody will ever run. This lesson builds direct proof, contradiction and induction as working tools, shows the two ways induction fails, and connects it to the loop invariants that make a program correct rather than merely untested.

10 steps·~15 min
Computer Science
advanced

Bloom Filters: Membership in a Bit Array

A Bloom filter answers set membership using a bit array and a handful of hash functions, with no items stored anywhere. This lesson builds it, derives the sizing formula that trades memory against false positives, explains exactly why deletion is impossible, and covers the variants that buy it back.

8 steps·~12 min