Unit 14: Numbers and Primes
Arithmetic that survives a modulus.
Unit 14 of 25 in Competitive programming for kids. Its 4 lessons are Sieving for Primes, Taking a Number Apart, Working Under a Modulus and Dividing Under a Modulus — below is everything each one explains, and a question or two from it to try.
Every sample on this page was run through real Python before it shipped, and prints exactly what it says it prints.
This unit opens with a fortnight’s trial of everything — no card needed — or with a family plan, bought in the iPhone app. The first two units of every track are free for ever. Try it in the app.
🔱 Sieving for Primes
Testing one number: stop at the square root
To check whether n is prime, try dividing by 2, 3, 4… but only up to the square root. If n = a * b then one of a and b is at most the square root, so a factor above it would already have been found below.
So one number costs about sqrt(n), which for a million is a thousand.
Python
def is_prime(n):
if n < 2:
return False
i = 2
while i * i <= n:
if n % i == 0:
return False
i += 1
return True
print([n for n in range(2, 30) if is_prime(n)])
It prints
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Testing every number: cross out instead
Checking a million numbers one at a time costs a thousand million. The sieve of Eratosthenes does the lot in about three million: take each prime and cross out its multiples.
Start the crossing at p * p — anything smaller has a smaller factor and is already gone.
Python
limit = 30
prime = [True] * (limit + 1)
prime[0] = False
prime[1] = False
for p in range(2, limit + 1):
if prime[p]:
for multiple in range(p * p, limit + 1, p):
prime[multiple] = False
print([n for n in range(limit + 1) if prime[n]])
print(sum(prime))
It prints
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29] 10
How many primes are there?
Roughly n / log n of the numbers below n are prime — 25 below 100, 168 below 1000, about 78 thousand below a million.
That matters when a problem says "for each prime": it is far fewer than n, and often the difference between fitting and not.
Try it yourself
Why does the sieve start crossing out at p * p rather than 2 * p?
- To save memory
- Everything below `p * p` has a smaller prime factor and is already crossed out
- Because `2 * p` is prime
- It is a mistake — it should start at `2 * p`
What does this print?
Python
limit = 20
prime = [True] * (limit + 1)
prime[0] = False
prime[1] = False
for p in range(2, limit + 1):
if prime[p]:
for multiple in range(p * p, limit + 1, p):
prime[multiple] = False
print(sum(prime))
Answer them in the app
🧱 Taking a Number Apart
Every number is a product of primes, one way only
Divide out each factor as often as it goes. Whatever survives past the square root is itself a prime and goes on the end.
360 is 2 times 2 times 2 times 3 times 3 times 5. Nothing else multiplies to 360 in primes.
Python
def factorise(n):
factors = []
d = 2
while d * d <= n:
while n % d == 0:
factors.append(d)
n //= d
d += 1
if n > 1:
factors.append(n)
return factors
print(factorise(360))
print(factorise(97))
print(factorise(1000000007))
It prints
[2, 2, 2, 3, 3, 5] [97] [1000000007]
The factors tell you how many divisors there are
360 is 2 to the 3, times 3 squared, times 5. A divisor picks 0 to 3 twos, 0 to 2 threes, and 0 or 1 five — so 4 * 3 * 2 = 24 of them.
Counting them by trying every number up to 360 gives the same answer and takes 360 times longer.
Python
print(4 * 3 * 2)
print(len([d for d in range(1, 361) if 360 % d == 0]))
It prints
24 24
Euclid, still the fastest thing in the unit
The greatest common divisor of a and b is the gcd of b and a % b, until the second one is 0. It takes about log steps and it is two lines.
The lowest common multiple comes free: divide the product by the gcd — and divide first if the numbers are big, or the product may be enormous.
Python
def gcd(a, b):
while b:
a, b = b, a % b
return a
print(gcd(360, 84))
print(360 // gcd(360, 84) * 84)
It prints
12 2520
Try it yourself
A number is 2 to the 4, times 7 squared. How many divisors has it?
- 6
- 8
- 15
- 12
What does this print?
Python
def gcd(a, b):
while b:
a, b = b, a % b
return a
print(gcd(48, 18), gcd(17, 5), gcd(0, 9))
Answer them in the app
🕐 Working Under a Modulus
Why every counting problem says "modulo 1000000007"
The answers are astronomically large. Rather than making you print a thousand digits, the setter asks for the remainder after dividing by a big prime — usually 1000000007.
Adding and multiplying survive that perfectly: you may take the remainder at every step and the answer is the same.
Python
MOD = 1000
a = 123456
b = 789
print((a * b) % MOD)
print(((a % MOD) * (b % MOD)) % MOD)
It prints
784 784
Powers by doubling
3 ** 100 under a modulus, one multiply at a time, is a hundred steps. Squaring instead does it in seven: read the exponent in binary — unit 3 again — and square as you go.
This is called fast exponentiation and it is everywhere.
Python
def power(base, exponent, mod):
result = 1
base %= mod
while exponent > 0:
if exponent & 1:
result = result * base % mod
base = base * base % mod
exponent >>= 1
return result
MOD = 1000000007
print(power(2, 10, MOD))
print(power(3, 100, MOD))
print(pow(3, 100, MOD))
It prints
1024 886041711 886041711
Python has it built in, and you should still know the loop
pow(base, exponent, mod) is the same thing and is what you would type in a contest. The loop is here because it is the shape of unit 10's jumping and unit 16's matrix powers — the same doubling, three times over.
And in C++ there is no built-in, so people write this loop from memory.
Subtracting can go negative
In Python -3 % 7 is 4, which is what you want. In most other languages it is -3, which is not — so contest code from elsewhere is full of (a - b + mod) % mod.
Worth knowing when you read someone else's solution and wonder what the extra + mod is for.
Python
print(-3 % 7)
print((4 - 6) % 7)
It prints
4 5
Try it yourself
How many multiplications does fast exponentiation need for an exponent near a million?
- A million
- A thousand
- About forty
- One
What does this print?
Python
def power(base, exponent, mod):
result = 1
base %= mod
steps = 0
while exponent > 0:
if exponent & 1:
result = result * base % mod
base = base * base % mod
exponent >>= 1
steps += 1
return result, steps
print(power(2, 100, 1000))
Answer them in the app
🏆 Dividing Under a Modulus
The one operation that does not survive
Adding, subtracting and multiplying all work fine with remainders. Division does not: 10 divided by 5 is 2, but the remainders of 10 and 5 tell you nothing useful.
And counting problems are full of division — every "choose k from n" is a fraction.
So multiply by the opposite instead
The inverse of a is the number that multiplies with it to give 1. Under 11, the inverse of 7 is 8, because 56 leaves 1.
Dividing by 7 is then multiplying by 8, and everything stays a whole number.
Python
MOD = 11
for a in range(1, 11):
for b in range(1, 11):
if a * b % MOD == 1:
print(a, b)
It prints
1 1 2 6 3 4 4 3 5 9 6 2 7 8 8 7 9 5 10 10
Fermat's little theorem finds it for you
When the modulus is prime, a to the power mod - 1 leaves 1. So a times a to the power mod - 2 leaves 1 — which makes a ** (mod - 2) the inverse.
That is one call to fast exponentiation, and it is why contests always pick a prime modulus.
Python
MOD = 1000000007
inverse = pow(3, MOD - 2, MOD)
print(inverse)
print(3 * inverse % MOD)
print(pow(7, 9, 11), 7 * pow(7, 9, 11) % 11)
It prints
333333336 1 8 1
Now binomial coefficients work
"How many ways to choose 50 from 100" is a fraction of factorials with about 160 digits in it. Under the modulus it is three numbers multiplied together.
Work out all the factorials once, then every question is two inverses and a multiply.
Python
MOD = 1000000007
limit = 200
fact = [1] * (limit + 1)
for i in range(1, limit + 1):
fact[i] = fact[i - 1] * i % MOD
def choose(n, k):
return fact[n] * pow(fact[k], MOD - 2, MOD) % MOD * pow(fact[n - k], MOD - 2, MOD) % MOD
print(choose(10, 3))
print(choose(20, 10))
print(choose(100, 50))
It prints
120 184756 538992043
Why the answer looks like nonsense
choose(100, 50) really is a 30-digit number; 538992043 is its remainder. That is the answer the judge wants, and there is no way to look at it and tell it is right.
So check your code on choose(10, 3), where you can count 120 by hand. Always test a modular program on a case small enough to verify.
Try it yourself
Why does Fermat's trick need the modulus to be prime?
- It is faster that way
- The theorem it comes from is only true for a prime modulus
- Primes are bigger
- It works for any modulus
What does this print?
Python
MOD = 13
inverse = pow(5, MOD - 2, MOD)
print(inverse, 5 * inverse % MOD)
Answer them in the app