# UVa 294: Divisors

In UVa 294, Divisors, we are asked to find the number with the maximum number of divisors in a given range. Counting number of divisors is a classic problem, and there exists a fast and simple way to do it. We start off with a straight-forward, but slow, implementation and progressively optimize it until it’s fast enough. We don’t stop there, though, but we end with a much faster implementation by noticing that divisor count is related to prime factorization.

Read the problem description here or here.

## Interpretation

The problem description is straight-forward and easy to understand, but there are some things we must note before moving forward, that is, $L$ and $U$ can be as big as $10^9$ and their difference can be as big as $10^4$. The numbers are too big for a simple-minded approach to work (as said in the problem description), so we have to find a fast algorithm to avoid getting a Time Limit Exceeded verdict from the online judge. Also note that if two or more numbers all have the maximum divisor count, then the smallest one should be selected.

## Implementation

We’ll start off by implementing the main method. We loop from $L$ to $U$ and compute the divisor count, making use of the divisorCount function that we have yet to implement, and keep track of the number with the highest divisor count. Then we output the result in the format described in the problem description.

public static void main(String[] args) throws Exception {
Scanner in = new Scanner(System.in);
PrintWriter out = new PrintWriter(System.out, true);

int tests = in.nextInt(); // read the number of tests

// for each test
for (int test = 0; test < tests; test++) {

int L = in.nextInt(),    // read L
U = in.nextInt(),    // read U
maxDivisorCount = 0, // the maximum divisor count found
maxNumber = 0;       // the number with the maximum divisor count

// loop through the numbers
for (int i = L; i <= U; i++) {

// the divisor count of the current number
// the divisorCount function calculates it for us
int currentDivisorCount = divisorCount(i);

// if the current divisor count is larger than
// the largest divisor count (a contradiction),
// then the current divisor count is the
// largest divisor count
if (currentDivisorCount > maxDivisorCount) {

// update appropriate variables
maxDivisorCount = currentDivisorCount;
maxNumber = i;
}
}

// output the result in the correct format
out.printf("Between %d and %d, %d has a maximum of %d divisors.\n", L, U, maxNumber, maxDivisorCount);
}
}

Now we just have to implement the divisorCount function.

Our first attempt at the divisorCount function will be to loop through all numbers from $1$ to $n$ and count how many of them are divisors of $n$.

public static int divisorCount(int n) {
// a counter for the number of divisors
int count = 0;

// loop through 1..n
for (int i = 1; i <= n; i++)
if (n % i == 0) // if the remainder of n divided by i is 0
count++;    // then i is a divisor of n

// return the number of divisors
return count;
}

This method tests $n$ numbers, which is bad. We can do way better…

It’s easy to see that $n$ is always a divisor of $n$, and that the second-biggest divisor is at most $n/2$. We can use that fact to cut our search space in half. Now we can modify our divisorCount function to only check numbers from $1$ to $n/2$.

public static int divisorCount(int n) {
// a counter for the number of divisors
// intially 1 because n is a divisor of n
int count = 1;

// loop through 1..n/2
for (int i = 1; i <= n / 2; i++)
if (n % i == 0) // if the remainder of n divided by i is 0
count++;    // then i is a divisor of n

// return the number of divisors
return count;
}

Now we are only checking $n/2$ numbers, but we can still do one more (but big) optimization to our current method.

Now, let’s say that $n = 24$. Its divisors are $\{1,2,3,4,6,8,12,24\}$. If we split the list into two, $\{1,2,3,4\}$ and $\{6,8,12,24\}$, we see that for each number $d$ in the left list, $n/d$ is in the right list. For example, $3$ is in the left list and $24/3 = 8$ is in the right list. Now we only have to look for numbers in the left list, and for each number we find in that list we increment our divisor counter by, not $1$ but $2$, because of the other divisor in the right list. But where does the left list stop, and the right list start? Well, that’s exactly $\sqrt{n}$. So in other words, for every divisor below the square root, there exists exactly one divisor above the square root. The only exceptions are square numbers, but we need to be careful not to count their square root twice, since it will be in both lists.

Now we’ll only loop from $1$ to $\sqrt{n}$, and count each divisor we find, twice.

public static int divisorCount(int n) {
if (n == 1)    // 1 is a tricky number,
return 1;  // so we'll handle it separately

// a counter for the number of divisors
int count = 0;

// save the square root to avoid re-computation
int sqrt = (int)Math.sqrt(n);

// loop through 1..sqrt(n)
for (int i = 1; i <= sqrt; i++)
if (n % i == 0) // if the remainder of n divided by i is 0
count += 2; // then i and n/i are divisors of n

// if n is a square number, then
// we counted sqrt(n) twice
if (sqrt * sqrt == n)
count--; // so we fix the count

// return the number of divisors
return count;
}

Now we’re only checking $\sqrt{n}$ numbers, and that is about as good as it gets using this kind of brute-force algorithm to compute the divisor count. Our solution is currently fast enough to get an Accepted verdict, but we can still do better.

## Divisor Count through Prime Factorization

We won’t be able to optimize our current algorithm more, so if we want a faster solution, we are going to have to think of a better way than checking each number.

Once again, let’s assume $n = 24$. We all know how to find the prime factors (aka. the prime factorization) of a number (and if not, take a look at Wikipedia), and since we’re trying to find some patterns, we might as well factorize $n$. The prime factorization of $24$ is $\displaystyle 24 = 2^3 \times 3^1$. Now that we’ve factorized $n$, we might as well factorize it’s divisors (smiley-face).

$\displaystyle 1 = 2^0 \times 3^0$

$\displaystyle 2 = 2^1 \times 3^0$

$\displaystyle 4 = 2^2 \times 3^0$

$\displaystyle 8 = 2^3 \times 3^0$

$\displaystyle 3 = 2^0 \times 3^1$

$\displaystyle 6 = 2^1 \times 3^1$

$\displaystyle 12 = 2^2 \times 3^1$

$\displaystyle 24 = 2^3 \times 3^1$

Now we notice that the divisors have very similar prime factorizations to $n$. More precisely, they just have different combinations of the powers of the primes. The powers of $2$ range from $0$ to $3$ (same as the power in $n$), and the powers of $3$ range from $0$ to $1$ (same as the power in $n$). Now if we were to count the divisors of $24$, we would use combinatorics and get $(3 + 1) \times (1 + 1) = 8$. Or more generally for any number $n = p_0^{a_0} \times p_1^{a_1} \times \cdots \times p_n^{a_n}$, where $p_i$ is the $i$-th prime factor and $a_i$ is the power of that prime factor, it’s divisor count is $(a_0 + 1) \times (a_1 + 1) \times \cdots \times (a_n + 1)$.

Our new algorithm finds the prime factorization of the number, and then computes the divisor count according to our new formula.
Note that I use an optimized trial division, similar to what we did above, to find the prime factorization, but we could also have used a list of primes.

public static int divisorCount(int n) {
// a counter for the number of divisors
// intially 1 (the multiplication identity)
int count = 1;

// save the square root to avoid re-computation
int sqrt = (int)Math.sqrt(n);

// loop through 2 and the odd numbers up to sqrt(n)
for (int i = 2; i <= sqrt; i = (i == 2 ? 3 : i + 2)) {

// a counter for the power of the
// current number in the prime factorization
int pow = 0;

// while i is in n's prime factorization
while (n % i == 0) {
pow++;  // increment the power count
n /= i; // remove one i from the prime factorization of n
}

// if there were any i's in n's prime factorization
if (pow != 0) {
// change the divisor count according to our formula
count *= pow + 1;

// recompute the square root, since we've changed n
// (a little optimization)
sqrt = (int)Math.sqrt(n);
}
}

// if we've still not removed all factors from n,
// then there is one prime factor left
if (n != 1)
// change the divisor count according to our formula
// (the power of the last prime is 1)
count *= 1 + 1;

// return the number of divisors
return count;
}

## Conclusion

We started with a very slow trial division algorithm, but after some optimizations and some mathematical thinking, we realized that divisor count is related to prime factorization, and used that insight to develop a way faster algorithm. Our current solution is more than fast enough to get an Accepted verdict at the online judge.

If you still want more about divisor count and prime factorization, you might want to take a look at Project Euler 12 and Project Euler 12 – Revisited.

You can take a look at the complete source code here.
So what do you think? Did/would you solve it differently? Let me know in the comments.

### Posted by Bjarki Ágúst

Mike

If you need to do this for a lot of numbers, you can do:

for(int n : primes){
for(int nn=n; nn<=r; nn+=n){
totient[nn]*=(1-1d/n);
}
}


which is equivalent to your method, but avoid repeating work for every number. My programs actually generate the prime list while generating the torient list by noting that a number, i, is prime iff totient[i]=i.

Bjarki Ágúst

First, I don’t understand why you are calculating the totient function?

But if you’re talking about using a sieve to calculate the divisor counts, it won’t work here since the range we’re working with is too big. We would have to store $10^9$ integers, which needs nearly 4GB of memory. I’m also pretty sure it would be too slow, since we would be going through each number and adding it to the divisor count of all its multiples.

Please explain if you had something other in mind.

Eman

Hehe absolutely not!!!!

The limit of numbers you are checking is 10^9 to make a prime factorization of 10^9 you only need sqrt(10^9) = 10^4 * sqrt(10).

So with this optimalization you can generate primes less than sqrt(10^9). And you also use the sqrt(double) function only once.

With this you can do the code in 0.024 sec.

If someone is interested in code, here is C

#include <stdio.h>
#include <math.h>

int maxNum;
int maxDiv;
int primes[15000];
int totalPrimes=0;

int main()
{

int cases;
int a,b;
int i,j;

int PRIME_LIMIT = sqrt(1000000000.0);
int values[PRIME_LIMIT];
for(i = 0; i<PRIME_LIMIT; i++)
values[i] = 0;

for(i = 2; i<PRIME_LIMIT; i++)
if(values[i] == 0)
{
for(j = i+i; j<PRIME_LIMIT; j+=i)
values[j] = 1;
primes[totalPrimes++] = i;
}

scanf("%d", &cases);
for(i = 0; i<cases; i++)
{
scanf("%d%d", &a, &b);
findMax(a,b);
printf("Between %d and %d, %d has a maximum of %d divisors.\n", a,b,maxNum, maxDiv);

}

return 0;

}

void findMax(int a, int b)
{

maxNum = 0;
maxDiv = 0;

int i,j;
int tempDiv;
int num;

for(; a<=b; a++)
{
i = 0;
tempDiv = 1;
num = a;

while(i < totalPrimes && num != 1)
{
j = 1;
while(num % primes[i] == 0)
{
j++;
num /= primes[i];
}

tempDiv *= j;
i++;
}

if(num != 1)
tempDiv *= 2;

if(maxDiv < tempDiv)
{
maxNum = a;
maxDiv = tempDiv;
}
}

}

Bjarki Ágúst

0.024 seconds is pretty good. The Java code I wrote for this post ran in 0.140 seconds, but my original C++ code ran in 0.016 seconds.

But like I mentioned in my post, it can be sped up precalculating primes instead of implicitly finding them each time we calculate the divisor count.

I have one suggestion for your code, though. On line 65, I would add the following code:

if (primes[i] * primes[i] > num) break;

Let me know how if that is faster.

Eman

ok 🙂 best result with all speedups which I was able to do is 0.008 sec.
With your improvement it runs in a half of that time (0.012 sec). But I changed the code little bit (the change is in method findMax(int, int))

If (a % 2 == 1) – find the number of divisors of a, increment a
then (a % 2 == 0) and we can now only roll through even numbers.
with this improvement my code run in 8 ms. (0.008 sec).

Bjarki Ágúst

I actually tried it out for myself. The line that I suggested was enough to get your solution down to 0.008 sec. Take a look at my last two submissions.

Eman

yop I am quite satisfied with it. And can I contact you here somehow?

Bjarki Ágúst

You can use the Contact Us page.