Every code block has a Python and a C++ version; use the switch above a block to pick one. Your choice is remembered.
Introduction (Number theory)
This page teaches the number tricks that keep showing up in coding interviews: primes, divisors, remainders, powers, and counting with huge numbers. It assumes only that you can write a loop and a simple function. You do not need to know any advanced mathematics.
The page is a staircase. Chapter 1 is deliberately gentle: what a factor is, what a prime is and what a remainder is, with small examples. Each later chapter adds one new tool, and the mathematics gets a little heavier. By the last chapters you will be able to test whether a huge number is prime and combine remainders from different moduli. If a later chapter looks hard, do not worry; every chapter only uses what came before.
Every code example is shown in Python and in C++, so pick whichever language you like.
Chapter 1. Numbers, factors and primes
1.1 Factors and multiples
A factor (or divisor) of a number is a whole number that divides it with nothing left over. The factors of 12 are 1, 2, 3, 4, 6 and 12, because . A multiple of a number is what you get when you multiply it by 1, 2, 3 and so on, so the multiples of 4 are 4, 8, 12, 16, and so on.
To check whether is a factor of we look at the remainder of the division. In Python and C++ the remainder is written n % d. For example 12 % 5 is 2, because 5 goes into 12 twice and 2 is left. A number divides exactly when n % d == 0.
Listing all factors of the slow way means trying every number from 1 to . That works for small numbers, but there is a neat shortcut. Factors come in pairs: if is a factor, then so is . In the pair for 12, one of the two is always at most , so we only need to try numbers up to and record both members of each pair.
def divisors(n):
small, large = [], []
d = 1
while d * d <= n:
if n % d == 0:
small.append(d)
if d != n // d:
large.append(n // d)
d += 1
return small + large[::-1]
assert divisors(12) == [1, 2, 3, 4, 6, 12]
assert divisors(36) == [1, 2, 3, 4, 6, 9, 12, 18, 36]
assert divisors(1) == [1]
vector<long long> divisors(long long n) {
vector<long long> small, large;
for (long long d = 1; d * d <= n; d++) {
if (n % d == 0) {
small.push_back(d);
if (d != n / d) large.push_back(n / d);
}
}
reverse(large.begin(), large.end());
small.insert(small.end(), large.begin(), large.end());
return small;
}
The loop runs about times, so the cost is . For that is about a million steps, which is fast. The check d != n // d handles perfect squares such as 36, where the pair must be counted only once.
1.2 Prime numbers
A prime number is a number greater than 1 whose only factors are 1 and itself. The first primes are 2, 3, 5, 7, 11, 13. A number greater than 1 that is not prime is called composite; it can be split into smaller factors, like . The number 1 is neither prime nor composite.
Primes are the building blocks of all numbers. This is the fundamental theorem of arithmetic: every whole number greater than 1 can be written as a product of primes in exactly one way, apart from the order. For instance . We will use this fact again and again.
To test whether is prime, we can use the same square-root shortcut. If had a factor larger than , it would also have a matching factor smaller than . So if no number from 2 up to divides , then is prime.
def is_prime_slow(n):
if n < 2:
return False
d = 2
while d * d <= n:
if n % d == 0:
return False
d += 1
return True
assert [p for p in range(20) if is_prime_slow(p)] == [2, 3, 5, 7, 11, 13, 17, 19]
assert is_prime_slow(1_000_000_007)
bool is_prime_slow(long long n) {
if (n < 2) return false;
for (long long d = 2; d * d <= n; d++)
if (n % d == 0) return false;
return true;
}
This costs time and constant space. It is perfect when is up to about and you test only a few numbers. Later chapters show faster ways for many numbers or for enormous ones.
1.3 What the remainder tells us
The remainder operation is more powerful than it looks. If you look at a clock, after 12 comes 1 again; the hour is the remainder of the total hours when divided by 12. This idea of "wrapping around" is called modular arithmetic. We write for the remainder of divided by , and it is always between 0 and .
Two numbers and are called congruent modulo if they have the same remainder, which we write . For example , because both leave remainder 5.
The most useful property is that adding, subtracting and multiplying can be done on the remainders. If you want the last digit of , you only need . Formally:
This lets us keep numbers small while computing with huge ones, which is the heart of the later chapters. One warning: in Python the result of % with a positive modulus is never negative, but in C++ (-7) % 5 is . When subtracting in C++, write ((a - b) % m + m) % m.
def add_mod(a, b, m):
return (a % m + b % m) % m
def sub_mod(a, b, m):
return ((a - b) % m + m) % m
def mul_mod(a, b, m):
return (a % m) * (b % m) % m
assert add_mod(10, 5, 12) == 3
assert sub_mod(3, 10, 12) == 5
assert mul_mod(4237, 8951, 10) == 7
long long add_mod(long long a, long long b, long long m) {
return (a % m + b % m) % m;
}
long long sub_mod(long long a, long long b, long long m) {
return ((a - b) % m + m) % m;
}
long long mul_mod(long long a, long long b, long long m) {
return (a % m) * (b % m) % m;
}
1.4 Check yourself
- Why is it enough to test divisors up to when listing the factors of ?
- Is 1 a prime number? Is 91 a prime number? Give a reason in one sentence each.
- What is , computed using only the last digits?
- Why does
(-7) % 5need care in C++ but not in Python?
Chapter 2. Sieve, factorisation and counting divisors
2.1 Counting primes with the sieve
Testing every number below with trial division would be slow. A much faster idea was found by the Greek mathematician Eratosthenes more than two thousand years ago. Write down all the numbers from 2 to . Take the first number that is not crossed out, 2, and cross out all of its multiples (4, 6, 8, and so on). The next number not crossed out, 3, must be prime; cross out its multiples. Repeat. Every number that is never crossed out is prime. This is called the sieve of Eratosthenes, and it is exactly the idea of the problem Count Primes.
Here is the sieve for numbers below 30. After crossing out the multiples of 2, 3 and 5, only the primes remain: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29. We can stop crossing out once the prime satisfies , because any composite number below already has a factor at most . And for a prime we may begin crossing out at , since smaller multiples such as and were removed earlier.
def count_primes(n):
if n < 3:
return 0
is_prime = [True] * n
is_prime[0] = is_prime[1] = False
p = 2
while p * p < n:
if is_prime[p]:
for multiple in range(p * p, n, p):
is_prime[multiple] = False
p += 1
return sum(is_prime)
assert count_primes(30) == 10
assert count_primes(10) == 4
assert count_primes(2) == 0
assert count_primes(100) == 25
int count_primes(int n) {
if (n < 3) return 0;
vector<bool> is_prime(n, true);
is_prime[0] = is_prime[1] = false;
for (long long p = 2; p * p < n; p++) {
if (is_prime[p]) {
for (long long m = p * p; m < n; m += p) is_prime[m] = false;
}
}
return count(is_prime.begin(), is_prime.end(), true);
}
The sieve takes about time, which is almost linear, and memory. For it runs in well under a second in C++.
2.2 Writing a number as a product of primes
By the fundamental theorem of arithmetic, every equals a product of primes in exactly one way. We write it as , where each is a prime and each says how many times it appears. For example . This is the problem Prime Factorization.
To find the factors we do trial division, but with a trick: divide out each factor completely as soon as we find it. We start with . While divides , we divide and count. Then we move on to , and so on. Because we divide out the small primes first, whenever divides the current , is guaranteed to be prime. We can stop when , because what is left over is either 1 or a single prime.
def prime_factors(n):
result = []
d = 2
while d * d <= n:
if n % d == 0:
exponent = 0
while n % d == 0:
n //= d
exponent += 1
result.append([d, exponent])
d += 1
if n > 1:
result.append([n, 1])
return result
assert prime_factors(360) == [[2, 3], [3, 2], [5, 1]]
assert prime_factors(97) == [[97, 1]]
assert prime_factors(1) == []
assert prime_factors(1001) == [[7, 1], [11, 1], [13, 1]]
vector<pair<long long, int>> prime_factors(long long n) {
vector<pair<long long, int>> result;
for (long long d = 2; d * d <= n; d++) {
if (n % d == 0) {
int exponent = 0;
while (n % d == 0) {
n /= d;
exponent++;
}
result.push_back({d, exponent});
}
}
if (n > 1) result.push_back({n, 1});
return result;
}
The time is , because never needs to go above . After the loop, if , the remaining value is a prime larger than every we tried, so we add it with exponent 1.
2.3 Counting the divisors from the factors
Once we know , counting the divisors is easy. A divisor picks, for each prime , how many copies to use: anywhere from 0 up to , which is choices. Choices for different primes are independent, so we multiply:
For we get divisors. This is the problem Count Divisors. Likewise the sum of all divisors is , because each prime contributes .
def count_divisors(n):
total = 1
for _, exponent in prime_factors(n):
total *= exponent + 1
return total
def sum_divisors(n):
total = 1
for p, exponent in prime_factors(n):
total *= (p ** (exponent + 1) - 1) // (p - 1)
return total
assert count_divisors(360) == 24
assert count_divisors(1) == 1
assert count_divisors(97) == 2
assert sum_divisors(12) == 28
long long count_divisors(long long n) {
long long total = 1;
for (auto [p, exponent] : prime_factors(n)) total *= exponent + 1;
return total;
}
long long sum_divisors(long long n) {
long long total = 1;
for (auto [p, exponent] : prime_factors(n)) {
long long term = 1, power = 1;
for (int i = 0; i < exponent; i++) {
power *= p;
term += power;
}
total *= term;
}
return total;
}
2.4 Check yourself
- In the sieve, why can the inner loop start at instead of ?
- What are the prime factors of 1001, and how many divisors does it have?
- After the factorisation loop finishes with , why is the leftover a prime?
- How many divisors does have, and how many does have?
Chapter 3. The GCD and the Euclidean algorithm
3.1 Greatest common divisor
The greatest common divisor of two numbers, written , is the largest number that divides both. For 12 and 18 the common divisors are 1, 2, 3 and 6, so . Two numbers whose gcd is 1 are called coprime: they share no prime factor. For instance 8 and 15 are coprime.
We could find the gcd by comparing the prime factorisations, but there is a far faster method, found by Euclid. It rests on one observation: any number that divides both and also divides , and also divides the remainder of divided by . So . We repeat this until the second number becomes 0, and then the answer is the first number, because .
Let us trace . We have , so we continue with . Then , so we continue with . Then , so we continue with , and the answer is 21.
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
def lcm(a, b):
return a // gcd(a, b) * b
assert gcd(252, 105) == 21
assert gcd(8, 15) == 1
assert gcd(0, 7) == 7
assert lcm(4, 6) == 12
long long gcd_euclid(long long a, long long b) {
while (b != 0) {
long long t = a % b;
a = b;
b = t;
}
return a;
}
long long lcm_euclid(long long a, long long b) {
return a / gcd_euclid(a, b) * b;
}
Each step makes the numbers much smaller (the remainder is less than half of the larger number after two steps), so the loop runs only times. The least common multiple of and is the smallest number that both divide, and it satisfies . We divide before multiplying in lcm to keep the numbers small.
3.2 Extended Euclid: finding the coefficients
There is a beautiful fact called Bézout's identity: for any two numbers and there are whole numbers and (they may be negative) such that
For example, with and the gcd is 21, and . The extended Euclidean algorithm finds and together with the gcd by remembering the steps of Euclid's algorithm. The idea is recursive. Suppose we already know and with . Since , we can rewrite this as . So and .
def ext_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
g, x, y = ext_gcd(252, 105)
assert g == 21 and 252 * x + 105 * y == 21
g, x, y = ext_gcd(3, 7)
assert g == 1 and 3 * x + 7 * y == 1
long long ext_gcd(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 = ext_gcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return g;
}
The extended algorithm has the same cost. We need it in Chapter 5 to compute modular inverses and in Chapter 7 to combine remainder conditions.
3.3 Check yourself
- Why is ?
- Compute and by hand.
- If , what does Bézout's identity say about and ?
- Why does
lcmdivide by the gcd first and then multiply?
Chapter 4. Powers with remainders
4.1 Why we need fast powers
Suppose we want . The number has almost half a million digits, so we cannot write it down. We could multiply by 3 one million times and take the remainder each time (we may, by the rule from Chapter 1), but a million steps is wasteful, and for exponents like it is impossible.
The trick is fast exponentiation, also called binary exponentiation or square and multiply. The idea is that squaring doubles the exponent in one step. We know and . So to compute we compute once, square it, and multiply by one extra if is odd. The exponent halves each time, so we need only about steps.
Let us trace . Since in binary, we have . We compute the squares , , , and multiply together those whose bit in 13 is 1: .
def power_mod(a, n, m):
result = 1 % m
a %= m
while n > 0:
if n & 1:
result = result * a % m
a = a * a % m
n >>= 1
return result
assert power_mod(3, 13, 10**9) == 1594323
assert power_mod(2, 10, 1000) == 24
assert power_mod(5, 0, 7) == 1
assert power_mod(3, 10**18, 1000) == pow(3, 10**18, 1000)
long long power_mod(long long a, long long n, long long m) {
long long result = 1 % m;
a %= m;
while (n > 0) {
if (n & 1) result = result * a % m;
a = a * a % m;
n >>= 1;
}
return result;
}
The loop runs times. In C++, the products stay below , so they fit in a 64-bit integer when is below about . In Python there is also the built-in pow(a, n, m), which does the same thing.
4.2 Powers with a huge exponent given as digits
Sometimes the exponent itself is too large to store, and we get it as a list of its decimal digits. This is the problem Super Pow: compute , where is given as a list of digits, such as [1, 0] for 10.
We can use the rule . So we process the digits from left to right, keeping the value of . When a new digit arrives, the old exponent becomes , so the new answer is . Each of these small powers is computed with power_mod, so no huge number ever appears.
def super_pow(a, digits, m=1337):
result = 1
for d in digits:
result = power_mod(result, 10, m) * power_mod(a, d, m) % m
return result
assert super_pow(2, [1, 0]) == pow(2, 10, 1337)
assert super_pow(2, [3]) == 8
assert super_pow(2147483647, [2, 0, 0]) == pow(2147483647, 200, 1337)
long long super_pow(long long a, const vector<int>& digits, long long m = 1337) {
long long result = 1;
for (int d : digits)
result = power_mod(result, 10, m) * power_mod(a, d, m) % m;
return result;
}
For a list of digits this takes steps, each costing a constant number of small fast powers.
4.3 Check yourself
- How many multiplications does fast exponentiation need for , roughly?
- Why do we write
result = 1 % mand not just1? - Why can we keep taking remainders in the middle of the computation?
- In Super Pow, why is the old answer raised to the power 10 when a new digit arrives?
Chapter 5. Modular inverses and Euler's totient function
5.1 Dividing in a world of remainders
We can add, subtract and multiply remainders freely, but division is trickier. If we know , we cannot just divide by and take the remainder, because the result of a division is usually not a whole number. Instead of dividing by , we multiply by the modular inverse of .
The modular inverse of modulo is a number with such that . For example, modulo 7 the inverse of 3 is 5, because . Multiplying by 5 modulo 7 acts exactly like dividing by 3. This is the problem Modular Inverse.
An inverse exists exactly when . If and share a factor, every multiple of shares that factor with and can never leave remainder 1. When the gcd is 1, Bézout's identity from Chapter 3 gives numbers and with . Looking at this equation modulo , the term disappears, so , and is the inverse. We only need to bring into the range 0 to .
def mod_inverse(a, m):
g, x, _ = ext_gcd(a % m, m)
if g != 1:
return -1
return x % m
def ext_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
assert mod_inverse(3, 7) == 5
assert mod_inverse(2, 4) == -1
assert mod_inverse(10, 17) * 10 % 17 == 1
assert mod_inverse(5, 1) == 0
long long mod_inverse(long long a, long long m) {
long long x, y;
long long g = ext_gcd(((a % m) + m) % m, m, x, y);
if (g != 1) return -1;
return ((x % m) + m) % m;
}
(The helper ext_gcd is the one from Chapter 3. The Python version above defines it again so that this block runs on its own.) The cost is . For every number is congruent to 0, and the answer is 0, which the code handles because .
5.2 Euler's totient function
How many numbers between 1 and are coprime to ? The count is called Euler's totient function and is written . For the numbers coprime to 12 are 1, 5, 7, 11, so . This is the problem Euler Totient.
We could test every number with gcd, but there is a formula that uses only the prime factors of . Take the numbers from 1 to and remove those that share a prime factor with : a fraction of all numbers are multiples of , so a fraction survives. Doing this for every distinct prime factor gives
For we get . To avoid fractions, we compute it by starting with and, for each prime of , replacing the value by value - value // p.
def euler_phi(n):
result = n
d = 2
while d * d <= n:
if n % d == 0:
while n % d == 0:
n //= d
result -= result // d
d += 1
if n > 1:
result -= result // n
return result
assert euler_phi(12) == 4
assert euler_phi(1) == 1
assert euler_phi(13) == 12
assert euler_phi(36) == 12
long long euler_phi(long long n) {
long long result = n;
for (long long d = 2; d * d <= n; d++) {
if (n % d == 0) {
while (n % d == 0) n /= d;
result -= result / d;
}
}
if (n > 1) result -= result / n;
return result;
}
This is the same loop as prime factorisation, so it costs .
5.3 Fermat's little theorem: a shortcut for prime moduli
If is a prime and is not a multiple of , then
This is Fermat's little theorem. For example , so . Multiplying both sides by gives . So when the modulus is prime, the inverse of is simply , which we can compute with fast exponentiation. This is the usual way to divide modulo , which is a prime.
Euler generalised the theorem to any modulus: if then .
def inverse_prime(a, p):
return pow(a, p - 2, p)
P = 10**9 + 7
assert inverse_prime(3, 7) == 5
assert 12345 * inverse_prime(12345, P) % P == 1
assert inverse_prime(2, P) == (P + 1) // 2
long long inverse_prime(long long a, long long p) {
return power_mod(a, p - 2, p);
}
5.4 Check yourself
- For which does have an inverse modulo 12?
- What is ? What is for a prime ?
- Why does Fermat's theorem give the inverse ?
- Why can we not use Fermat's trick when the modulus is 12?
Chapter 6. Counting under a modulus
6.1 Factorials and binomial coefficients
Counting problems often ask for a huge number "modulo ". A very common one is the binomial coefficient , the number of ways to choose items out of . It equals
The division is the problem: is gigantic, and we only keep remainders. Because is prime, we can divide by multiplying with the modular inverse from Chapter 5. We precompute all factorials modulo , then the inverse of each factorial, and every becomes three multiplications. This is the problem Binomial Coefficient Modulo.
A neat way to get all inverse factorials cheaply is to compute only the last one, , with Fermat's trick, and then walk backwards using .
MOD = 10**9 + 7
def make_factorials(n):
fact = [1] * (n + 1)
for i in range(1, n + 1):
fact[i] = fact[i - 1] * i % MOD
inv = [1] * (n + 1)
inv[n] = pow(fact[n], MOD - 2, MOD)
for i in range(n, 0, -1):
inv[i - 1] = inv[i] * i % MOD
return fact, inv
def binomial_mod(n, k, fact, inv):
if k < 0 or k > n:
return 0
return fact[n] * inv[k] % MOD * inv[n - k] % MOD
fact, inv = make_factorials(1000)
assert binomial_mod(5, 2, fact, inv) == 10
assert binomial_mod(10, 0, fact, inv) == 1
assert binomial_mod(5, 7, fact, inv) == 0
assert binomial_mod(1000, 500, fact, inv) == 159835829
const long long MOD = 1000000007;
pair<vector<long long>, vector<long long>> make_factorials(int n) {
vector<long long> fact(n + 1, 1), inv(n + 1, 1);
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % MOD;
inv[n] = power_mod(fact[n], MOD - 2, MOD);
for (int i = n; i > 0; i--) inv[i - 1] = inv[i] * i % MOD;
return {fact, inv};
}
long long binomial_mod(int n, int k, const vector<long long>& fact, const vector<long long>& inv) {
if (k < 0 || k > n) return 0;
return fact[n] * inv[k] % MOD * inv[n - k] % MOD;
}
Precomputing costs , and each query then costs . If you need only one value for a huge and a small , you can use the product with a single inverse.
6.2 Zeros at the end of a factorial
How many zeros does end with? A zero at the end comes from a factor of 10, and . In the number 2 appears far more often than 5, so the number of trailing zeros equals the number of times 5 divides . This is the problem Trailing Zeros of Factorial.
Counting how many times 5 divides works like this. Among the numbers there are multiples of 5, and each contributes one factor 5. But multiples of 25 contribute a second one, and there are of them. Multiples of 125 contribute a third, and so on:
For this is zeros. The same idea, called Legendre's formula, counts how often any prime divides .
def trailing_zeros(n):
count = 0
power = 5
while power <= n:
count += n // power
power *= 5
return count
assert trailing_zeros(5) == 1
assert trailing_zeros(100) == 24
assert trailing_zeros(0) == 0
assert trailing_zeros(25) == 6
long long trailing_zeros(long long n) {
long long count = 0;
for (long long power = 5; power <= n; power *= 5) count += n / power;
return count;
}
The loop runs times, so it is .
6.3 Huge Fibonacci numbers
The Fibonacci numbers are , and . For up to we cannot loop times. This is the problem Fibonacci Modulo. Here is a fast method called fast doubling. From the Fibonacci recurrence one can show
These formulas let us jump from to in one step, and then move to by adding one more step if needed. As with fast exponentiation, the index halves each time, so we need only steps. For example, and .
MOD = 10**9 + 7
def fib_pair(n):
if n == 0:
return 0, 1
a, b = fib_pair(n // 2)
c = a * ((2 * b - a) % MOD) % MOD
d = (a * a + b * b) % MOD
if n % 2 == 0:
return c, d
return d, (c + d) % MOD
def fib_mod(n):
return fib_pair(n)[0]
assert [fib_mod(i) for i in range(10)] == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
assert fib_mod(50) == 12586269025 % MOD
assert fib_mod(10**18) == 209783453
pair<long long, long long> fib_pair(long long n) {
if (n == 0) return {0, 1};
auto [a, b] = fib_pair(n / 2);
long long c = a * ((2 * b - a + MOD) % MOD) % MOD;
long long d = (a * a + b * b) % MOD;
if (n % 2 == 0) return {c, d};
return {d, (c + d) % MOD};
}
long long fib_mod(long long n) {
return fib_pair(n).first;
}
The recursion goes about levels deep, and each level does a few multiplications, so the cost is .
6.4 Check yourself
- Why must the modulus be prime for the inverse-factorial method to work?
- How many zeros does end with?
- Why do trailing zeros of depend only on the factor 5, not on 2?
- Use fast doubling to find from and .
Chapter 7. Combining remainder conditions
7.1 The Chinese remainder idea
Here is an old puzzle. A number leaves remainder 2 when divided by 3, and remainder 3 when divided by 5. What could it be? We are looking for with and . Listing numbers that leave remainder 2 modulo 3 gives 2, 5, 8, 11, 14, 17, and so on; the first one that leaves remainder 3 modulo 5 is 8. The next solution is , because adding changes neither remainder. So the solutions are , all congruent to 8 modulo 15.
This is the Chinese remainder theorem, or CRT: when the moduli are coprime, a system of remainder conditions always has a solution, and it is unique modulo the product of the moduli. The problem Chinese Remainder Theorem asks for the smallest non-negative solution, and it allows moduli that are not coprime, in which case there may be no solution at all. For example and is impossible: the first says is odd and the second says is even.
7.2 Merging two conditions at a time
The easiest way to solve a whole list of conditions is to merge them two at a time. Suppose we already know and we add . Every solution of the first condition has the form for some whole number . Putting this into the second condition gives
Let . This equation has a solution only if divides ; otherwise the conditions contradict each other, like the odd and even example above. If it does divide, we can divide the whole equation by to get . Now and are coprime, so has an inverse modulo (Chapter 5), and we can solve for . The merged condition is modulo .
Starting from the empty condition and merging every condition in turn, we get the final answer. The result, in the range 0 to the final modulus minus 1, is the smallest non-negative .
def ext_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
def solve_congruences(remainders, moduli):
r, m = 0, 1
for r2, m2 in zip(remainders, moduli):
r2 %= m2
g, p, _ = ext_gcd(m, m2)
if (r2 - r) % g != 0:
return -1
step = m2 // g
t = ((r2 - r) // g) * p % step
r = r + m * t
m = m // g * m2
r %= m
return r
assert solve_congruences([2, 3], [3, 5]) == 8
assert solve_congruences([1, 2], [4, 6]) == -1
assert solve_congruences([2, 3, 2], [3, 5, 7]) == 23
assert solve_congruences([], []) == 0
assert solve_congruences([1, 3], [4, 6]) == 9
long long ext_gcd(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 = ext_gcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return g;
}
long long solve_congruences(const vector<long long>& remainders, const vector<long long>& moduli) {
long long r = 0, m = 1;
for (size_t i = 0; i < remainders.size(); i++) {
long long m2 = moduli[i];
long long r2 = ((remainders[i] % m2) + m2) % m2;
long long p, q;
long long g = ext_gcd(m, m2, p, q);
if ((r2 - r) % g != 0) return -1;
long long step = m2 / g;
long long t = (((r2 - r) / g) % step * (p % step)) % step;
if (t < 0) t += step;
r = r + m * t;
m = m / g * m2;
r %= m;
}
return r;
}
Each merge costs , so a list of conditions costs . A warning for C++: the numbers can grow up to the lcm of all moduli, so the products must fit in 64 bits; the contest versions of this problem promise that they do. In Python, integers never overflow.
7.3 Check yourself
- Find the smallest with and .
- Why do and have no solution?
- After merging two conditions, what is the new modulus?
- What happens in the merging step if the moduli are coprime?
Chapter 8. Testing huge numbers for primality
8.1 Why trial division is not enough
Trial division needs about steps. For a 18-digit number that is a billion steps, which is far too slow if we have many numbers to test. We need a test that takes time proportional to the number of digits, not to the number itself. The tool is the Miller–Rabin test, and it is the topic of the problem Miller Rabin Primality.
The starting point is Fermat's little theorem from Chapter 5: if is prime and is not a multiple of , then . We can run this as a test: pick , compute with fast exponentiation, and if the answer is not 1, then is certainly composite. Unfortunately some composite numbers, such as 561, pass the test for every (they are called Carmichael numbers). Miller and Rabin strengthened the test to remove this weakness.
8.2 The stronger test
Write with odd, by pulling out all factors of 2. For a prime , consider the sequence
Each term is the square of the previous one. The last term is 1 by Fermat. For a prime modulus, the only numbers whose square is 1 are 1 and (the only square roots of 1). So going backwards, the sequence must either start with , or contain somewhere before it reaches 1. If neither happens, is composite, and is called a witness of its compositeness.
One witness is enough to prove a number composite. And a handful of fixed bases is enough to be certain for every number below : it is known that testing the twelve bases gives the correct answer for all 64-bit numbers. So this version is not random at all; it is exact.
def is_prime(n):
if n < 2:
return False
small = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
for p in small:
if n % p == 0:
return n == p
d, s = n - 1, 0
while d % 2 == 0:
d //= 2
s += 1
for a in small:
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(s - 1):
x = x * x % n
if x == n - 1:
break
else:
return False
return True
assert [p for p in range(30) if is_prime(p)] == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
assert not is_prime(561)
assert not is_prime(3215031751)
assert is_prime(1_000_000_007)
assert is_prime(2**61 - 1)
assert not is_prime(2**61 + 1)
long long mul_mod_big(long long a, long long b, long long m) {
return (__int128)a * b % m;
}
long long power_mod_big(long long a, long long n, long long m) {
long long result = 1;
a %= m;
while (n > 0) {
if (n & 1) result = mul_mod_big(result, a, m);
a = mul_mod_big(a, a, m);
n >>= 1;
}
return result;
}
bool is_prime(long long n) {
if (n < 2) return false;
long long small[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
for (long long p : small) {
if (n % p == 0) return n == p;
}
long long d = n - 1;
int s = 0;
while (d % 2 == 0) {
d /= 2;
s++;
}
for (long long a : small) {
long long x = power_mod_big(a, d, n);
if (x == 1 || x == n - 1) continue;
bool composite = true;
for (int i = 0; i < s - 1; i++) {
x = mul_mod_big(x, x, n);
if (x == n - 1) {
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
Each base costs multiplications, and there are twelve bases, so one test takes time, a few microseconds even for 18-digit numbers. In C++ the product of two 64-bit numbers can overflow, so we multiply in 128 bits (__int128) before taking the remainder.
8.3 Check yourself
- Write for : what are and ?
- Why does one witness prove that a number is composite?
- Why is the number 561 a good example of why plain Fermat testing is not enough?
- Why do we use
__int128in the C++ version?
Chapter 9. Counting with digits
9.1 Counting the digit one
How many times does the digit 1 appear when we write all numbers from 1 to ? For the numbers 1, 10, 11, 12, 13 contain six 1s (11 contains two). This is the problem Count Digit One. Writing out every number takes too long for up to , so we count position by position instead.
Take one position at a time, say the tens position, of the numbers from 1 to . Split around that position into three parts: the digits above it (high), the digit at it (cur), and the digits below it (low). For and the tens position, high is 3, cur is 1 and low is 4. The tens digit cycles through 0 to 9 in blocks of length 100. Every complete block (the high part counts how many there are) has exactly 10 numbers with tens digit 1. So complete blocks contribute . In the last, incomplete block, the numbers with tens digit 1 are: none if cur is 0; all of them (that is, ) if cur is 1; or a full ten if cur is above 1.
For a position with value (1, 10, 100, and so on), the count is
Adding this up over all positions gives the answer, in time.
def count_digit_one(n):
total = 0
p = 1
while p <= n:
high = n // (p * 10)
cur = (n // p) % 10
low = n % p
total += high * p
if cur > 1:
total += p
elif cur == 1:
total += low + 1
p *= 10
return total
assert count_digit_one(13) == 6
assert count_digit_one(0) == 0
assert count_digit_one(100) == 21
assert count_digit_one(314) == sum(str(i).count("1") for i in range(1, 315))
long long count_digit_one(long long n) {
long long total = 0;
for (long long p = 1; p <= n; p *= 10) {
long long high = n / (p * 10);
long long cur = (n / p) % 10;
long long low = n % p;
total += high * p;
if (cur > 1) total += p;
else if (cur == 1) total += low + 1;
}
return total;
}
9.2 The nth digit of an endless string
Write all positive numbers one after another: 123456789101112... We want the -th digit. This is the problem Nth Digit. We first find which number the digit belongs to, then which digit of that number.
The 1-digit numbers 1 to 9 give 9 digits. The 2-digit numbers 10 to 99 give digits, the 3-digit numbers give , and in general the -digit numbers give digits. We subtract these block sizes from until the remaining falls inside a block. Then, inside the block of length- numbers, the number we want is , and the digit we want is the -th digit of it, counting from 0 on the left.
For : subtract 9 to get 2, so we are in the 2-digit block. The number is and the digit index is , the second digit of 10, which is 0.
def nth_digit(n):
length, count, start = 1, 9, 1
while n > length * count:
n -= length * count
length += 1
count *= 10
start *= 10
number = start + (n - 1) // length
return int(str(number)[(n - 1) % length])
assert nth_digit(11) == 0
assert nth_digit(3) == 3
assert nth_digit(10) == 1
assert nth_digit(190) == 1
assert nth_digit(1000) == int("".join(str(i) for i in range(1, 400))[999])
int nth_digit(long long n) {
long long length = 1, count = 9, start = 1;
while (n > length * count) {
n -= length * count;
length++;
count *= 10;
start *= 10;
}
long long number = start + (n - 1) / length;
string s = to_string(number);
return s[(n - 1) % length] - '0';
}
The loop runs once per digit length, so it is .
9.3 Check yourself
- In the digit-one count, what are high, cur and low for and the hundreds position?
- How many 1s appear in the numbers from 1 to 99?
- How many digits do all the numbers from 1 to 999 have in total?
- Which number holds the 15th digit of 123456789101112...?
Chapter 10. Sums over divisors
10.1 Counting pairs instead of divisors
Let be the number of divisors of . The problem Sum of Divisor Counts asks for . Computing for every by factorisation would work for moderate , but there is a much simpler view.
The sum counts pairs where divides and . Instead of fixing and counting its divisors , fix the divisor and count how many numbers up to it divides: those are , and there are of them. So
For the terms are , which add up to 14, and indeed also adds up to 14.
def sum_divisor_counts_slow(n):
return sum(n // j for j in range(1, n + 1))
assert sum_divisor_counts_slow(6) == 14
assert sum_divisor_counts_slow(1) == 1
long long sum_divisor_counts_slow(long long n) {
long long total = 0;
for (long long j = 1; j <= n; j++) total += n / j;
return total;
}
This costs , which is fine for up to about but too slow for .
10.2 Grouping equal quotients
Look at the values of for : for they are 100, 50, 33, 25, 20, 16, 14, 12, 11, 10, and for large the same value repeats many times (all from 51 to 100 give 1). The number of different values of is only about : for there are at most different values, and for the quotient is below , so it takes at most different values.
So we can jump over whole runs of equal quotients. If the current has quotient , the run of equal quotients ends at . We add and continue from . This quotient grouping cuts the work to .
def sum_divisor_counts(n):
total = 0
j = 1
while j <= n:
q = n // j
last = n // q
total += q * (last - j + 1)
j = last + 1
return total
assert sum_divisor_counts(6) == 14
assert sum_divisor_counts(100) == sum_divisor_counts_slow(100)
assert sum_divisor_counts(10**5) == sum_divisor_counts_slow(10**5)
assert sum_divisor_counts(1) == 1
long long sum_divisor_counts(long long n) {
long long total = 0;
for (long long j = 1; j <= n;) {
long long q = n / j;
long long last = n / q;
total += q * (last - j + 1);
j = last + 1;
}
return total;
}
Quotient grouping is a handy trick whenever a formula contains , for example in sums of divisors and in counting coprime pairs.
10.3 Check yourself
- Why does equal ?
- For , list the runs of equal and the jumps the grouping loop makes.
- Why are there only about different quotient values?
- How would you count the pairs with ?
Chapter 11. Counting with geometry and the gcd
11.1 Points on one line
Given points on a plane, find the largest number of points that lie on one straight line. This is the problem Max Points on a Line. A direct approach checks every triple of points, costing . A better idea is to fix one point and look at the direction from to every other point. All points on one line through have the same direction (the same slope), so we count how many other points share each direction and take the biggest group, plus itself. Doing this for every choice of costs .
The trap is how to compare slopes. The slope as a decimal number can be wrong because of rounding. Instead we keep it as a pair of whole numbers and reduce it with the gcd so that, for example, and become the same pair. We also fix the sign (so that and are equal) and handle vertical lines, where , by the pair .
def gcd(a, b):
while b:
a, b = b, a % b
return a
def max_points(points):
n = len(points)
best = min(n, 2)
for i in range(n):
counts = {}
for j in range(i + 1, n):
dx = points[j][0] - points[i][0]
dy = points[j][1] - points[i][1]
g = gcd(abs(dx), abs(dy))
dx, dy = dx // g, dy // g
if dx < 0 or (dx == 0 and dy < 0):
dx, dy = -dx, -dy
counts[(dx, dy)] = counts.get((dx, dy), 0) + 1
best = max(best, counts[(dx, dy)] + 1)
return best
assert max_points([[1, 1], [2, 2], [3, 3]]) == 3
assert max_points([[1, 1], [3, 2], [5, 3], [4, 1], [2, 3], [1, 4]]) == 4
assert max_points([[0, 0]]) == 1
assert max_points([[0, 0], [0, 5], [0, 9]]) == 3
int max_points(const vector<vector<int>>& points) {
int n = points.size();
int best = min(n, 2);
for (int i = 0; i < n; i++) {
map<pair<int, int>, int> counts;
for (int j = i + 1; j < n; j++) {
int dx = points[j][0] - points[i][0];
int dy = points[j][1] - points[i][1];
int g = __gcd(abs(dx), abs(dy));
dx /= g;
dy /= g;
if (dx < 0 || (dx == 0 && dy < 0)) {
dx = -dx;
dy = -dy;
}
best = max(best, ++counts[{dx, dy}] + 1);
}
}
return best;
}
The points in the problem are all different, so the gcd is never 0 (if both and were 0, the points would coincide). The cost is pairs, each doing one gcd and one dictionary update.
11.2 Check yourself
- Why is comparing slopes as decimal numbers risky?
- After reducing by the gcd, why do we also fix the sign of the pair?
- Why do we add 1 to the biggest group count?
- What is the cost of the method for 1000 points?
Chapter 12. Putting the tools together
12.1 Choosing the right tool
When a number problem appears, ask what it is really about. If you must test or list primes below a limit that you can store, use the sieve. If you must factor or analyse a single number up to about , use trial division up to . If the number is enormous, up to , use Miller–Rabin. If an answer is asked "modulo " and involves division, multiply by a modular inverse. If an exponent is huge, use fast exponentiation, and if an index is huge, think of fast doubling. If remainders from different moduli must be combined, merge them with CRT. And if a sum contains , group equal quotients.
12.2 Common mistakes
The usual slips are all small. Forgetting to take the remainder after each multiplication makes numbers overflow in C++. Subtracting in C++ without adding the modulus gives negative remainders. Using the inverse trick with a modulus that is not prime breaks it; use extended Euclid instead. Looping up to where is enough turns a fast solution into a timeout. Writing d * d <= n with int overflows for large , so use long long. And when a problem says "return if impossible", remember to check the gcd condition before dividing.
12.3 Check yourself
- Which tool counts the primes below , and which tests a single 18-digit number?
- When can you use Fermat's trick for an inverse, and what do you use otherwise?
- What do you do when an answer needs modulo for many queries?
- Name two places where (and not ) is the right loop limit.
Sources and credit
Several chapters adapt ideas from articles on cp-algorithms.com, including the sieve of Eratosthenes, the Euclidean algorithm, binary exponentiation, the modular inverse, Euler's totient function, the Chinese remainder theorem, primality tests, binomial coefficients, Fibonacci numbers and factorials modulo a prime. The text here is rewritten for beginners. Like the original, it is shared under the Creative Commons Attribution-ShareAlike 4.0 licence.