9

I'm trying to refactor this algorithm to make it faster. What would be the first refactoring here for speed?

public int GetHowManyFactors(int numberToCheck)
    {
        // we know 1 is a factor and the numberToCheck
        int factorCount = 2; 
        // start from 2 as we know 1 is a factor, and less than as numberToCheck is a factor
        for (int i = 2; i < numberToCheck; i++) 
        {
            if (numberToCheck % i == 0)
                factorCount++;
        }
        return factorCount;
    }
ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Dave Mateer
  • 6,588
  • 15
  • 76
  • 125
  • 10
    A better method name would be `GetFactorCount`. – SLaks Dec 28 '10 at 21:04
  • possible duplicate of http://stackoverflow.com/questions/110344/algorithm-to-calculate-the-number-of-divisors-of-a-given-number – empi Dec 28 '10 at 21:26

11 Answers11

19

The first optimization you could make is that you only need to check up to the square root of the number. This is because factors come in pairs where one is less than the square root and the other is greater.

One exception to this is if n is an exact square then its square root is a factor of n but not part of a pair.

For example if your number is 30 the factors are in these pairs:

  • 1 x 30
  • 2 x 15
  • 3 x 10
  • 5 x 6

So you don't need to check any numbers higher than 5 because all the other factors can already be deduced to exist once you find the corresponding small factor in the pair.

Here is one way to do it in C#:

public int GetFactorCount(int numberToCheck)
{
    int factorCount = 0;
    int sqrt = (int)Math.Ceiling(Math.Sqrt(numberToCheck));

    // Start from 1 as we want our method to also work when numberToCheck is 0 or 1.
    for (int i = 1; i < sqrt; i++)
    {
        if (numberToCheck % i == 0)
        {
            factorCount += 2; //  We found a pair of factors.
        }
    }

    // Check if our number is an exact square.
    if (sqrt * sqrt == numberToCheck)
    {
        factorCount++;
    }

    return factorCount;
}

There are other approaches you could use that are faster but you might find that this is already fast enough for your needs, especially if you only need it to work with 32-bit integers.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • But he's not checking for primality. If you're counting factors of 100, you would presumably want to include 20, 25, and 50. – phoog Dec 28 '10 at 21:08
  • 1
    @phoog, when you figure out that 5 is a factor, 20 is the other part of the pair. 4 -> 25. 2 -> 50. Mark specifically mentions obtaining factor pairs. – Anthony Pegram Dec 28 '10 at 21:10
  • @Anthony Pegram: Either he edited the post after I first saw it, or I read it carelessly. I didn't see anything about pairs. – phoog Dec 28 '10 at 21:22
  • @Mark Byers: Here's to careless reading. I'll add another upvote as penance :) – phoog Dec 28 '10 at 21:30
3

Reducing the bound of how high you have to go as you could knowingly stop at the square root of the number, though this does carry the caution of picking out squares that would have the odd number of factors, but it does help reduce how often the loop has to be executed.

JB King
  • 11,860
  • 4
  • 38
  • 49
1
  1. You can limit the upper limit of your FOR loop to numberToCheck / 2
  2. Start your loop counter at 2 (if your number is even) or 3 (for odd values). This should allow you to check every other number dropping your loop count by another 50%.

    public int GetHowManyFactors(int numberToCheck)
    {
      // we know 1 is a factor and the numberToCheck
      int factorCount = 2; 
    
      int i = 2 + ( numberToCheck % 2 ); //start at 2 (or 3 if numberToCheck is odd)
    
      for( ; i < numberToCheck / 2; i+=2) 
      {
         if (numberToCheck % i == 0)
            factorCount++;
      }
      return factorCount;
    }
    
Babak Naffas
  • 12,395
  • 3
  • 34
  • 49
1

Looks like there is a lengthy discussion about this exact topic here: Algorithm to calculate the number of divisors of a given number

Hope this helps

Community
  • 1
  • 1
Benoit Martin
  • 3,323
  • 2
  • 22
  • 22
  • that is so true. every other optimization with square root cannot be compared to a proper solution that basically is http://en.wikipedia.org/wiki/Divisor_function – empi Dec 28 '10 at 21:24
  • @empi: While those answers correctly explain how to generate divisors from the prime power factorization, you still need to obtain the factorization. And for that the square root optimization is huge. Furthermore, once you decide to use the square root factoring algorithm it is trivial to modify it to obtain all divisors. Of course there are better factoring algorithms for larger integers. – President James K. Polk Dec 28 '10 at 21:45
1

The first thing to notice is that it suffices to find all of the prime factors. Once you have these it's easy to find the number of total divisors: for each prime, add 1 to the number of times it appears and multiply these together. So for 12 = 2 * 2 * 3 you have (2 + 1) * (1 + 1) = 3 * 2 = 6 factors.

The next thing follows from the first: when you find a factor, divide it out so that the resulting number is smaller. When you combine this with the fact that you need only check to the square root of the current number this is a huge improvement. For example, consider N = 10714293844487412. Naively it would take N steps. Checking up to its square root takes sqrt(N) or about 100 million steps. But since the factors 2, 2, 3, and 953 are discovered early on you actually only need to check to one million -- a 100x improvement!

Another improvement: you don't need to check every number to see if it divides your number, just the primes. If it's more convenient you can use 2 and the odd numbers, or 2, 3, and the numbers 6n-1 and 6n+1 (a basic wheel sieve).

Here's another nice improvement. If you can quickly determine whether a number is prime, you can reduce the need for division even further. Suppose, after removing small factors, you have 120528291333090808192969. Even checking up to its square root will take a long time -- 300 billion steps. But a Miller-Rabin test (very fast -- maybe 10 to 20 nanoseconds) will show that this number is composite. How does this help? It means that if you check up to its cube root and find no factors, then there are exactly two primes left. If the number is a square, its factors are prime; if the number is not a square, the numbers are distinct primes. This means you can multiply your 'running total' by 3 or 4, respectively, to get the final answer -- even without knowing the factors! This can make more of a difference than you'd guess: the number of steps needed drops from 300 billion to just 50 million, a 6000-fold improvement!

The only trouble with the above is that Miller-Rabin can only prove that numbers are composite; if it's given a prime it can't prove that the number is prime. In that case you may wish to write a primality-proving function to spare yourself the effort of factoring to the square root of the number. (Alternately, you could just do a few more Miller-Rabin tests, if you would be satisfied with high confidence that your answer is correct rather than a proof that it is. If a number passes 15 tests then it's composite with probability less than 1 in a billion.)

Charles
  • 11,269
  • 13
  • 67
  • 105
0

https://codility.com/demo/results/demoAAW2WH-MGF/

 public int solution(int n) {
      var counter = 0;          
      if (n == 1) return 1;
      counter = 2; //1 and itself      
      int sqrtPoint = (Int32)(Math.Truncate(Math.Sqrt(n)));
      for (int i = 2; i <= sqrtPoint; i++)
      {
        if (n % i == 0)
        {
          counter += 2; //  We found a pair of factors.         
        }       
      }
      // Check if our number is an exact square.
      if (sqrtPoint * sqrtPoint == n)
      {
        counter -=1;
      }

      return counter;
    }
nowords
  • 1
  • 2
  • Hello and welcome to Stack Overflow! Exactly the same code as you posted was already posted as an answer to this question five years ago, so I fail to see how this contributes. – Anders Aug 20 '15 at 21:07
  • Sorry, my bad, didn't notice the difference. It would be a great answer if you elaborated a bit in text on what you had fixed. – Anders Aug 20 '15 at 21:12
0

Well if you are going to use this function a lot you can use modified algorithm of Eratosthenes http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes and store answars for a interval 1 to Max in array. It will run IntializeArray() once and after it will return answers in 0(1).

const int Max =1000000;
int arr [] = new int [Max+1];

public void InitializeArray()
{
    for(int i=1;i<=Max;++i)
        arr[i]=1;//1 is factor for everyone

    for(int i=2;i<=Max;++i)
        for(int j=i;i<=Max;i+=j)
           ++arr[j];
}
public int GetHowManyFactors(int numberToCheck)
{
   return arr[numberToCheck];
}

But if you are not going to use this function a lot I think best solution is to check unitll square root.


Note: I have corrected my code!

UmmaGumma
  • 5,633
  • 1
  • 31
  • 45
0

An easy to implement algorithm that will bring you much farther than trial division is Pollard Rho

Here is a Java implementation, that should be easy to adapt to C#: http://www.cs.princeton.edu/introcs/78crypto/PollardRho.java.html

Landei
  • 54,104
  • 13
  • 100
  • 195
0

Codility Python 100 % Here is solution in python with little explanation-

def solution(N):
"""
Problem Statement can be found here-
https://app.codility.com/demo/results/trainingJNNRF6-VG4/
Codility 100%

Idea is count decedent factor in single travers. ie. if 24 is divisible by 4 then it is also divisible by 8
Traverse only up to square root of number ie. in case of 24, 4*4 < 24 but 5*5!<24 so loop through only i*i<N
"""
print(N)
count = 0
i = 1
while i * i <= N:
    if N % i == 0:
        print()
        print("Divisible by " + str(i))
        if i * i == N:
            count += 1
            print("Count increase by one " + str(count))
        else:
            count += 2
            print("Also divisible by " + str(int(N / i)))
            print("Count increase by two count " + str(count))
    i += 1
return count

Example by run-

if __name__ == '__main__':
# result = solution(24)
# result = solution(35)
result = solution(1)
print("")
print("Solution " + str(result))

""" Example1- 24

Divisible by 1 Also divisible by 24 Count increase by two count 2

Divisible by 2 Also divisible by 12 Count increase by two count 4

Divisible by 3 Also divisible by 8 Count increase by two count 6

Divisible by 4 Also divisible by 6 Count increase by two count 8

Solution 8

Example2- 35

Divisible by 1 Also divisible by 35 Count increase by two count 2

Divisible by 5 Also divisible by 7 Count increase by two count 4

Solution 4

Example3-

1

Divisible by 1 Count increase by one 1

Solution 1 """

Github link

0

I got pretty good results with complexity of O(sqrt(N)).

if (N == 1) return 1;
    int divisors = 0;
    int max = N;
    for (int div = 1; div < max; div++) {
        if (N % div == 0) {
            divisors++;
            if (div != N/div) {
                divisors++;
            }
        }
        if (N/div < max) {
            max = N/div;
        }
    }
    return divisors;
0

Python Implementation Score 100% https://app.codility.com/demo/results/trainingJ78AK2-DZ5/


import math;

def solution(N):
    # write your code in Python 3.6
    NumberFactor=2; #one and the number itself
    if(N==1):
        return 1;
    if(N==2):
        return 2;
    squareN=int(math.sqrt(N)) +1;
    #print(squareN)

    for elem in range (2,squareN):
        if(N%elem==0):
            NumberFactor+=2;
    
    if( (squareN-1) * (squareN-1) ==N):
        NumberFactor-=1;
    return NumberFactor