Modular Inverse: General Case and Precomputation
Pillar: Chain — "Each inverse depends on the previous: \(\text{inv}[i] = -(m/i) \times \text{inv}[m \bmod i] \bmod m\). This IS a chain recurrence." Pillar: Fix — "When \(m\) isn't prime, fix \(ax \equiv 1 \pmod{m}\) and solve via Extended Euclidean."
The Setup
Ch4 L3 gave you one tool: Fermat's Little Theorem. It computes \(a^{-1} \bmod p\) in \(O(\log p)\), but only when \(p\) is prime. Two gaps remain.
First, what if \(m\) is not prime? Problems with moduli like \(10^6\), \(998244352\) (prime, fine), or arbitrary \(m\) given as input all show up in contest and interview settings. Fermat doesn't apply when \(m\) is composite.
Second, what if you need the inverses of \(1, 2, 3, \ldots, n\) all at once? Computing each one individually via Fermat or Extended GCD costs \(O(n \log m)\). There's a recurrence that does it in \(O(n)\) — one multiplication per value.
This lesson fills both gaps.
When \(m\) Is Not Prime
The equation you're solving is \(ax \equiv 1 \pmod{m}\). Rewrite it: \(ax + my = 1\) for some integer \(y\). This is exactly Bezout's identity from Ch3 L3. The Extended Euclidean algorithm finds \(x\) and \(y\).
An inverse exists if and only if \(\gcd(a, m) = 1\). When \(\gcd(a, m) > 1\), the left side \(ax + my\) is always a multiple of \(\gcd(a, m)\), so it can never equal \(1\).
Worked Example: \(3^{-1} \bmod 10\)
Solve \(3x \equiv 1 \pmod{10}\), i.e., find \(x\) such that \(3x + 10y = 1\).
Run Extended Euclidean on \((10, 3)\):
| Step | \(a\) | \(b\) | \(q\) | \(r\) | Coefficients |
|---|---|---|---|---|---|
| 0 | \(10\) | \(3\) | \(3\) | \(1\) | — |
| 1 | \(3\) | \(1\) | \(3\) | \(0\) | — |
Unwind:
So \(x = -3\) for variable \(a = 3\), meaning \(3 \times (-3) + 10 \times 1 = -9 + 10 = 1\). Check.
Reduce: \(x = -3 \equiv 7 \pmod{10}\).
Verify: \(3 \times 7 = 21 = 2 \times 10 + 1\), so \(21 \bmod 10 = 1\). Confirmed: \(3^{-1} \bmod 10 = 7\).
The Algorithm
Extract the coefficient of \(a\) from the Extended Euclidean algorithm. If \(\gcd(a, m) \neq 1\), no inverse exists — return \(-1\) or signal an error.
long long modInverseExtGCD(long long value, long long mod) {
long long oldRemainder = value, newRemainder = mod;
long long oldCoefficient = 1, newCoefficient = 0;
while (newRemainder != 0) {
long long quotient = oldRemainder / newRemainder;
long long tempRemainder = newRemainder;
newRemainder = oldRemainder - quotient * newRemainder;
oldRemainder = tempRemainder;
long long tempCoefficient = newCoefficient;
newCoefficient = oldCoefficient - quotient * newCoefficient;
oldCoefficient = tempCoefficient;
}
if (oldRemainder != 1) return -1;
return (oldCoefficient % mod + mod) % mod;
}
\(O(\log(\min(a, m)))\) time, \(O(1)\) space.
This is the same Extended GCD from Ch3 L3, but we only track the coefficient for \(a\) (not \(b\)), and we reduce the result into \([0, m)\).
Precomputing All Inverses \(1\) to \(n\) in \(O(n)\)
This is the centerpiece of the lesson. You need the inverse of every integer from \(1\) to \(n\) modulo a prime \(m\). Computing each one via Fermat costs \(O(\log m)\) per value, for \(O(n \log m)\) total. The chain recurrence does it in \(O(n)\).
The Derivation
Start with the division algorithm. Write \(m\) divided by \(i\):
Let \(q = \lfloor m/i \rfloor\) and \(r = m \bmod i\). So \(m = qi + r\).
Take this equation modulo \(m\). The left side vanishes:
Rearrange:
Multiply both sides by \(i^{-1} \cdot r^{-1}\) (both exist because \(m\) is prime and \(1 \leq i, r < m\)):
Therefore:
Substitute back \(q = \lfloor m/i \rfloor\) and \(r = m \bmod i\):
Since \(m \bmod i < i\), the right side references an inverse we've already computed. That's the chain: each value depends on a strictly earlier one.
Base Case
\(\text{inv}[1] = 1\), because \(1 \times 1 \equiv 1 \pmod{m}\) for any \(m\).
Trace Table: Inverses Mod 7
Walk through \(i = 1\) to \(6\) with \(m = 7\).
| \(i\) | \(\lfloor 7/i \rfloor\) | \(7 \bmod i\) | \(\text{inv}[7 \bmod i]\) | \(\text{inv}[i] = (7 - \lfloor 7/i \rfloor \times \text{inv}[7 \bmod i] \bmod 7) \bmod 7\) | Verify: \(i \times \text{inv}[i] \bmod 7\) |
|---|---|---|---|---|---|
| \(1\) | \(7\) | \(0\) | — (base) | \(1\) | \(1 \times 1 = 1\) |
| \(2\) | \(3\) | \(1\) | \(\text{inv}[1] = 1\) | \((7 - 3 \times 1 \bmod 7) \bmod 7 = (7 - 3) \bmod 7 = 4\) | \(2 \times 4 = 8 \equiv 1\) |
| \(3\) | \(2\) | \(1\) | \(\text{inv}[1] = 1\) | \((7 - 2 \times 1 \bmod 7) \bmod 7 = (7 - 2) \bmod 7 = 5\) | \(3 \times 5 = 15 \equiv 1\) |
| \(4\) | \(1\) | \(3\) | \(\text{inv}[3] = 5\) | \((7 - 1 \times 5 \bmod 7) \bmod 7 = (7 - 5) \bmod 7 = 2\) | \(4 \times 2 = 8 \equiv 1\) |
| \(5\) | \(1\) | \(2\) | \(\text{inv}[2] = 4\) | \((7 - 1 \times 4 \bmod 7) \bmod 7 = (7 - 4) \bmod 7 = 3\) | \(5 \times 3 = 15 \equiv 1\) |
| \(6\) | \(1\) | \(1\) | \(\text{inv}[1] = 1\) | \((7 - 1 \times 1 \bmod 7) \bmod 7 = (7 - 1) \bmod 7 = 6\) | \(6 \times 6 = 36 \equiv 1\) |
Every product checks out. The pattern \(\text{inv}[i] + \text{inv}[7 - i] = 7\) is no coincidence — it follows from \(i^{-1} + (m-i)^{-1} \equiv 0 \pmod{m}\).
The Code
vector<long long> precomputeInverses(int limit, long long mod) {
vector<long long> inverse(limit + 1, 0);
inverse[1] = 1;
for (int i = 2; i <= limit; i++) {
inverse[i] = (mod - (mod / i) * inverse[mod % i] % mod) % mod;
}
return inverse;
}
\(O(n)\) time, \(O(n)\) space.
The expression mod - (mod / i) * inverse[mod % i] % mod implements \(-\lfloor m/i \rfloor \cdot \text{inv}[m \bmod i] \bmod m\) without ever going negative. The outer mod - replaces the negation, and the final % mod handles the case where the subtraction equals exactly \(m\) (producing \(0\)).
Precomputed Factorials (Preview)
Many combinatorics problems (starting in Ch5) need \(n! \bmod m\) for various \(n\). The idea is simple: build a table where \(\text{fact}[i] = i! \bmod m\) using the recurrence \(\text{fact}[i] = \text{fact}[i-1] \times i \bmod m\). This is \(O(n)\) time and \(O(n)\) space.
To compute \(\binom{n}{k} \bmod m\), you'll also need inverse factorials — a second table built using a single modpow call and a backward loop. Ch5 L2 teaches the complete factorial + inverse factorial precomputation as a single unit, after introducing the combinatorial identities that motivate it. That's where you'll write the code.
Decision Table: Which Method When?
| Scenario | Method | Time | Constraint |
|---|---|---|---|
| Single inverse, \(m\) is prime | Fermat: \(a^{m-2} \bmod m\) (Ch4 L3) | \(O(\log m)\) | \(\gcd(a, m) = 1\), \(m\) prime |
| Single inverse, \(m\) arbitrary | Extended GCD: solve \(ax + my = 1\) | \(O(\log m)\) | \(\gcd(a, m) = 1\) |
| All inverses \(1\) to \(n\), \(m\) prime | Chain recurrence | \(O(n)\) | \(m\) prime, \(n < m\) |
| \(n!\) for all \(n\) up to limit | Factorial precomputation | \(O(n)\) | — |
| \(\binom{n}{k} \bmod m\) for many queries | Factorial + inverse factorial (Ch5 L2) | \(O(n)\) precompute, \(O(1)\) query | \(m\) prime |
The default competitive programming modulus \(10^9 + 7\) is prime, so Fermat and the chain recurrence both apply. Use Extended GCD only when the problem gives an arbitrary (possibly composite) modulus.
One subtlety: the chain recurrence requires \(m\) to be prime. If \(m\) is composite, \(m \bmod i\) might share a factor with \(m\), and \(\text{inv}[m \bmod i]\) wouldn't exist. For composite moduli, compute inverses individually via Extended GCD (only for values coprime to \(m\)).
The Code (Complete)
All three functions together:
#include <bits/stdc++.h>
using namespace std;
long long modInverseExtGCD(long long value, long long mod) {
long long oldRemainder = value, newRemainder = mod;
long long oldCoefficient = 1, newCoefficient = 0;
while (newRemainder != 0) {
long long quotient = oldRemainder / newRemainder;
long long tempRemainder = newRemainder;
newRemainder = oldRemainder - quotient * newRemainder;
oldRemainder = tempRemainder;
long long tempCoefficient = newCoefficient;
newCoefficient = oldCoefficient - quotient * newCoefficient;
oldCoefficient = tempCoefficient;
}
if (oldRemainder != 1) return -1;
return (oldCoefficient % mod + mod) % mod;
}
vector<long long> precomputeInverses(int limit, long long mod) {
vector<long long> inverse(limit + 1, 0);
inverse[1] = 1;
for (int i = 2; i <= limit; i++) {
inverse[i] = (mod - (mod / i) * inverse[mod % i] % mod) % mod;
}
return inverse;
}
vector<long long> precomputeFactorials(int limit, long long mod) {
vector<long long> factorial(limit + 1);
factorial[0] = 1;
for (int i = 1; i <= limit; i++) {
factorial[i] = factorial[i - 1] * i % mod;
}
return factorial;
}
Common Mistakes
-
Forgetting the negation in the chain recurrence. Writing
(mod / i) * inverse[mod % i] % modwithout the negation gives a wrong value that still looks plausible. The formula has a minus sign — implement it asmod - (...)to avoid negative intermediate values. -
Using the chain recurrence with composite \(m\). The derivation divides by \(r = m \bmod i\), which requires \(\gcd(r, m) = 1\). When \(m\) is composite, this can fail silently — the recurrence produces garbage without any runtime error. Only use it when \(m\) is prime.
-
Calling Extended GCD when \(\gcd(a, m) \neq 1\) and not checking. The function returns \(-1\) when no inverse exists, but if you ignore the return value and use it as a valid inverse, you'll get wrong answers. Always check or ensure \(\gcd(a, m) = 1\) before using the result.
Quick Recap
- Extended GCD computes \(a^{-1} \bmod m\) for any \(m\) (not just primes), as long as \(\gcd(a, m) = 1\). Runs in \(O(\log m)\).
- The chain recurrence \(\text{inv}[i] = -\lfloor m/i \rfloor \cdot \text{inv}[m \bmod i] \bmod m\) precomputes all inverses from \(1\) to \(n\) in \(O(n)\). Requires \(m\) prime.
- Factorial precomputation builds \(i! \bmod m\) for \(i = 0\) to \(n\) in \(O(n)\). Combine with inverse factorials (Ch5 L2) for fast \(\binom{n}{k}\) queries.
- Use Fermat (\(a^{m-2}\)) for one-off inverses with prime \(m\). Use Extended GCD for composite \(m\). Use the chain recurrence when you need all inverses \(1\) to \(n\).
Problems
| Problem | Link | Difficulty | Focus |
|---|---|---|---|
| CSES — Binomial Coefficients | cses.fi/problemset/task/1079 | Medium | Precomputed factorials + inverse factorials (preview of Ch5 L2) |
Next lesson: Euler's Totient Function — counting integers coprime to \(n\), and generalizing Fermat's theorem to composite moduli.