针对计算给定数字的约数数量,最优算法(在性能上)是什么?
如果您能提供伪代码或示例链接,那将非常好。
编辑:所有答案都非常有帮助,谢谢。 我正在实现Atkin筛法,然后将使用类似于Jonathan Leffler指出的方法。 Justin Bozonier发布的链接提供了我所需的更多信息。
针对计算给定数字的约数数量,最优算法(在性能上)是什么?
如果您能提供伪代码或示例链接,那将非常好。
编辑:所有答案都非常有帮助,谢谢。 我正在实现Atkin筛法,然后将使用类似于Jonathan Leffler指出的方法。 Justin Bozonier发布的链接提供了我所需的更多信息。
Dmitriy说你需要使用Atkin筛法生成质数列表,但我不认为这可以解决所有问题。现在你有了一个质数列表,你需要看看其中有多少质数作为因子(以及出现次数)。
这里是该算法的Python代码,在这里搜索“Subject: math - need divisors algorithm”。然后只需计算列表中的项目数量而不是返回它们即可。
基本上,如果您的数字n
是:
n = a^x * b^y * c^z
(其中a、b和c是n的质因数,x、y和z是该因子重复的次数)
那么所有因子的总数为:
(x + 1) * (y + 1) * (z + 1)
。
编辑:顺便说一句,要找到a、b、c等,您需要执行一种贪心算法(如果我理解正确的话)。从最大的质数因子开始,并将其乘以自身,直到进一步的乘法超过数字n。然后转移到下一个较低的因子,并将前一个质数^它被当前质数乘以的次数与当前质数相乘,并继续乘以该质数,直到下一个质数超过n...等等。跟踪您将除数相乘的次数,然后将这些数字应用于上面的公式。
对我的算法描述不是100%确定,但如果不是这样,那么也是类似的。
n = (a^x * b^y * c^z)-(x + 1) * (y + 1) * (z + 1)
是规则。 - SIslam而这只是众多已开发出来的技巧之一。这是相当简单的一个。学习足够的数论知识以理解基于椭圆曲线的因数分解技术需要很长时间。(我知道它们存在。但我不理解它们。)
因此,除非您处理的是小整数,否则我不会尝试自己解决该问题。相反,我会尝试找到一种使用PARI库之类已经实现了高效解决方案的方法。使用它,我可以在约0.05秒内分解一个随机的40位数,例如124321342332143213122323434312213424231341。(如果您想知道它的因式分解,它是29*439*1321*157907*284749*33843676813*4857795469949。我非常有信心它没有使用Atkin筛法来计算...)@Yasky
你的约数函数存在一个错误,对于完全平方数无法正确工作。
尝试使用以下代码:
int divisors(int x) {
int limit = x;
int numberOfDivisors = 0;
if (x == 1) return 1;
for (int i = 1; i < limit; ++i) {
if (x % i == 0) {
limit = x / i;
if (limit != i) {
numberOfDivisors++;
}
numberOfDivisors++;
}
}
return numberOfDivisors;
}
我不同意使用Atkin筛法,因为检查[1,n]内每个数的素性可能比通过除法减少数字所需的时间更长。
这里有一些代码,虽然有点不太正规,但通常会快得多:
import operator
# A slightly efficient superset of primes.
def PrimesPlus():
yield 2
yield 3
i = 5
while True:
yield i
if i % 6 == 1:
i += 2
i += 2
# Returns a dict d with n = product p ^ d[p]
def GetPrimeDecomp(n):
d = {}
primes = PrimesPlus()
for p in primes:
while n % p == 0:
n /= p
d[p] = d.setdefault(p, 0) + 1
if n == 1:
return d
def NumberOfDivisors(n):
d = GetPrimeDecomp(n)
powers_plus = map(lambda x: x+1, d.values())
return reduce(operator.mul, powers_plus, 1)
提示 这是可解决此问题的Python代码。
这是一个直截了当的O(sqrt(n))算法。我用它来解决欧拉计划
def divisors(n):
count = 2 # accounts for 'n' and '1'
i = 2
while i ** 2 < n:
if n % i == 0:
count += 2
i += 1
if i ** 2 == n:
count += 1
return count
这个有趣的问题比看起来要难得多,而且还没有得到解答。这个问题可以分解为两个非常不同的问题。
现在看到的所有答案都涉及#1,并未提到对于巨大的数字它是不可行的。 对于适度大小的N,即使是64位数字,也很容易;但对于巨大的N,质因数分解问题可能需要“永远”。 公钥加密取决于此。
问题#2需要更多讨论。 如果L仅包含唯一数字,则使用组合公式选择n项中的k个对象进行简单计算。 实际上,您需要在变化k从1到sizeof(L)时对应用公式的结果求和。 但是,L通常将包含多个重复的质数。 例如,L = {2,2,2,3,3,5}是N = 360的分解。 现在这个问题就相当困难了!
重新陈述#2,给定包含k个项目的集合C,其中项目a具有a'个副本,项目b具有b'个副本等,有多少个1到k-1个项目的唯一组合? 例如,如果L = {2,2,2,3,3,5},则必须每个独特的子集合-{2},{2,2},{2,2,2},{2,3},{2,2,3,3}都仅出现一次。 每个这样的唯一子集都是通过将子集中的项相乘而得到N的唯一除数。
p_i
是一个具有k_i
重复次数的数的质因子,该数的总因数个数为(k_1+1)*(k_2+1)*...*(k_n+1)
。我猜你现在已经知道这个了,但我写下来是为了让随机读者受益。 - Will Ness对于您的问题的答案在很大程度上取决于整数的大小。对于小的数字,如小于100位的数字和约1000位的数字(如加密中使用的数字)的处理方式完全不同。
小的值n
和一些有用的参考资料:A000005:d(n)(也称为tau(n)或sigma_0(n)),n的除数数量。
现实世界的例子:因式分解整数
只需一行代码
我仔细思考了你的问题,并尝试编写了一段高效且性能良好的代码,
要在屏幕上打印给定数字的所有约数,我们只需要一行代码!(使用gcc编译时选项-std=c99)
for(int i=1,n=9;((!(n%i)) && printf("%d is a divisor of %d\n",i,n)) || i<=(n/2);i++);//n is your number
要找到一个数的约数个数,您可以使用以下非常快速的函数(对于除1和2之外的所有整数数字均有效):
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
return counter;
}
如果您将给定的数字视为除数(对于所有整数数字,除了1和2),则可以正确地处理。
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
return ++counter;
}
注意:上述两个函数对于除了数字1和2之外的所有正整数都能正确工作, 因此它适用于所有大于2的数字。 但如果您需要覆盖1和2,可以使用以下其中一个函数(速度稍慢)
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
if (n==2 || n==1)
{
return counter;
}
return ++counter;
}
或者
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(i==n) && !(n%i) && (counter++)) || i<=(n/2);i++);
return ++counter;
}
small is beautiful :)
def factors(n):
for x in xrange(2,n):
if n%x == 0:
return (x,) + factors(n/x)
return (n,1)
for z in 1..n:
prime[z] = false
prime[2] = true;
prime[3] = true;
for x in 1..sqrt(n):
xx = x * x
for y in 1..sqrt(n):
yy = y * y
z = 4*xx+yy
if (z <= n) and ((z mod 12 == 1) or (z mod 12 == 5)):
prime[z] = not prime[z]
z = z-xx
if (z <= n) and (z mod 12 == 7):
prime[z] = not prime[z]
z = z-yy-yy
if (z <= n) and (x > y) and (z mod 12 == 11):
prime[z] = not prime[z]
for z in 5..sqrt(n):
if prime[z]:
zz = z*z
x = zz
while x <= limit:
prime[x] = false
x = x + zz
for z in 2,3,5..n:
if prime[z]:
if n modulo z == 0 then print z