Learning Objectives
Modular arithmetic is the algebra of remainders. It is one of the smallest pieces of mathematics that pays the largest dividends in computing: hashing, cryptography, pseudo-random generators, error-correcting codes, circular buffers, parsers for time and dates, and an entire branch of competitive programming all live inside the world of . By the end of this section you will be able to:
- State and use the congruence relation and reason about equivalence classes.
- Manipulate modular expressions algebraically, knowing exactly when addition, subtraction, multiplication, and division are and are not safe.
- Compute in time by repeated squaring, in both Python and TypeScript with
BigInt. - Find modular inverses via the extended Euclidean algorithm and via Fermat's little theorem when is prime.
- Recognize when modular arithmetic is the right tool, from hash tables and Rabin–Karp to RSA and circular buffers.
- Avoid the classical landmines: negative numbers, signed-vs-unsigned overflow, non-prime moduli, and bad hash-table sizes.
Why this matters: almost every fast algorithm that touches integers eventually reduces them . The hash table you use a thousand times a day is built on it. The HTTPS handshake that protects your bank login is built on it. Once modular arithmetic feels natural, large stretches of algorithms stop looking magical.
Definition: Congruence Modulo n
Fix an integer , called the modulus. Two integers and are congruent modulo , written
,
if divides their difference: . Equivalently, they leave the same remainder when divided by . So because both leave remainder after division by , and divides .
Three equivalent definitions
- .
- , i.e. for some integer .
- and have the same remainder under integer division by .
Equivalence classes
Congruence is an equivalence relation: reflexive, symmetric, and transitive. So it carves the integers into disjoint equivalence classes, often written . The set of these classes, with the inherited addition and multiplication, is the ring . From the algorithm designer's point of view, is just "the integers with arithmetic that wraps around at ."
Clock Arithmetic: The Pictorial Definition
A 12-hour clock is the canonical picture of arithmetic modulo . If it is currently 7 o'clock and you wait 8 hours, the hour hand does not point to 15—it points to 3, because 15 mod 12 = 3. The clock automatically reduces every result back into . In symbols:
.
Three observations are worth memorizing because every other identity will follow from them:
- The clock has exactly positions, labeled . There is no "12" on a mod-12 clock; the position you would call 12 is the same physical position as 0.
- Walking forward by steps and walking backward by steps land you in the same place. Negation is a wrap-around.
- Multiplication is repeated addition, so it also wraps—but with a twist we will examine: not every nonzero number on the clock has a multiplicative inverse.
Interactive: The Modular Wheel
Drag the modulus slider to change , pick an operation (add , multiply by , or one binary-expansion step of squaring), and click Step to watch the pointer hop around the wheel. The history pane shows the residue stream, exactly what an algorithm sees.
Things to try
+ k, , and step repeatedly: you walk every position before returning to , because . Now try : you only ever land on multiples of . The orbit length is —a small fact with very large consequences for hashing.The Algebra of Congruences
The reason modular arithmetic is so useful in algorithms is that addition, subtraction, and multiplication respect congruence. If and , then
- ,
- ,
- .
The practical corollary is the rule that lets you keep numbers small at every step of a computation:
,
.
This is why a sum like can be computed without ever forming a number larger than about : reduce after every multiplication.
Why division is special
The same convenience does not hold for division. From we cannot divide both sides by 2 to conclude —that statement is false. Division in requires a modular inverse, which only exists when . We will return to this in a few sections.
A clean proof sketch
Why does multiplication respect congruence? If and , then
.
The second term is a multiple of , so . The proofs for addition and subtraction are identical and shorter.
Negative Numbers and the C/Python Divide
Mathematically, the canonical residue of mod is the unique integer in . Real programming languages do not all agree on this convention.
| Language | (-7) % 3 evaluates to | Why |
|---|---|---|
| Python | 2 | Result has the sign of the divisor; result is in [0, n-1] when n > 0. |
| C, C++, Java, Go, Rust, JavaScript | -1 | Result has the sign of the dividend; truncation toward zero. |
| Mathematica, R (with %%) | 2 | Floor-division semantics, like Python. |
Python and Mathematica use floor division: with the remainder always nonnegative. C and friends use truncation toward zero: with the remainder taking the sign of .
The canonicalization trick
%, force a nonnegative remainder with the idiom1r = ((a % n) + n) % n; // works in C, C++, Java, Go, Rust, JS, TS+ n costs one cycle and removes an entire bug class.Modular Exponentiation by Repeated Squaring
Many algorithms—Rabin–Karp, Miller–Rabin primality, RSA, Diffie–Hellman, polynomial hashing, modular combinatorics—need to compute for large . Computing naively, then reducing, is hopeless: with and the intermediate number has about half a billion digits.
The fix is the identity
Each level of recursion halves , so the depth is . The iterative form walks the binary representation of from least to most significant bit, maintaining two registers: and a running . Wherever the bit is , multiply the result by the current base; in either case, square the base.
Python implementation
TypeScript implementation with BigInt
In any fixed-precision language, the product can overflow even when both operands are below . For cryptographic-size moduli the only sane option is an arbitrary-precision integer type. JavaScript and TypeScript ship one: bigint.
Overflow trap in fixed-precision languages
uint64_t, the product with a, b < n < 2^{63} overflows 64 bits. Real implementations either widen to __int128, use Montgomery or Barrett reduction, or fall back to a double-based correction trick. Python and BigInt hide the problem; in any language with fixed-width integers, you must face it.Interactive: Watching Repeated Squaring
The visualizer below traces every iteration of power_mod(a, b, n). The binary expansion of is highlighted, and each row shows whether the current bit triggered a multiplication and what the running result becomes. The number of steps is exactly the number of bits in : that is the guarantee, made physical.
| i | bit | base = a^(2^i) mod n | action | result mod n |
|---|---|---|---|---|
| 0 | 0 | 7 | skip (bit is 0) | 1 |
Try this
Modular Inverses
The modular inverse of modulo is the unique residue satisfying
.
It exists if and only if . The proof is constructive: Bézout's identity guarantees integers with , and then is the inverse. The tool that finds is the extended Euclidean algorithm.
Method 1: Extended Euclidean
Method 2: Fermat's little theorem (when n is prime)
If is prime and , then , so multiplying both sides by gives the closed form
.
Combined with power_mod, this is a one-liner that runs in time. Competitive programmers reach for it constantly because is prime.
1def mod_inverse_prime(a: int, p: int) -> int:
2 """Inverse of a mod p when p is prime and gcd(a, p) = 1."""
3 return power_mod(a, p - 2, p)Which method to use
power_mod available—the code is shorter and easier to audit.Fermat's Little Theorem
Stated cleanly: for any prime and any integer with ,
.
Equivalently, for every integer (no coprimality needed), . The proof, due to Euler, observes that the map permutes , so the product of those residues equals the product of their -multiples. Cancel the common product, and the identity drops out.
Why algorithms care
- Cheap inverses mod a prime, as we just saw.
- Probabilistic primality testing. Pick a random ; if , then is composite. The contrapositive is the basis of the Fermat test, refined into the Miller–Rabin test you will meet in Chapter 37.
- RSA correctness. The key derivation in RSA uses Fermat (and its generalization, Euler's theorem) to guarantee that decryption inverts encryption.
A Glimpse of the Chinese Remainder Theorem
Suppose moduli are pairwise coprime, with product . The Chinese Remainder Theorem says the system of congruences
has a unique solution modulo , and there is an explicit formula for it. Conceptually, residues mod can be split into independent residues mod each and reassembled. Algorithmically this means: a single problem on a huge modulus can be replaced by several smaller, independent problems—massive speed wins for big-integer multiplication, RSA decryption (via and separately), and signal processing.
We will treat CRT properly in Chapter 37 once we have its full algorithmic context. For now, just remember: it is the structural reason that composite moduli factor cleanly into their prime parts.
Why Algorithms Care About Modular Arithmetic
Modular arithmetic is one of the most leveraged primitives in computing. A non-exhaustive tour:
| Domain | Where the modulus shows up | Operation that matters |
|---|---|---|
| Hash tables | Bucket index = h(k) mod m | Choose m prime, far from powers of 2 |
| Rolling hashes (Rabin–Karp) | Polynomial hash mod a large prime | Add and remove characters in O(1) |
| Pseudo-random generators | Linear congruential X_{n+1} = (a X_n + c) mod m | Period analysis via gcd and prime factors |
| Cryptography (RSA, ECC, Diffie–Hellman) | Big-integer modular exponentiation | power_mod with 2048+ bit n |
| Circular buffers / ring queues | next index = (i + 1) mod n | Wrap-around without branches |
| Hash table probing | Probe sequence (h + i) mod m or (h + c1 i + c2 i^2) mod m | Coverage of the table |
| Competitive counting problems | All answers reduced mod 10^9 + 7 or 998244353 | Inverse via Fermat for division |
| Calendar and time | Day-of-week mod 7, week-of-year mod 52 | Zeller-like formulas |
| Music and signal processing | 12-tone equal temperament, FFT mod a prime | Number-theoretic transforms |
| Distributed systems | Consistent-hashing rings of size 2^32 or 2^64 | Server placement and lookup |
Worked example: Rabin–Karp polynomial hashing
To find a pattern of length inside a text of length , define a hash for any window by treating its characters as digits in base mod a large prime :
.
Sliding the window by one position is then an operation:
.
Modular arithmetic keeps every intermediate small enough to fit in a 64-bit word; without it, the hash itself would have bits and updating it would be linear, not constant, in .
Edge Cases and Pitfalls
1. Negative numbers in C-family languages
We have already covered the canonicalization idiom. The bug is so common that some teams ban raw % in favor of a positiveMod wrapper.
2. Hashing with
It is tempting to take the modulus to be a power of two, because is a bitmask. But this throws away every bit of above position . If the high bits of your hash function carry the entropy (very common), the table degenerates and collisions explode. Use a prime modulus for hash tables, or hash with a strong mixing function before masking.
3. Inverse does not exist
Calling mod_inverse(6, 9) is a bug, not a number. Always check , or design the modulus so it is prime.
4. Off-by-one in circular indexing
For a buffer of capacity , the next index after is and the previous is . Writing in C will return for —a memory bug, not a wrap-around.
5. Overflow in fixed-width languages
With 64-bit integers, overflows whenever . Use 128-bit intermediates (__int128 in C++, BigInteger in Java) or Montgomery/Barrett reduction.
Summary
| Concept | Key formula or fact |
|---|---|
| Congruence | a ≡ b (mod n) iff n | (a − b) |
| Algebra | (a ± b) mod n and (a · b) mod n distribute over the inner mods |
| Negative residue | ((a % n) + n) % n produces the canonical [0, n−1] form |
| Fast exponent | a^b mod n in O(log b) by repeated squaring |
| Inverse | exists iff gcd(a, n) = 1; computed by extended Euclidean or Fermat (when n prime) |
| Fermat's little theorem | a^(p−1) ≡ 1 (mod p) for prime p, gcd(a, p) = 1 |
| CRT | Pairwise coprime moduli combine into one unique residue mod their product |
Modular arithmetic is the connective tissue between integer algebra and the bounded-precision world of computer arithmetic. Once the four habits—reduce early, canonicalize negatives, use repeated squaring, watch for non-existent inverses—become reflex, the rest of cryptography, hashing, and number-theoretic algorithms reads like applied common sense.
Quick Check
Use Fermat's little theorem to compute 7^20 mod 13.
Quick Check
A hash table of capacity m = 2^16 stores keys via h(k) = k mod m. What is the most likely failure mode if the hash function h tends to return values whose low 8 bits are nearly constant?
Exercises
Computation
- Compute by hand: in both Python and C semantics.
- Use repeated squaring on paper to compute . Show the binary expansion of and the running product at each step.
- Find two different ways: extended Euclidean, and Fermat's little theorem.
- Show that has no inverse modulo . What about modulo ?
Implementation
- Implement
power_modrecursively (split on parity of ) and verify it agrees with the iterative version on 1000 random inputs. - Write a Rabin–Karp substring search using rolling polynomial hashing modulo . Verify the result on a known case before trusting it; collisions are real.
- Build a circular buffer of capacity 8 using for advance and for retreat. Stress-test it with random push/pop sequences.
Conceptual
- Prove that if and , then . Where in the proof do you use ?
- Suppose is the size of a hash table and is a hash function. Argue that if shares a large factor with the typical period of , you should expect clustering. (This is the practical reason for prime moduli.)
- Why does Fermat's little theorem fail for composite ? Find an with when .
With logarithms, summations, induction, probability, and now modular arithmetic in hand, you possess the entire mathematical kit a working algorithm designer needs day-to-day. The next chapter turns this kit on its first major target: Recursion Mastery.