获得数字的因素

时间:2010-12-28 21:01:40

标签: c# performance math factors

问题:我正在尝试重构此算法以使其更快。什么是速度的第一次重构?

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;
    }

11 个答案:

答案 0 :(得分:17)

您可以进行的第一个优化是您只需要检查数字的平方根。这是因为因子成对,其中一个小于平方根而另一个小于平方根。

一个例外是如果n是一个精确的平方,那么它的平方根是n的因子,但不是一对的一部分。

例如,如果您的数字是30,那么这些因素就是这些因素:

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

因此,您不需要检查任何高于5的数字,因为一旦您在对中找到相应的小因子,就可以推断出所有其他因素都已存在。

以下是在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;
}

您可以使用更快的其他方法,但您可能会发现这已经足够快,可以满足您的需求,特别是如果您只需要它可以使用32位整数。

答案 1 :(得分:3)

减少你必须走多远的界限,因为你可以明知地停留在数字的平方根上,尽管这确实带有选择具有奇数因子的正方形的警告,但它确实有助于减少循环必须多久执行一次。

答案 2 :(得分:1)

  1. 您可以将FOR循环的上限限制为numberToCheck / 2
  2. 在2处开始循环计数器(如果您的数字是偶数)或3(对于奇数值)。这应该允许您检查每个其他数字,将循环计数再减少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;
    }
    

答案 3 :(得分:1)

看起来这里有关于这个确切主题的冗长讨论:Algorithm to calculate the number of divisors of a given number

希望这有帮助

答案 4 :(得分:1)

首先要注意的是,找到所有主要因素就足够了。一旦你有了这些,就很容易找到总除数的数量:对于每个素数,将它加到它出现的次数并将它们相乘。因此,对于12 = 2 * 2 * 3,您有(2 + 1)*(1 + 1)= 3 * 2 = 6个因素。

接下来的事情从第一个开始:当你找到一个因子时,将其除去,以便得到的数字更小。当你将它与你只需要检查当前数字的平方根这一事实相结合时,这是一个巨大的进步。例如,考虑N = 10714293844487412.天真地需要N步。检查其平方根需要sqrt(N)或大约1亿步。但是因为早期发现了因素2,2,3和953,你实际上只需要检查一百万 - 一个100倍的改进!

另一个改进:你不需要检查每个数字,看它是否除了你的数字,只是素数。如果更方便的话你可以使用2和奇数,或2,3和数字6n-1和6n + 1(一个基本的轮筛)。

这是另一个不错的改进。如果您可以快速确定某个数字是否为素数,则可以进一步减少除法的需要。假设在删除小因素后,您有120528291333090808192969。即使检查其平方根也需要很长时间--3000亿步。但米勒 - 拉宾测试(非常快 - 可能是10到20 纳秒)将显示这个数字是复合的。这有什么用?这意味着如果你检查它的立方根并且没有找到任何因子,那么就剩下两个素数。如果数字是正方形,则其因子是素数;如果数字不是正方形,则数字是不同的素数。这意味着您可以将“运行总计”分别乘以3或4,以获得最终答案 - 即使不知道因素!这可能会产生比你想象的更大的不同:所需的步骤数量从3000亿减少到5000万,增加了6000倍!

上述唯一的问题是米勒 - 拉宾只能证明数字是复合的;如果给它一个素数,它就不能证明这个数字是素数。在这种情况下,您可能希望编写一个primality-proving函数,以免自己分解数字的平方根。 (换句话说,你可以再做一些Miller-Rabin测试,如果你对你的答案是正确的而不是证明它是否正确而感到满意。如果一个数字通过了15次测试,那么它的复合概率小于1十亿。)

答案 5 :(得分:0)

如果你要使用这个函数很多,你可以使用Eratosthenes http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes的修改算法,并将数据的间隔1到Max存储答案。它将运行IntializeArray()一次,之后将返回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];
}

但是如果你不打算使用这个功能我认为最好的解决办法是检查unitll平方根。


注意:我已更正了我的代码!

答案 6 :(得分:0)

一种易于实施的算法,可以为您带来比试验分割更远的Pollard Rho

这是一个Java实现,应该很容易适应C#:http://www.cs.princeton.edu/introcs/78crypto/PollardRho.java.html

答案 7 :(得分: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;
    }

答案 8 :(得分:0)

Codility Python 100 % 这是python的解决方案,几乎没有解释-

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
  

运行示例-

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

“”“ 示例1 24

可除以1 也可被24整除 计数增加2计数2

可除以2 也可被12整除 数增加两个数4

可除以3 也可被8整除 数增加两个数6

可除以4 也可被6整除 数增加两个数8

解决方案8

示例2- 35

可除以1 也可被35整除 计数增加2计数2

可除以5 也可被7整除 数增加两个数4

解决方案4

Example3-

1

可除以1 计数增加1 1

解决方案1 “”“

Github link

答案 9 :(得分:0)

我的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;

答案 10 :(得分:0)

Python实现 得分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