Algorithm to calculate the number of divisors of a given number
计算给定数的除数的最佳算法(性能方面)是什么?
如果您可以提供伪代码或到某个示例的链接,那就太好了。
编辑:所有的答案都很有帮助,谢谢。我正在实施阿特金的筛选,然后我将使用类似于乔纳森·莱弗勒所说的东西。JustinBozonier发布的链接提供了我想要的更多信息。
德米特里是对的,你会希望筛选阿特金生成主要名单,但我不认为这照顾到整个问题。现在你有了一个素数列表,你需要看看这些素数中有多少是作为除数的(以及有多少次)。
here's some python for the algolook here and search for"subject:math-need divisors algorithm".只需计算列表中的项目数,而不是返回它们。
这是一个数学博士,他解释了你需要在数学上做什么。
基本上可以归结为,如果您的数字
编辑:顺便说一句,要找到A、B、C等,如果我理解正确的话,你会想做一个贪婪的算法。从最大的素数除数开始,将其自身相乘,直到进一步的相乘超过n。然后移到下一个最低的因子,乘以前一个素数乘以当前素数,并一直乘以素数,直到下一个素数超过n。等等,跟踪你将除数相乘的次数,然后把这些数字应用到上面的公式中。
不是100%确定我的算法描述,但如果不是,它是类似的东西。
有比阿特金筛更多的分解技术。例如,假设我们想要系数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位数字(如124321342332143213123234331221321342423141)乘以因数。(如果您想知道,它的因子分解是29*439*1321*157907*284749*33843676813*4857795469949。我很有信心,用阿特金的筛子没能搞清楚……)
@ Yasky
你的除数函数有一个缺陷,它不能正确地适用于完全平方。
尝试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } |
号
我不同意阿特金筛子是一种方法,因为检查[1,n]中的每一个数字的初等性比按分区减少数字容易花费更长的时间。
下面是一些代码,虽然稍微有点粗糙,但通常速度要快得多:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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代码来解决这个问题的。
这个有趣的问题比看上去难得多,而且还没有得到回答。这个问题可以分解成两个截然不同的问题。
1给定n,找出n的素因子列表l2给定l,计算唯一组合数到目前为止,我所看到的所有答案都涉及到1,而且没有提到它对于庞大的数字是不可理解的。对于中等大小的n,即使是64位数字,也很容易;对于巨大的n,阶乘问题可能需要"永远"。公钥加密依赖于此。
问题2需要更多讨论。如果L只包含唯一的数字,则使用组合公式从n个项目中选择k个对象是一个简单的计算。实际上,您需要对应用公式的结果求和,同时将k从1变为sizeof(l)。但是,L通常包含多个素数的多次出现。例如,L=2,2,2,3,3,5是n=360的因式分解。现在这个问题很难解决!
重申2,给定集合C包含k个项目,这样项目A有一个‘重复’,项目B有一个‘重复’,等等。1到k-1项目有多少个独特的组合?例如,2,2,2,2,2,2,2,3,2,2,3,3如果l=2,2,2,3,3,5,则每个事件必须发生一次且仅发生一次。每一个这样的唯一子集合都是一个n的唯一除数,乘以子集合中的项。
问题的答案很大程度上取决于整数的大小。对于较小的数字,例如小于100位的数字和~1000位的数字(例如用于密码学)的方法是完全不同的。
概述:http://en.wikipedia.org/wiki/divisor_函数
小的
n 和一些有用的参考值:a000005:d(n)(也称为tau(n)或sigma_(n)),n的除数。实数例子:整数的因式分解
这里是一个直接的O(sqrt(n))算法。我用这个解项目欧拉
1 2 3 4 5 6 7 8 9 | def divisors(n): count=2 # accounts for 'n' and '1' i=2 while(i**2 < n): if(n%i==0): count+=2 i+=1 count+=(1 if i**2==n else 0) return count |
。
只有一行我对你的问题考虑得很仔细,我试着写一段效率很高、性能很好的代码。要在屏幕上打印给定数字的所有除数,我们只需要一行代码!(通过gcc编译时使用选项-std=c99)
1 2 | for(int i=1,n=9;((!(n%i)) && printf("%d is a divisor of %d ",i,n)) || i<=(n/2);i++);//n is your number |
对于查找除数,可以使用以下非常快的函数(适用于除1和2之外的所有整数)
1 2 3 4 5 6 | 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之外的所有整数都正确工作)
1 2 3 4 5 6 | 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,可以使用以下功能之一(稍微慢一点)
1 2 3 4 5 6 7 8 9 10 | 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; } |
。
或
1 2 3 4 5 6 | 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; } |
。
小是美的:)
你可以试试这个。它有点黑,但速度相当快。
1 2 3 4 5 | def factors(n): for x in xrange(2,n): if n%x == 0: return (x,) + factors(n/x) return (n,1) |
号
阿特金筛是埃拉托斯滕筛的一个优化版本,它使所有素数都达到一个给定的整数。你应该能够在谷歌上搜索到更多细节。
一旦你有了这个列表,用你的数字除以每一个素数,看看它是不是一个精确的除数(即余数是零)。
计算一个数(n)的除数的基本步骤是[这是从实代码转换的伪代码,所以我希望我没有引入错误]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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 |
号
一旦有了主因子分解,就有一种方法可以找到除数。在每个因子的每个指数上加一个,然后将这些指数相乘。
例如:三十六主因子分解:2^2*3^2除数:1,2,3,4,6,9,12,18,36除数:9
每个指数加一个2^3*3^3乘指数:3*3=9
在提交解决方案之前,请考虑在典型情况下,筛选方法可能不是一个好的答案。
前一段时间有一个质数问题,我做了一个时间测试——对于32位整数,至少确定它是否是质数要比蛮力慢。有两个因素:
1)当一个人需要一段时间来做除法时,他们在计算机上的速度非常快——这与查找答案的成本类似。
2)如果没有主表,可以创建一个完全在一级缓存中运行的循环。这使它更快。
这是一个有效的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #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; } |
。
除数做了一些惊人的事情:它们完全分裂。如果你想检查一个数的除数,
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
对于每一种算法,都有一个弱点。我认为这对素数来说是弱的。但由于三角数字不是印刷品,所以它完美地发挥了作用。从我的资料来看,我认为它做得很好。
节日快乐。
下面是一个C程序,用于查找给定数字的除数。
上述算法的复杂度为O(sqrt(n))。
该算法不仅适用于完全平方数,而且适用于非完全平方数。
请注意,循环的上限设置为数字的平方根,以使算法最有效。
注意,将上限存储在单独的变量中也可以节省时间,您不应该在for循环的条件部分调用sqrt函数,这也可以节省计算时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #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 ",numberOfDivisors); return 0; } |
除了上面的for循环之外,您还可以使用下面的循环,因为这样可以消除查找数字平方根的需要,从而更加高效。
1 2 3 4 | for(i=2;i*i<=n;i++) { ... } |
号
@肯德尔
我测试了您的代码并做了一些改进,现在速度更快了。我也用@?????????????????代码,这也比他的代码快。
1 2 3 4 5 6 7 8 9 10 11 | 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; } |
质数法在这里非常清楚。p[]是小于或等于sq=sqrt(n)的素数列表;
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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)=τ(a)τ(b),当a和b没有公因数时。(证明:A和B的每对除数都给出AB的一个独特除数)。
现在注意,对于pA素数,τ(p**k)=k+1(p的幂)。因此,可以很容易地从它的因式分解中计算τ(n)。
然而,分解大数的过程可能很慢(rsa-crytophy的安全性取决于两个大素数的乘积很难分解)。这表明了这种优化算法
这是我写的一个函数。最糟糕的时间复杂度是O(sqrt(n)),另一方面最好的时间是O(log(n))。它给出了所有的素数除数及其发生的次数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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; } |
你想要atkin的筛子,描述如下:http://en.wikipedia.org/wiki/sive-of-atkin
这是计算除数的最基本方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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); } } |
这是我根据贾斯汀的回答提出的。它可能需要一些优化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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)) |
。
我想这个既方便又精确
script.pyton脚本
print len(factors)
您可以预先计算素数,直到最大可能n的平方根,然后计算一个数的每个素数因子的指数。n的除数(n=p1^a p2^b p3^c…)是(a+1)(b+1)(c+1),因为它与计算这个因子的素数的方法相同(这将计算除数)。如果你预先计算质数,这会很快
有关此方法的详细信息:
https://mathschallenge.net/library/number/number_除数
https://www.math.upenn.edu/~deturck/m170/wk2/numdivisors.html
http://primes.utm.edu/glossary/xpage/tau.html(英文)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <iostream> #include <cmath> #include #include <vector> using namespace std; int divisors_count(const vector<int>& primes, int n) { int divisors = 1; for (int i = 0; i < primes.size(); ++i) { int factor = primes[i]; int factor_exponent = 0; while (n % factor == 0) { ++factor_exponent; n /= factor; } divisors *= (factor_exponent + 1); } if (n > 1) return 2*divisors; // prime factor > sqrt(MAX_N) return divisors; } int main() { const int MAX_N = 1e6; int max_factor = sqrt(MAX_N); vector<char> prime(max_factor + 1, true); for (int i = 3; i <= max_factor; i += 2) { if (prime[i]) { for (int j = 3*i; j <= max_factor; j += 2*i) { prime[j] = false; } } } vector<int> primes; primes.reserve(max_factor/2); primes.push_back(2); for (int i = 3; i <= max_factor; i += 2) { if (prime[i]) { primes.push_back(i); } } int n; while (cin >> n) { cout << divisors_count(primes, n) << endl; } } |
尝试以下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int divisors(int myNum) { int limit = myNum; int divisorCount = 0; if (x == 1) return 1; for (int i = 1; i < limit; ++i) { if (myNum % i == 0) { limit = myNum / i; if (limit != i) divisorCount++; divisorCount++; } } return divisorCount; } |
。
这不是一个将数字分解成因子的问题吗-确定数字的所有因子?然后您可以决定是否需要一个或多个因素的所有组合。
因此,一种可能的算法是:
1 2 3 4 5 6 7 8 9 | factor(N) divisor = first_prime list_of_factors = { 1 } while (N > 1) while (N % divisor == 0) add divisor to list_of_factors N /= divisor divisor = next_prime return list_of_factors |
然后由你来结合这些因素来决定剩下的答案。
我想这就是你要找的。我按你的要求做。复制并粘贴到记事本中。另存为*.bat.run.enter number。将进程乘以2,即除数。我故意这样做,以便它更快地确定除数:
请注意,cmd varriable不能支持超过999999999的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | @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 |
。
我不知道最有效的方法,但我会做以下工作:
- 创建素数表,找出所有小于或等于数字平方根的素数(就个人而言,我会使用atkin筛)
- 计算所有小于或等于该数平方根的素数,并将其乘以2。如果数字的平方根是整数,则从计数变量中减去一。
应该工作o/
如果你需要的话,我明天可以用C语言编写一些代码来演示。