Modular Arithmetic Explained — Binary Exponentiation, Modular Inverse, Fermat's Little Theorem [LC 50, Google, Stripe]
Advertisement
Algorithm Statement
Modular arithmetic is the system of integers under a fixed modulus m, where every value is reduced to its remainder mod m. The four operations you must master:
- Binary exponentiation: compute in .
- Modular inverse: find such that .
- Modular addition / subtraction / multiplication with overflow safety.
- Fermat's little theorem: if p is prime and , then .
Constraints typical in interviews:
- is often prime, usually or .
Example (LC 50 Pow(x, n) variant for integers under mod):
Input: a = 2, n = 10, m = 1000
Output: 24 (since 2^10 = 1024, and 1024 mod 1000 = 24)Why This Problem Matters
Almost every hard problem at Google, Meta, Stripe, and competitive programming sites uses modular arithmetic. The reason is simple: counting problems and combinatorial answers explode quickly. The number of paths in a grid, the number of valid sequences, the number of subsets satisfying a constraint — these grow far beyond 64-bit integers. To return a finite answer, problem setters add the phrase "return the answer modulo ."
If you cannot do modular exponentiation in , problems like LC 50 Pow(x, n), LC 1922 Count Good Numbers, LC 2400 Number of Ways to Reach a Position After Exactly k Steps, and LC 1735 Count Ways to Make Array With Product are unreachable. Stripe specifically uses modular arithmetic in cryptographic verification, idempotency hashing, and fee computation — interviewers there expect candidates to know binary exponentiation cold.
This is the gateway technique. Get it right and the entire number-theory tree opens up.
The Core Insight
Modular arithmetic obeys the same algebraic identities as ordinary arithmetic, with one big exception: division. The four key identities are:
Subtraction needs the extra because the difference can be negative; many languages return negative remainders for negative operands.
Binary exponentiation rests on the binary expansion of the exponent. Write in binary as . Then
a^n = \prod_{i : b_i = 1} a^{2^i}.We square at each step (so we have ) and multiply only when the corresponding bit of is 1. Total work: multiplications.
Modular inverse via Fermat. If is prime and , Fermat's little theorem says . Multiplying both sides by gives . So computing via binary exponentiation gives you in time. This works only when is prime — otherwise use the Extended Euclidean Algorithm.
Proof sketch of Fermat. Consider the residues . Because , these are a permutation of . Multiplying both sides: . Cancelling (which is invertible mod by Wilson) yields .
Visual Dry Run
Compute using binary exponentiation. The exponent in binary is .
result = 1
base = 3
exp = 13 (binary 1101)
Step 1: exp & 1 = 1 → result = 1 * 3 = 3
base = 3*3 = 9, exp = 6 (binary 110)
Step 2: exp & 1 = 0 → result unchanged (3)
base = 9*9 = 81, exp = 3 (binary 11)
Step 3: exp & 1 = 1 → result = 3 * 81 = 243
base = 81*81 = 6561 mod 1000 = 561, exp = 1
Step 4: exp & 1 = 1 → result = 243 * 561 mod 1000
= 136323 mod 1000 = 323
Final: 3^13 mod 1000 = 323Verification: 3^{13} = 1{,}594{,}323, and 1{,}594{,}323 \bmod 1000 = 323. Correct.
For modular inverse, find . By Fermat, . Compute . Verify: . Correct.
Solution (Optimal)
Python
MOD = 10**9 + 7
def power(a: int, n: int, m: int = MOD) -> int:
"""Compute a^n mod m in O(log n)."""
a %= m
result = 1
while n > 0:
if n & 1:
result = result * a % m
a = a * a % m
n >>= 1
return result
def mod_inverse(a: int, p: int = MOD) -> int:
"""Modular inverse of a mod prime p via Fermat's little theorem."""
return power(a, p - 2, p)
def mod_add(a: int, b: int, m: int = MOD) -> int:
return (a % m + b % m) % m
def mod_sub(a: int, b: int, m: int = MOD) -> int:
return (a % m - b % m + m) % m
def mod_mul(a: int, b: int, m: int = MOD) -> int:
return (a % m) * (b % m) % m
def mod_div(a: int, b: int, p: int = MOD) -> int:
return mod_mul(a, mod_inverse(b, p), p)JavaScript
const MOD = 1_000_000_007n;
function power(a, n, m = MOD) {
a = ((a % m) + m) % m;
let result = 1n;
while (n > 0n) {
if (n & 1n) result = (result * a) % m;
a = (a * a) % m;
n >>= 1n;
}
return result;
}
function modInverse(a, p = MOD) {
return power(a, p - 2n, p);
}
function modMul(a, b, m = MOD) {
return ((a % m) * (b % m)) % m;
}Complexity: time, extra space for each operation.
Use BigInt in JavaScript whenever the modulus exceeds , because the intermediate product can overflow 53-bit Number precision. Python integers are arbitrary precision, so the issue does not arise.
Common Mistakes
- Forgetting to reduce inputs. If
ais already larger thanm, the first multiplication can overflow. Always start witha %= m. - Negative remainders in subtraction. Python's
%returns non-negative results, but C, C++, Java, and JavaScript do not. Addmbefore the final mod. - Using Fermat with a composite modulus. Fermat requires the modulus to be prime. For composite
m, use the Extended Euclidean Algorithm. - Computing inverse of zero or a non-coprime value. has no inverse, and if the inverse does not exist.
- Iterative multiplication instead of fast power. Computing by multiplying times is — far too slow for .
- JavaScript Number overflow. Native numbers lose precision past . Always use BigInt for serious modular work.
- Mutating the global MOD. Keep MOD as a constant, not a variable that can be accidentally reassigned.
Interview Tips
- State the modulus first: "I will assume MOD = 1e9 + 7 unless told otherwise." Show you know the convention.
- Write the helper
power(a, n, m)once and reuse it. Interviewers love clean abstractions. - When asked for a closed-form on a counting problem, mention modular inverse for division — this signals fluency.
- If the modulus is not prime, immediately pivot to Extended Euclidean. Do not blindly apply Fermat.
- For very large moduli where overflows 64 bits, mention
__int128(C++) or Python's arbitrary precision.
Follow-up Questions
Q1: How would you compute the inverse if the modulus is not prime? A: Use the Extended Euclidean Algorithm. It returns integers such that . If , then is the inverse.
Q2: Compute where does not fit in 64 bits.
A: Use Python's native big integers, or in C++ use __int128 for the intermediate product, or use Montgomery reduction.
Q3: Compute the sum of in . A: Use the geometric-sum recurrence: split into even and odd indices and recurse. Or use with modular inverse when is coprime to .
Q4: Why is chosen as the modulus? A: It is prime (so Fermat works), close to (so fits in 64 bits), and unlikely to collide with structural properties of the problem.
Key Takeaways
- Binary exponentiation computes in by repeated squaring on the binary digits of .
- Fermat's little theorem turns modular inverse into modular exponentiation: when is prime.
- Always reduce intermediates modulo to prevent overflow; use BigInt in JavaScript and
long long(or__int128) in C++. - Subtraction needs
+ mthen% min languages where the modulo of a negative is negative. - Composite moduli demand Extended Euclidean, not Fermat — the prime-only assumption is a common interview trap.
- The modulo is prime and chosen so that products of two values under it still fit in a 64-bit integer.
Advertisement