- Modular arithmetic: reduce after every + and *, fix subtraction with
((a - b) % m + m) % m, and never divide; multiply by an inverse instead. - GCD: Euclid in O(log n).
lcm = a / gcd(a, b) * b(divide first to avoid overflow). - Primes: trial division to √n for one number; Sieve of Eratosthenes in O(n log log n) for all numbers up to n; a smallest-prime-factor sieve factorises any number ≤ n in O(log n).
- Fast power: square-and-multiply in O(log e). Powers
Pow(x, n), modular inverses and matrix exponentiation all build on it. - nCr mod p: precompute factorials and inverse factorials once, then each query is O(1).
Math problems are the ones candidates skip in preparation and then lose easy points on in online assessments. The good news: the set of number theory that interviews actually use is small and stable. This guide covers that set from first principles, with C++ and Python implementations verified against the LeetCode examples, and shows which interview problems each tool unlocks.
What Math Is Actually Asked in Coding Interviews
| Topic | Where it shows up | Frequency in interviews |
|---|---|---|
| Modular arithmetic | Any "return the answer mod 109+7" counting or DP problem | Very common in OAs |
| GCD / LCM | Fractions, slopes, string periods, water jug, array GCD problems | Common |
| Primes and sieve | Count Primes, prime factorisation, ugly numbers | Common |
| Fast exponentiation | Pow(x, n), counting with huge exponents, inverses | Common |
| nCr and counting | Grid paths, distributing items, arrangements | Moderate |
| Catalan, inclusion-exclusion, pigeonhole | Unique BSTs, parentheses, "at least one" counting, duplicate detection | Occasional |
| CRT, Lucas, FFT, Möbius | Competitive programming, some quant firms | Rare in SDE rounds |
If you are aiming at Codeforces or CodeChef ratings as well, the heavier topics are covered in the competitive programming roadmap. For SDE interviews, master the first five rows.
Modular Arithmetic and Why 10^9 + 7
When a problem says "return the answer modulo 109 + 7", the real answer would overflow 64 bits. You keep every intermediate value reduced. The rules:
| Operation | Safe form | Why |
|---|---|---|
| Addition | (a + b) % m | Two residues below 1.0000001 × 109 sum below 231 - 1 |
| Subtraction | ((a - b) % m + m) % m | C++ and Java % keep the sign of the dividend, so -7 % 3 is -1 |
| Multiplication | (a * b) % m in 64-bit | Product of two residues is below 1.0000001 × 1018 < 9.22 × 1018 |
| Division | a * inverse(b) % m | Division does not distribute over mod |
| Power | Fast exponentiation, reducing each step | Exponent can be 1018 |
Why this specific number? 109 + 7 is prime, so every non-zero residue has an inverse (needed for nCr and division). It is just under 230, so sums fit in a signed 32-bit int and products fit in a signed 64-bit int. 998244353 is the other common modulus; it is prime too and has a special structure used by number-theoretic transforms.
int a, b; long long c = a * b % MOD; multiplies in 32-bit and overflows before the conversion. Store residues as long long/long, or write 1LL * a * b % MOD. Python has arbitrary-precision integers, so it never overflows, but reducing still matters for speed.GCD, LCM and the Extended Euclidean Algorithm
Euclid's algorithm: gcd(a, b) = gcd(b, a mod b), and gcd(a, 0) = a. It works because any common divisor of a and b also divides a - k·b, which includes a mod b. It takes O(log min(a, b)) steps; the worst case is consecutive Fibonacci numbers.
long long gcdLL(long long a, long long b) {
while (b != 0) {
long long t = a % b;
a = b;
b = t;
}
return a;
}
long long lcmLL(long long a, long long b) { return a / gcdLL(a, b) * b; } // divide first
// returns g = gcd(a, b) and sets x, y with a*x + b*y = g
long long extGcd(long long a, long long b, long long& x, long long& y) {
if (b == 0) { x = 1; y = 0; return a; }
long long x1, y1;
long long g = extGcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return g;
}
def gcd(a, b):
while b:
a, b = b, a % b
return a
def ext_gcd(a, b):
"""Return (g, x, y) with a*x + b*y == g == gcd(a, b)."""
if b == 0:
return a, 1, 0
g, x1, y1 = ext_gcd(b, a % b)
return g, y1, x1 - (a // b) * y1
Dry run of gcd(48, 18): (48, 18) → (18, 12) → (12, 6) → (6, 0) → 6. And ext_gcd(30, 12) returns (6, 1, -2): 30 × 1 + 12 × (-2) = 6. Built-ins exist in all three languages (std::gcd/std::lcm in C++17, math.gcd/math.lcm in Python 3.9+), but interviewers sometimes ask you to write Euclid yourself.
Where GCD appears:
- Greatest Common Divisor of Strings (LC 1071): if
s1 + s2 == s2 + s1, the answer is the prefix of lengthgcd(len1, len2), otherwise empty. - Max Points on a Line (LC 149): represent each slope as a reduced fraction
(dy/g, dx/g)with a normalised sign, avoiding floating point. - Water and Jug Problem (LC 365): target t is reachable iff
t <= x + yand t is a multiple ofgcd(x, y)(Bézout's identity). - Rotate Array via juggling: the rotation splits into
gcd(n, k)cycles.
Primes: Trial Division, the Sieve and Factorisation
Is one number prime?
Check divisors up to √n: if n = a × b with a ≤ b then a ≤ √n. O(√n) per number, fine for a single n up to about 1012 to 1014. Write the loop as p * p <= n to avoid floating-point sqrt errors.
All primes up to n: Sieve of Eratosthenes
Mark every number as prime, then for each prime i cross out its multiples starting from i * i (smaller multiples were already crossed out by smaller primes). Total work is n/2 + n/3 + n/5 + ... over primes, which is O(n log log n).
vector<bool> sieve(int n) {
vector<bool> isPrime(n + 1, true);
isPrime[0] = false;
if (n >= 1) isPrime[1] = false;
for (long long i = 2; i * i <= n; i++)
if (isPrime[i])
for (long long j = i * i; j <= n; j += i)
isPrime[j] = false;
return isPrime;
}
def count_primes(n):
"""LeetCode 204: number of primes strictly less than n."""
if n < 3:
return 0
is_prime = bytearray([1]) * n
is_prime[0] = is_prime[1] = 0
i = 2
while i * i < n:
if is_prime[i]:
is_prime[i * i::i] = bytearray(len(range(i * i, n, i)))
i += 1
return sum(is_prime)
The Python version uses slice assignment so the inner loop runs in C; a plain Python inner loop is several times slower and can time out at n = 5 × 106. Our run returns 4 for n = 10 and 348,513 for n = 5 × 106, the known prime count.
Factorising many numbers: smallest prime factor sieve
Store spf[x], the smallest prime dividing x. Then factorise any x ≤ n by repeatedly dividing by spf[x], which takes O(log x) steps because each division at least halves x.
vector<int> smallestPrimeFactor(int n) {
vector<int> spf(n + 1);
iota(spf.begin(), spf.end(), 0);
for (long long i = 2; i * i <= n; i++)
if (spf[i] == i) // i is prime
for (long long j = i * i; j <= n; j += i)
if (spf[j] == j) spf[j] = i;
return spf;
}
// factorise 84: 2, 2, 3, 7
// while (x > 1) { int p = spf[x]; x /= p; }
For a single large number, trial division with exponent counting is enough: 360 = 2^3 · 3^2 · 5. Factorisation gives you the number of divisors (product of (e + 1)), the sum of divisors, and Euler's totient if a problem ever needs it.
Fast Exponentiation (Binary Exponentiation)
To compute be, write e in binary. b13 = b8 · b4 · b1 since 13 = 11012. Repeatedly square the base and multiply it into the result when the current bit is set. O(log e) multiplications instead of e.
long long power(long long base, long long exp, long long mod) {
long long result = 1 % mod; // handles mod == 1
base %= mod;
if (base < 0) base += mod;
while (exp > 0) {
if (exp & 1) result = result * base % mod;
base = base * base % mod;
exp >>= 1;
}
return result;
}
def power(base, exp, mod):
result = 1 % mod
base %= mod
while exp > 0:
if exp & 1:
result = result * base % mod
base = base * base % mod
exp >>= 1
return result
# Python's built-in pow(base, exp, mod) does the same, and is what you should use.
Dry run of 313: bits of 13 from the right are 1, 0, 1, 1. Result picks up 3, skips 9, picks up 81, picks up 6561: 3 × 81 × 6561 = 1,594,323 = 313. Four squarings instead of twelve multiplications.
LeetCode 50: Pow(x, n)
The same loop on doubles, with two traps: negative n (use 1/x and -n), and n = INT_MIN, where -n overflows an int. Copy n into a 64-bit variable first.
double myPow(double x, int n) {
long long e = n; // -INT_MIN overflows int, not long long
if (e < 0) { x = 1 / x; e = -e; }
double result = 1;
while (e > 0) {
if (e & 1) result *= x;
x *= x;
e >>= 1;
}
return result;
}
Count Good Numbers (LC 1922) is fast power in disguise: even indices have 5 choices (even digits), odd indices have 4 (prime digits), so the answer is 5^ceil(n/2) · 4^floor(n/2) mod p with n up to 1015. For n = 4 this gives 25 × 16 = 400. The same squaring idea on 2×2 matrices computes the n-th Fibonacci number in O(log n), a favourite follow-up.
Modular Inverse: Fermat and Extended Euclid
The inverse of a modulo m is the x with a · x ≡ 1 (mod m). It exists iff gcd(a, m) = 1.
- Prime modulus (the usual case): Fermat's little theorem says ap-1 ≡ 1 (mod p), so ap-2 is the inverse. One fast power, O(log p). For p = 109+7, the inverse of 3 is 333,333,336 (check: 3 × 333,333,336 = 1,000,000,008 ≡ 1).
- Any modulus: extended Euclid gives x and y with
a·x + m·y = 1, so x mod m is the inverse. Inverse of 3 mod 10 is 7; 4 has no inverse mod 10 because gcd(4, 10) = 2.
long long modInverse(long long a, long long p) { // p prime, a % p != 0
return power(a, p - 2, p);
}
long long modInverseExt(long long a, long long m) { // any m
long long x, y;
long long g = extGcd(a, m, x, y);
if (g != 1) return -1; // no inverse
return ((x % m) + m) % m;
}
In Python 3.8+, pow(a, -1, m) computes the modular inverse directly and raises ValueError if none exists.
nCr mod p: Factorials and Inverse Factorials
C(n, r) = n! / (r! (n-r)!). Under a prime modulus, replace the division with inverses. Computing each inverse separately costs O(log p) per query; precomputing inverse factorials makes every query O(1).
const long long MOD = 1'000'000'007LL;
struct Combinatorics {
vector<long long> fact, invFact;
Combinatorics(int n) : fact(n + 1), invFact(n + 1) {
fact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % MOD;
invFact[n] = power(fact[n], MOD - 2, MOD); // one inverse
for (int i = n; i > 0; i--) invFact[i - 1] = invFact[i] * i % MOD;
}
long long nCr(int n, int r) const {
if (r < 0 || r > n) return 0;
return fact[n] * invFact[r] % MOD * invFact[n - r] % MOD;
}
};
MOD = 10**9 + 7
class Combinatorics:
def __init__(self, n, mod=MOD):
self.mod = mod
self.fact = [1] * (n + 1)
for i in range(1, n + 1):
self.fact[i] = self.fact[i - 1] * i % mod
self.inv_fact = [1] * (n + 1)
self.inv_fact[n] = pow(self.fact[n], mod - 2, mod)
for i in range(n, 0, -1):
self.inv_fact[i - 1] = self.inv_fact[i] * i % mod
def ncr(self, n, r):
if r < 0 or r > n:
return 0
return self.fact[n] * self.inv_fact[r] % self.mod * self.inv_fact[n - r] % self.mod
Why the downward fill works: 1/(i-1)! = i / i!, so invFact[i-1] = invFact[i] · i. Setup is O(n + log p). Our implementation returns 10 for C(5, 2) and 149,033,233 for C(100000, 50000) mod 109+7, matching Python's exact math.comb reduced mod p.
| Situation | Method | Cost |
|---|---|---|
| n ≤ ~60, no modulus | Multiplicative formula with 64-bit, divide as you go | O(r) |
| n ≤ ~5000, any modulus | Pascal's triangle C[n][r] = C[n-1][r-1] + C[n-1][r] | O(n²) |
| n ≤ ~106, prime modulus | Factorials + inverse factorials | O(n) setup, O(1) query |
| n larger than p, prime p | Lucas' theorem | O(p + logp n) |
| Python, exact value | math.comb(n, r) | Big integers |
Combinatorics Patterns That Recur
Grid paths
Unique Paths (LC 62): any path from top-left to bottom-right of an m × n grid is a sequence of (m-1) downs and (n-1) rights, so the answer is C(m+n-2, m-1). For 3 × 7 that is C(8, 2) = 28. The DP solution is expected first; mentioning the closed form is a strong signal. With obstacles, the closed form no longer applies and you go back to DP.
Catalan numbers
Cn = C(2n, n) / (n + 1): 1, 1, 2, 5, 14, 42, ... They count structurally unique BSTs with n nodes (Unique Binary Search Trees, LC 96), valid parentheses strings with n pairs (the count behind Generate Parentheses), ways to triangulate a polygon and mountain ranges. Recognising "this recurrence is sum dp[i] · dp[n-1-i]" as Catalan lets you answer instantly.
Stars and bars
The number of ways to put n identical items into k distinct boxes is C(n + k - 1, k - 1); if every box needs at least one, C(n - 1, k - 1). It appears in "count non-negative integer solutions" problems and in distributing candies variants.
Inclusion-exclusion
"Count numbers up to n divisible by a or b" is n/a + n/b - n/lcm(a,b). Combined with binary search on the answer, this solves Nth Magical Number (LC 878) and Ugly Number III (LC 1201). Generally: add singles, subtract pairs, add triples.
Pigeonhole
n + 1 items in n boxes force a collision. It is the reason Find the Duplicate Number (LC 287) has a duplicate at all, and why "subarray sum divisible by k" problems always have an answer when the array is longer than k (two prefix sums share a remainder).
Classic Math Interview Problems and the Tool Each Needs
| Problem | Key idea | Complexity |
|---|---|---|
| LC 204 Count Primes | Sieve | O(n log log n) |
| LC 50 Pow(x, n) | Binary exponentiation, INT_MIN care | O(log n) |
| LC 172 Factorial Trailing Zeroes | Count factors of 5: n/5 + n/25 + ... | O(log n) |
| LC 69 Sqrt(x) | Binary search with mid <= x / mid to avoid overflow | O(log x) |
| LC 1071 GCD of Strings | Concatenation check + gcd of lengths | O(n + m) |
| LC 149 Max Points on a Line | Slopes as reduced fractions via gcd | O(n²) |
| LC 62 Unique Paths | C(m+n-2, m-1) or DP | O(min(m, n)) |
| LC 96 Unique BSTs | Catalan | O(n²) DP or O(n) |
| LC 1922 Count Good Numbers | Modular fast power | O(log n) |
| LC 263/264 Ugly Number I/II | Divide out 2, 3, 5; three-pointer merge | O(log n) / O(n) |
| LC 365 Water and Jug | Bézout: multiple of gcd | O(log) |
| LC 878 Nth Magical Number | Inclusion-exclusion + binary search | O(log(n · min(a,b))) |
| LC 166 Fraction to Recurring Decimal | Long division, hash map of remainders | O(denominator) |
Factorial Trailing Zeroes deserves a sentence: zeros come from factors of 10 = 2 × 5, and factors of 2 are always more plentiful, so count factors of 5. Numbers like 25 contribute two, which is why you add n/25, n/125 and so on. For n = 100 the answer is 20 + 4 = 24.
How Math Shows Up in Interviews and OAs
Pure math questions are more common in online assessments (HackerRank, HackerEarth, Codility-style tests used by many Indian product companies and banks) than in onsite rounds. In live interviews, math usually appears as a component: a DP whose answer is taken mod 109+7, a binary search whose bounds need overflow care, a hash key that must be a reduced fraction.
What interviewers probe:
- Overflow awareness. "What happens if n is 109?" Know your type limits: 231 - 1 ≈ 2.1 × 109, 263 - 1 ≈ 9.2 × 1018.
- Proof of complexity. Why is Euclid logarithmic? Why does the sieve start at i²?
- Exactness. Using
doublefor slopes or square roots is a red flag when an integer method exists. - Closed form vs DP. For Unique Paths or Unique BSTs, offering both and explaining the trade-off (closed form is faster but breaks with obstacles or constraints) shows depth.
For quant-leaning firms (DE Shaw, Goldman Sachs, Tower-style shops), expect more probability and combinatorics; the company guides such as DE Shaw interview preparation list what is commonly reported.
Common Math Mistakes in Coding Rounds
- Reducing only at the end. Overflow happens in the middle. Reduce after every multiplication.
- Negative remainders.
(a - b) % MODcan be negative in C++/Java. Add MOD before the final %. - Dividing under a modulus. Use the inverse. And remember the inverse only exists when gcd(b, m) = 1.
lcm = a * b / gcd. The product overflows first; usea / gcd * b.- Floating-point sqrt in loops.
for (i = 2; i <= sqrt(n); i++)can miss by one due to rounding; usei * i <= nwith 64-bit i. - Sieve inner loop from 2·i. Correct but slower; from i·i is the standard. And
i * iin int overflows for i above 46,340. - Forgetting 0 and 1 in primality checks and sieves.
- Pow with INT_MIN. Negating overflows.
- Python slowness. Pure-Python loops over 107 elements time out; use slice assignment, built-in
powandmath.comb.
Frequently Asked Questions
Leave No Topic Uncovered
Prepflix's DSA program covers interview math alongside every core pattern, with live sessions and OA-style practice sets so modulo, sieve and nCr problems become free points.
Explore the Prepflix Course →