Modular Arithmetic
Do arithmetic on remainders: reduce after every add, subtract and multiply so results stay small, and use a prime modulus like 1e9+7 so division works.
Overview
Two integers are congruent mod `m` when they leave the same remainder on division by m. Modular arithmetic works with these remainders: (a + b) mod m = ((a mod m) + (b mod m)) mod m, likewise for subtraction and multiplication. Division is *not* free — it requires a Modular Inverse, which exists only when the divisor is coprime to m.
Problems that say "return the answer modulo 10^9 + 7" exist because the true answer (a count or product) would have thousands of digits. 10^9 + 7 is chosen because it is prime (so every nonzero residue has an inverse and Fermat's little theorem applies), and it is small enough that the product of two residues (< 10^18) fits in a signed 64-bit integer, so a * b % m never overflows. 998244353 is the other common choice because 2^23 divides m - 1, which enables NTT-based convolution.
Rules of thumb: reduce after every operation, keep values in [0, m), and fix negative results after subtraction with ((a - b) % m + m) % m. Languages with truncating % (C++, Java, JavaScript, Go) return negative remainders for negative dividends; Python's % is always non-negative for positive m.
Intuition
A mental model before the formal terms.
A 12-hour clock: 9 o'clock plus 5 hours is 2 o'clock, not 14. You never need to know how many full days have passed to answer "what time is it" — only the position on the dial. Modular arithmetic is doing all your sums on the dial. Multiplication is the same: 5 hours repeated 5 times lands at 25 mod 12 = 1. Division is the odd one out: "which time, repeated 3 times, gives 6" has two answers on a 12-hour dial (2 and 6), so it is only unambiguous when the step size shares no factor with the dial size.
How it works
- Addition:
(a + b) % m. Witha, b < mthe sum is< 2m, safe in 64-bit for anym < 2^62. - Subtraction:
(a - b + m) % mto avoid negatives whena, bare already reduced; general form((a - b) % m + m) % m. - Multiplication:
a * b % m. Requiresa, b < mandm² < 2^63, i.e.m < ~3·10^9. For largermuse 128-bit intermediates ormulmodby doubling. - Exponentiation: Fast Exponentiation with reduction at every step.
- Division by
b: multiply byb^(m-2) mod m(primem) or the extended-Euclid inverse — see Modular Inverse. - Comparison and
min/maxare meaningless after reduction; never take the modulo of a quantity you still need to compare or sort.
Why it works
Write a = q_a·m + r_a and b = q_b·m + r_b. Then a + b = (q_a + q_b)·m + (r_a + r_b) and a·b = (…)·m + r_a·r_b, so the remainder of the result depends only on r_a and r_b. Reducing early therefore changes nothing about the final remainder.
Division needs an inverse because ab ≡ ac (mod m) does not imply b ≡ c unless gcd(a, m) = 1: e.g. 2·3 ≡ 2·9 (mod 12) but 3 ≢ 9. With a prime modulus, every a ≢ 0 is coprime, so cancellation always works.
Overflow bound: with residues below m = 10^9 + 7, a product is below (10^9 + 7)² ≈ 1.0000000140·10^18 < 9.22·10^18 = 2^63, so 64-bit signed arithmetic is exact.
Recognition
How to tell a problem wants this.
- "Return the answer modulo
10^9 + 7" (or998244353) — counting paths, subsequences, arrangements, or products that overflow. - Cyclic structures: rotations, circular arrays (
(i + k) % n), days of the week, hashing (h = (h·B + c) mod m, see Rolling Hash (Polynomial Hashing)). - Divisibility questions: "is the sum divisible by k", "count subarrays whose sum is divisible by k" (prefix sums mod
k— see Prefix Sum).
Interactive visualization
Play, step, change the input. ← → and space work too.
Showing the closely related Fast Exponentiation visualization.
| e | e (binary) | e & 1 | base | result |
|---|---|---|---|---|
| 13 | 1101 | · | 3 | 1 |
1result = 1; base = base mod M2while e > 0:3 if e & 1: result = result · base mod M # this bit is set4 base = base · base mod M5 e = e >> 16return resultPseudocode
1add(a, b) = (a + b) mod m2sub(a, b) = (a - b + m) mod m3mul(a, b) = (a * b) mod m // a, b < m, m^2 fits4div(a, b) = mul(a, inverse(b)) // needs gcd(b, m) = 15normalize(x) = ((x mod m) + m) mod mImplementations
1# Modular arithmetic keeps every intermediate inside a fixed range, which is2# what makes counting problems with astronomically large answers tractable.3# The distributive laws hold for +, - and *; division needs an inverse.4MOD = 1_000_000_0075 6 71 · Addition and subtraction: one conditional each, no % needed8def add_mod(a: int, b: int, mod: int = MOD) -> int:9 s = a + b10 return s - mod if s >= mod else s11 12 13def sub_mod(a: int, b: int, mod: int = MOD) -> int:14 d = a - b15 return d + mod if d < 0 else d16 17 182 · Multiplication: no overflow to worry about, integers are unbounded19def mul_mod(a: int, b: int, mod: int = MOD) -> int:20 return a * b % mod21 22 233 · Normalising is a no-op in Python: % already follows the divisor's sign24def norm(a: int, mod: int = MOD) -> int:25 return a % mod # already in [0, mod) for a positive mod, even if a < 026 27 284 · Running products stay bounded: reduce at every step, never at the end29def factorial_mod(n: int, mod: int = MOD) -> int:30 acc = 1 % mod31 for i in range(2, n + 1):32 acc = acc * i % mod33 return acc34 35 365 · Precomputed tables turn repeated queries into O(1) lookups37def factorial_table(n: int, mod: int = MOD) -> list[int]:38 f = [1 % mod] * (n + 1)39 for i in range(1, n + 1):40 f[i] = f[i - 1] * i % mod41 return f- Python integers are unbounded, so
a * b % modis always exact — there is no overflow question to answer, andmul_modis a one-liner. normis effectively a no-op: Python%returns a result with the sign of the *divisor*, so-1 % 7is already6.factorial_modstill reduces every step. Not for correctness here, but for speed: an unreduced factorial of 10^5 has roughly half a million digits, and each further multiplication scales with that width.1_000_000_007uses numeric underscores, which are ignored by the parser and make the constant readable.f = [1 % mod] * (n + 1)is safe because integers are immutable; the same idiom with a list element would alias.
Reducing at every step is a genuine asymptotic improvement in Python, not a micro-optimisation — unreduced products grow without bound and multiplication cost grows with digit count.
%follows the sign of the divisor, so a positive modulus always yields a non-negative residue — the one language here where negative normalisation is unnecessary.divmod(a, b)returns quotient and remainder in one call and is faster than computing both separately.math.factorial(n) % modis exact but computes the full unreduced factorial first, which is enormous for large n.pow(base, exp, mod)is the built-in modular exponentiation and is the right primitive for modular division.
- Using
math.factorial(n) % modfor large n, which allocates a number with hundreds of thousands of digits before reducing. - Adding a C-style negative-modulo guard that is redundant and misleads readers into thinking Python behaves like C.
- Assuming the arbitrary precision makes reduction optional — it makes it *safe* to skip, not *cheap*.
- The sign of
%splits the four cleanly: Python follows the divisor (always non-negative for a positive modulus), while C++, JavaScript and TypeScript follow the dividend and need explicit normalisation. - Overflow: Python has none, JS/TS avoid it by using
BigInt(a compile error on mixing in TypeScript, a runtimeTypeErrorin JavaScript), and C++ needs the non-standard__int128. - Reducing at every step is required for correctness in C++ and for tractability in Python and BigInt JS/TS — the same discipline for two different reasons.
- Readable constants: C++14
1'000'000'007, Python and JS/TS1_000_000_007— same idea, different separator character.
Complexity
Per add/sub/mul. Division costs O(log m) via a modular inverse. Python big-int operations grow with digit count, which reduction keeps bounded.
Compare growth rates in the Complexity Explorer →When to use — and when not to
- Any counting or product answer that the problem asks for "mod
10^9 + 7" — reduce at every step, never at the end. - Hashing and rolling hashes, pseudo-random generators, cyclic index arithmetic.
- Divisibility counting via residues of prefix sums.
- When you need to compare, sort, or take the minimum of the true values — reduction destroys order. Compute with big integers or logarithms instead.
- When the problem never mentions a modulus and values fit: unnecessary
%operations are slow (integer division) and obscure the code. - Dividing by numbers that share a factor with the modulus — the inverse does not exist; restructure the computation (e.g. cancel factors before reducing).
Alternatives
Common mistakes
- Reducing only at the end — the intermediate overflows long before.
- Negative results from
(a - b) % min C++/Java/JS/Go; addmbefore reducing. - Multiplying two reduced 32-bit ints in a 32-bit type (
int * intin Java/C++) — cast to 64-bit first. - JavaScript:
a * b % mwitha, b ≈ 10^9silently loses precision above2^53; useBigIntor split multiplication. - Using
%to "divide" (x / y % m) — division must go through the modular inverse. - Choosing a composite modulus (
10^9) and then needing inverses that do not exist.
Interview patterns
- Count paths / subsequences / ways with DP mod
10^9 + 7. - Subarray sums divisible by
k; continuous subarray sum with prefix remainders. - Rolling hash with a large prime modulus and base — see Rabin–Karp.
- Modular combinatorics with precomputed factorials — see Combinatorics.