问题:我正在尝试重构此算法以使其更快。什么是速度的第一次重构?
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;
}
答案 0 :(得分:17)
您可以进行的第一个优化是您只需要检查数字的平方根。这是因为因子成对,其中一个小于平方根而另一个小于平方根。
一个例外是如果n
是一个精确的平方,那么它的平方根是n
的因子,但不是一对的一部分。
例如,如果您的数字是30,那么这些因素就是这些因素:
因此,您不需要检查任何高于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)
在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 “”“
答案 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