Modular Arithmetic, Inverses, and Fast Exponentiation
Working in \(\Z/n\Z\): congruences, inverses, Fermat and Euler, the Chinese Remainder Theorem, and computing \(a^b\bmod n\) in \(O(\log b)\) multiplications.
Leads to: 19.13 exponentiates matrices with the same doubling trick; the Euler Lab’s Number Theory path is saturated with ‘give the last 10 digits’ problems that require modpow.
Learning Objectives
Click a status chip to cycle: Not started → In progress → Studied → Practiced → Needs review → Mastered.
- Define congruence and prove that addition and multiplication are well defined on residue classes.
- Determine when \(a^{-1}\bmod n\) exists and compute it via extended Euclid.
- State and apply Fermat’s little theorem and Euler’s theorem, including the exponent-reduction rule.
- Implement binary (fast) exponentiation and justify its \(O(\log b)\) multiplication count.
- Solve a system of congruences with the Chinese Remainder Theorem and explain why coprimality is required.
Key Vocabulary
- Congruence
- \(a\equiv b\pmod n\) iff \(n\mid a-b\); an equivalence relation whose classes are the \(n\) residues.
- Unit / invertible residue
- \(a\) with \(\gcd(a,n)=1\); exactly these have an inverse mod \(n\), and they form a group \((\Z/n\Z)^\times\) of size \(\varphi(n)\).
- Fermat’s little theorem
- For prime \(p\) and \(p\nmid a\): \(a^{p-1}\equiv1\pmod p\). Hence \(a^{-1}\equiv a^{p-2}\).
- Euler’s theorem
- For \(\gcd(a,n)=1\): \(a^{\varphi(n)}\equiv1\pmod n\); so exponents may be reduced mod \(\varphi(n)\).
- Binary exponentiation
- Square-and-multiply: \(a^b\) from the binary digits of \(b\) using \(\le 2\log_2 b\) modular multiplications.
- Chinese Remainder Theorem
- For pairwise coprime \(n_1..n_k\), the map \(x\mapsto(x\bmod n_i)\) is a ring isomorphism \(\Z/N\Z\cong\prod\Z/n_i\Z\), \(N=\prod n_i\).
Intuition & Motivation
Congruence is a ring structure
Practical consequence: reduce mod \(n\) after every operation. The answer is unchanged and the operands never grow. Python’s big integers hide the cost of forgetting this - but \(2^{10^6}\) is a 300 000-digit number, and multiplying such things is thousands of times slower than staying inside \([0,n)\).
Inverses: exactly the units
Fast exponentiation
Write \(b\) in binary, \(b=\sum_i b_i 2^i\). Then \(a^{b}=\prod_{i:\,b_i=1} a^{2^i}\), and the powers \(a^{2^i}\) are obtained by repeated squaring:
Each loop iteration halves \(b\), so there are \(\lfloor\log_2 b\rfloor+1\) iterations and at most two modular multiplications per iteration: \(O(\log b)\) total, versus \(b-1\) for the naive loop. For \(b=10^{18}\) that is 60 iterations instead of a quintillion.
The Chinese Remainder Theorem
Coding workbench
In the Euler Lab, modular arithmetic is the difference between an answer and an overflow: problems that ask for the last digits of a gigantic quantity are asking you to never build the gigantic quantity. That is the whole lesson.
- Reducing an exponent mod \(n\) instead of mod \(\varphi(n)\) - exponents live in a different world from bases.
- Applying Euler’s theorem when \(\gcd(a,n)\ne1\). It is simply false there (use lifting-the-exponent or CRT on prime powers instead).
- Forgetting that Python’s % of a negative number is already non-negative, then ‘fixing’ it and introducing a bug - or porting to C and forgetting to fix it there.
- Dividing by \(k\) in a modular formula (e.g. a binomial coefficient) instead of multiplying by \(k^{-1}\). Modular division does not exist; modular multiplication by an inverse does.
- Using CRT with non-coprime moduli. Then the system may be inconsistent, or have \(\gcd\) solutions - you need the generalized CRT with a solvability check.
- Prime moduli like \(10^9+7\) are chosen so that every nonzero residue is invertible - that is why competitive problems ask for answers mod a prime.
- Precompute factorials and inverse factorials mod \(p\) once; then any binomial \(\binom{n}{k}\bmod p\) is an \(O(1)\) lookup (19.5).
- Python’s pow(a,b,n) is a C implementation of exactly (19.5), and pow(a,-1,n) gives the inverse. Write your own once to understand it, then use the built-in.
Practice this in the Euler Lab
Computational problems that exercise exactly this technique. Each opens in the Euler Lab with a Python workbench, a progressive hint ladder, and answer checking. Tier A/B run at full scale in the browser.
Warm-up:
#1 Multiples of 3 or 5 (1%, tier A) #2 Even Fibonacci Numbers (1%, tier A) #7 10 001st Prime (1%, tier A) #20 Factorial Digit Sum (1%, tier A)
Applied:
#293 Pseudo-Fortunate Numbers (16%, tier B) #313 Sliding Game (16%, tier B) #357 Prime Generating Integers (16%, tier B)
Challenge:
#161 Triominoes (41%, tier C) #182 RSA Encryption (41%, tier C)
483 Project Euler problems in total are mapped to this lesson. Open the Euler Lab to filter them all.
Knowledge Check
Practical Exercise
(a) Compute the last three digits of \(7^{1234567}\) by hand, i.e. \(7^{1234567}\bmod1000\), using Euler’s theorem and CRT. (b) Explain precisely why you may reduce the exponent mod \(\varphi(1000)\) here, and give an \(a\) for which you may not.
(a) \(1000=8\cdot125\), which are coprime, so work mod 8 and mod 125 and glue with CRT.
Mod 8: \(7\equiv-1\), so \(7^{1234567}\equiv(-1)^{\text{odd}}=-1\equiv7\pmod 8\).
Mod 125: \(\varphi(125)=100\) and \(\gcd(7,125)=1\), so exponents reduce mod 100: \(1234567\equiv67\pmod{100}\), giving \(7^{67}\bmod125\). By square-and-multiply: \(7^2=49\), \(7^4=49^2=2401\equiv26\), \(7^8\equiv26^2=676\equiv51\), \(7^{16}\equiv51^2=2601\equiv101\), \(7^{32}\equiv101^2=10201\equiv76\), \(7^{64}\equiv76^2=5776\equiv26\). Now \(67=64+2+1\): \(7^{67}\equiv26\cdot49\cdot7\). \(26\cdot49=1274\equiv1274-1250=24\); \(24\cdot7=168\equiv43\pmod{125}\).
CRT: find \(x\equiv7\pmod 8,\ x\equiv43\pmod{125}\). Write \(x=43+125t\); then \(43+125t\equiv7\pmod 8\). Since \(43\equiv3\) and \(125\equiv5\pmod 8\): \(3+5t\equiv7\Rightarrow5t\equiv4\pmod8\). \(5^{-1}\equiv5\pmod8\) (as \(25\equiv1\)), so \(t\equiv20\equiv4\pmod8\). Thus \(x=43+125\cdot4=543\).
Answer: the last three digits are 543. (Verify: pow(7,1234567,1000) returns 543.)
(b) Euler’s theorem \(a^{\varphi(n)}\equiv1\) requires \(\gcd(a,n)=1\), which holds for \(a=7,\ n=125\) (and mod 8). For \(a=10\) and \(n=1000\) it fails: \(\gcd(10,1000)=10\), and indeed \(10^k\bmod1000\) is eventually always 0, never returning to 1. There you split \(1000=8\cdot125\) and handle the non-coprime prime power directly (the powers of 2 in \(a\) simply saturate).
Lesson Summary
Retrieval Practice
Close the lesson and answer from memory before checking. This is deliberate, effortful recall - the single highest-yield study action.
A: Extended Euclid: works for any \(n\) with \(\gcd(a,n)=1\), cost \(O(\log n)\). Fermat: \(a^{p-2}\bmod p\), requires a PRIME modulus, cost \(O(\log p)\) multiplications.
A: Each iteration halves \(b\) (one squaring), and multiplies into the accumulator only on a 1-bit. So the number of iterations is the bit-length of \(b\), with ≤2 modular multiplications each.
Completion Checklist
- I can explain the core ideas in my own words
- I worked the derivations/examples by hand
- I completed the interactive workbench(es)
- I passed the knowledge check
Source References
This lesson synthesizes and paraphrases concepts from the sources below. No copyrighted text, problem sets, or solutions are reproduced. Return to the originals for full depth.
- Calculus, Vol. I (Tom Apostol, 2nd ed., 1967) foundational - Ch. I.4 - Series, induction and the algebra of the integers: the well-definedness argument for congruence classes is the same equivalence-class reasoning used throughout.
- Linear Algebra Done Right (Sheldon Axler, 4th ed., 2024) current - Ch. 1 (fields) - Group and field structure: \((\Z/p\Z)\) is a field, which is exactly why every nonzero residue is invertible - the abstract statement behind Fermat.
- The Pragmatic Programmer (Thomas & Hunt, 2nd/20th Anniv. ed., 2019) current - Ch. 4 (Pragmatic Paranoia) - Defensive coding: modinv must fail loudly when no inverse exists, rather than silently returning garbage.