计算给定数除数的最佳算法(性能方面)是什么?
如果您可以提供伪代码或一些示例的链接,那就太好了。
编辑:所有的答案都非常有帮助,谢谢。我正在实现Atkin筛分法,然后将使用类似于Jonathan Leffler所指出的方法。贾斯汀·博佐尼尔(Justin Bozonier)发布的链接提供了有关我想要的更多信息。
计算给定数除数的最佳算法(性能方面)是什么?
如果您可以提供伪代码或一些示例的链接,那就太好了。
编辑:所有的答案都非常有帮助,谢谢。我正在实现Atkin筛分法,然后将使用类似于Jonathan Leffler所指出的方法。贾斯汀·博佐尼尔(Justin Bozonier)发布的链接提供了有关我想要的更多信息。
Answers:
德米特里(Dmitriy)是对的,您希望让《阿特金筛子》(Sieve of Atkin)生成主要清单,但我认为这不能解决整个问题。现在您已经有了素数的列表,您将需要查看其中有多少素数可以用作除数(以及除数)。
这是用于算法的一些python。在 这里搜索“主题:数学-需要除数算法”。只是计算列表中的项目数,而不是返回它们。
本质上可以归结为您的数字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)
规则也是如此
这里有很多更多的技术,以保比·阿特金的筛。例如,假设我们要分解5893。那么它的sqrt是76.76 ...现在,我们尝试将5893写成平方的乘积。那么(77 * 77-5893)= 36的平方是6,所以5893 = 77 * 77-6 * 6 =(77 + 6)(77-6)= 83 * 71。如果那没有用,我们将查看78 * 78-5893是否是理想的正方形。等等。使用这种技术,您可以比测试单个素数更快地测试n的平方根附近的因子。如果您将此技术与筛网结合使用,可以排除较大的质数,那么与单独筛网相比,您将拥有更好的分解方法。
这只是已开发的众多技术之一。这是一个相当简单的过程。例如,您需要花很长时间学习足够多的数论,才能理解基于椭圆曲线的分解因式技术。(我知道它们存在。我不理解它们。)
因此,除非您要处理小整数,否则我不会尝试自己解决该问题。相反,我会尝试找到一种方法来使用已经实现了高效解决方案的PARI库之类的东西。这样,我可以在大约0.05秒内分解出一个随机的40位数字,例如124321342332143143213122323434312213424231341。(如果您想知道,它的因式分解为29 * 439 * 1321 * 157907 * 284749 * 33843676813 * 4857795469949。我非常有信心它没有使用Atkin的筛子解决这个问题...)
@雅斯基
您的除数函数有一个错误,即它不能正确地用于完美平方。
尝试:
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)
ps这是有效的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的因式分解。现在这个问题相当困难!
在给定集合C包含k个项目的情况下重述#2,使得项目a具有a'重复项,而项目b具有b'重复项,等等。有1到k-1个项目的多少个唯一组合?例如,{2},{2,2},{2,2,2},{2,3},{2,2,3,3}必须分别出现一次,并且仅在L = {2,2 ,2,3,3,5}。通过将子集合中的项目相乘,每个此类唯一子集合都是N的唯一除数。
p_i
是一个具有k_i
多重性的数的质因子,该数的除数的总数为(k_1+1)*(k_2+1)*...*(k_n+1)
。我想您现在已经知道了,但是如果在这里随机阅读,我为了方便起见将其写下来。
您问题的答案在很大程度上取决于整数的大小。较小的数字(例如小于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;
}
小就是美丽:)
阿特金(Atkin)筛是Eratosthenes筛的优化版本,它给出所有质数直至给定的整数。您应该可以在Google上搜索更多详细信息。
一旦有了该列表,将数字除以每个质数就很简单了,以查看它是否是一个精确的除数(即,余数为零)。
计算一个数(n)除数的基本步骤是[这是从实码转换成的伪码,所以希望我没有引入错误]:
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
您可以尝试这个。有点破烂,但是相当快。
def factors(n):
for x in xrange(2,n):
if n%x == 0:
return (x,) + factors(n/x)
return (n,1)
在提交解决方案之前,请考虑在典型情况下Sieve方法可能不是一个好的答案。
前一段时间有一个素数问题,我进行了时间测试-对于32位整数,至少确定它是否素数比强力慢。发生两个因素:
1)虽然人花了一段时间进行除法,但是他们在计算机上的速度非常快-类似于查找答案的成本。
2)如果没有主表,则可以创建一个完全在L1缓存中运行的循环。这使其速度更快。
这是一个有效的解决方案:
#include <iostream>
int main() {
int num = 20;
int numberOfDivisors = 1;
for (int i = 2; i <= num; i++)
{
int exponent = 0;
while (num % i == 0) {
exponent++;
num /= i;
}
numberOfDivisors *= (exponent+1);
}
std::cout << numberOfDivisors << std::endl;
return 0;
}
除数做得很壮观:它们完全分开。如果要检查某个数字的除数的数量,n
显然跨越整个频谱是多余的1...n
。我对此没有做任何深入的研究,但是我解决了三角数上的欧拉计划问题12。我针对大于500的除数测试的解决方案运行了309504微秒(〜0.3s)。我为解决方案编写了这个除数函数。
int divisors (int x) {
int limit = x;
int numberOfDivisors = 1;
for (int i(0); i < limit; ++i) {
if (x % i == 0) {
limit = x / i;
numberOfDivisors++;
}
}
return numberOfDivisors * 2;
}
对于每种算法,都有一个弱点。我认为这对素数来说是微不足道的。但是由于三角数字没有打印出来,因此可以完美地实现其目的。从我的分析来看,我认为它做得很好。
节日快乐。
numberOfDivisors
并在1处进行迭代;这应该摆脱零误差除法
您需要此处描述的Atkin筛子:http : //en.wikipedia.org/wiki/Sieve_of_Atkin
素数法在这里很清楚。P []是一个小于或等于sq = sqrt(n)的素数列表。
for (int i = 0 ; i < size && P[i]<=sq ; i++){
nd = 1;
while(n%P[i]==0){
n/=P[i];
nd++;
}
count*=nd;
if (n==1)break;
}
if (n!=1)count*=2;//the confusing line :D :P .
i will lift the understanding for the reader .
i now look forward to a method more optimized .
数论教科书称除数为tau。第一个有趣的事实是它是乘法的,即。当a和b没有公因子时,τ(ab)=τ(a)τ(b)。(证明:a和b的每对除数给出ab的不同除数)。
现在注意,对于pa素数,τ(p ** k)= k + 1(p的幂)。因此,您可以从其分解中轻松计算出τ(n)。
但是,分解大量数据可能很慢(RSA密码术的安全性取决于难以分解的两个大素数的乘积)。这表明该优化算法
以下是一个C程序,用于查找给定数的除数。
上述算法的复杂度为O(sqrt(n))。
该算法将对正整数和非正整数的数字正确工作。
请注意,将循环的上限设置为数字的平方根,以使算法最有效。
请注意,将上限存储在单独的变量中还可以节省时间,您不应在for循环的条件部分中调用sqrt函数,这也可以节省计算时间。
#include<stdio.h>
#include<math.h>
int main()
{
int i,n,limit,numberOfDivisors=1;
printf("Enter the number : ");
scanf("%d",&n);
limit=(int)sqrt((double)n);
for(i=2;i<=limit;i++)
if(n%i==0)
{
if(i!=n/i)
numberOfDivisors+=2;
else
numberOfDivisors++;
}
printf("%d\n",numberOfDivisors);
return 0;
}
除了上面的for循环,您还可以使用以下循环,它效率更高,因为这消除了查找数字平方根的需要。
for(i=2;i*i<=n;i++)
{
...
}
这是我编写的函数。它的最差时间复杂度是O(sqrt(n)),另一方面,最差时间是O(log(n))。它为您提供所有主要除数以及它的出现次数。
public static List<Integer> divisors(n) {
ArrayList<Integer> aList = new ArrayList();
int top_count = (int) Math.round(Math.sqrt(n));
int new_n = n;
for (int i = 2; i <= top_count; i++) {
if (new_n == (new_n / i) * i) {
aList.add(i);
new_n = new_n / i;
top_count = (int) Math.round(Math.sqrt(new_n));
i = 1;
}
}
aList.add(new_n);
return aList;
}
这是计算数字除数的最基本方法:
class PrintDivisors
{
public static void main(String args[])
{
System.out.println("Enter the number");
// Create Scanner object for taking input
Scanner s=new Scanner(System.in);
// Read an int
int n=s.nextInt();
// Loop from 1 to 'n'
for(int i=1;i<=n;i++)
{
// If remainder is 0 when 'n' is divided by 'i',
if(n%i==0)
{
System.out.print(i+", ");
}
}
// Print [not necessary]
System.out.print("are divisors of "+n);
}
}
@肯德尔
我测试了您的代码并进行了一些改进,现在它甚至更快。我还用@هومنجاویدپور代码进行了测试,这也比他的代码快。
long long int FindDivisors(long long int n) {
long long int count = 0;
long long int i, m = (long long int)sqrt(n);
for(i = 1;i <= m;i++) {
if(n % i == 0)
count += 2;
}
if(n / m == m && n % m == 0)
count--;
return count;
}
这是我根据贾斯汀的答案提出的。它可能需要一些优化。
n=int(input())
a=[]
b=[]
def sieve(n):
np = n + 1
s = list(range(np))
s[1] = 0
sqrtn = int(n**0.5)
for i in range(2, sqrtn + 1):
if s[i]:
s[i*i: np: i] = [0] * len(range(i*i, np, i))
return filter(None, s)
k=list(sieve(n))
for i in range(len(k)):
if n%k[i]==0:
a.append(k[i])
a.sort()
for i in range(len(a)):
j=1
while n%(a[i]**j)==0:
j=j+1
b.append(j-1)
nod=1
for i in range(len(b)):
nod=nod*(b[i]+1)
print('no.of divisors of {} = {}'.format(n,nod))
我认为这就是您要寻找的东西。将其复制并粘贴到记事本中,另存为* .bat.Run。输入Number。将过程乘以2,即除数的数量。我故意这样做,以便更快确定除数:
请注意,CMD可变斜面支持值超过999999999
@echo off
modecon:cols=100 lines=100
:start
title Enter the Number to Determine
cls
echo Determine a number as a product of 2 numbers
echo.
echo Ex1 : C = A * B
echo Ex2 : 8 = 4 * 2
echo.
echo Max Number length is 9
echo.
echo If there is only 1 proces done it
echo means the number is a prime number
echo.
echo Prime numbers take time to determine
echo Number not prime are determined fast
echo.
set /p number=Enter Number :
if %number% GTR 999999999 goto start
echo.
set proces=0
set mindet=0
set procent=0
set B=%Number%
:Determining
set /a mindet=%mindet%+1
if %mindet% GTR %B% goto Results
set /a solution=%number% %%% %mindet%
if %solution% NEQ 0 goto Determining
if %solution% EQU 0 set /a proces=%proces%+1
set /a B=%number% / %mindet%
set /a procent=%mindet%*100/%B%
if %procent% EQU 100 set procent=%procent:~0,3%
if %procent% LSS 100 set procent=%procent:~0,2%
if %procent% LSS 10 set procent=%procent:~0,1%
title Progress : %procent% %%%
if %solution% EQU 0 echo %proces%. %mindet% * %B% = %number%
goto Determining
:Results
title %proces% Results Found
echo.
@pause
goto start
我不知道MOST有效的方法,但我会执行以下操作:
应该工作\ o /
如果需要,我明天可以在C语言中编写代码进行演示。