Note: For discussion, please jump to the blog on Codeforces. This post is re-written and hence a bit more refined, but carries the same underlying idea.
Pre-requisites
- Basic maths
- Factorization
- Algorithms for primality testing
Problem
Given a number , count the number of divisors of .
For example:
- has 2 divisors
- has 4 divisors
- has 4 divisors
Naive Solution - Factorization
One straightforward solution is to use the fact that if is a factor of , then is also a factor of . We can prove that . This gives us an algorithm to find all the factors of .
Pseudocode
def divisor_count(N):
divisors = 0
i = 1
while i * i <= N:
if N % i == 0:
p = i
q = N / i
divisors += 1
if p != q:
# Handle the case when N is not perfect square.
divisors += 1
i += 1
return divisors
Moving towards prime factorization
An alternate way of counting divisors is to decompose into it’s prime factors such that . Now the number of divisors of is simply , where denotes the number of divisors of . One important thing to note about is that if and , then . The proof is left as an exercise for the reader.
Similar to the naive approach, we can loop over primes such that and find corresponding . The number left after dividing by is either 1 or a prime number. The proof is simple – there is only 1 prime that can divide .
Pseudocode
def divisor_count(N):
primes = list of primes <= sqrt(N)
product = 1
for p in primes:
counter = 0
while N % p == 0:
N /= p
counter += 1
product *= (counter + 1)
if N != 1:
product *= 2
return product
Optimized Solution
Let us try to extend what we have already seen so far. Let’s try to write such that . Finding is fairly simple – we only need to consider primes under . We can also observe that . So if we can find , then .
An important observation is that since the minimum number that divides is greater than , hence we can conclude that has at most 2 prime divisors. Therefore, the possible 4 cases are:
- ,
- is a prime number,
- is square of a prime number,
- is product of 2 prime numbers,
Checking for (1) is simple. If we can check for (2) and (3), then we need not worry about (4). And the common part for both (2) and (3) is primality testing. Let’s write out some pseudocode so we understand the theory until now.
Pseudocode
def divisor_count(N):
primes = list of primes <= cube root of N
divisors = 1
for p in primes:
counter = 0
while N % p == 0:
N /= p
counter += 1
divisors *= (counter + 1)
if is_prime(N):
divisors *= 2
elif is_square(N) and is_prime(sqrt(N)):
divisors *= 3
elif N != 1:
divisors *= 4
return divisors
Implementing is_prime function
The only non trivial part of the above pseudocode is the is_prime function, since the largest prime can be of the order . We can use a probabilistic primality test – Miller Rabin test. It has a time complexity of , where is the number tested for primality, and is the number of rounds performed.
Analysis
The time complexity of this approach is . is generally sufficient for the Miller Rabin test, hence the overall time complexity is .
Practice
You can try this technique on problems with more lenient requirements as well (eg: for problems requiring factorisation), but here’s one that requires this trick:
Hope you found this useful and happy coding!