给定一个数字1 <= n <= 10^18
,如何在最短的时间复杂度下对其进行分解?
互联网上有很多帖子说明你如何找到素数因素,但在特定情况下,没有一个(至少从我所见过的)中说明他们的好处。
除了Eratosthenes的筛子之外,我使用Pollard的rho算法:
n
除以这些素数。我的实施:
#include <iostream>
#include <vector>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>
using namespace std;
typedef unsigned long long ull;
typedef long double ld;
typedef pair <ull, int> pui;
#define x first
#define y second
#define mp make_pair
bool prime[10000005];
vector <ull> p;
void initprime(){
prime[2] = 1;
for(int i = 3 ; i < 10000005 ; i += 2){
prime[i] = 1;
}
for(int i = 3 ; i * i < 10000005 ; i += 2){
if(prime[i]){
for(int j = i * i ; j < 10000005 ; j += 2 * i){
prime[j] = 0;
}
}
}
for(int i = 0 ; i < 10000005 ; ++i){
if(prime[i]){
p.push_back((ull)i);
}
}
}
ull modularpow(ull base, ull exp, ull mod){
ull ret = 1;
while(exp){
if(exp & 1){
ret = (ret * base) % mod;
}
exp >>= 1;
base = (base * base) % mod;
}
return ret;
}
ull gcd(ull x, ull y){
while(y){
ull temp = y;
y = x % y;
x = temp;
}
return x;
}
ull pollardrho(ull n){
srand(time(NULL));
if(n == 1)
return n;
ull x = (rand() % (n - 2)) + 2;
ull y = x;
ull c = (rand() % (n - 1)) + 1;
ull d = 1;
while(d == 1){
x = (modularpow(x, 2, n) + c + n) % n;
y = (modularpow(y, 2, n) + c + n) % n;
y = (modularpow(y, 2, n) + c + n) % n;
d = gcd(abs(x - y), n);
if(d == n){
return pollardrho(n);
}
}
return d;
}
int main ()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
initprime();
ull n;
cin >> n;
ull c = n;
vector <pui> o;
for(vector <ull>::iterator i = p.begin() ; i != p.end() ; ++i){
ull t = *i;
if(!(n % t)){
o.push_back(mp(t, 0));
}
while(!(n % t)){
n /= t;
o[o.size() - 1].y++;
}
}
while(n > 1){
ull u = pollardrho(n);
o.push_back(mp(u, 0));
while(!(n % u)){
n /= u;
o[o.size() - 1].y++;
}
if(n < 10000005){
if(prime[n]){
o.push_back(mp(n, 1));
}
}
}
return 0;
}
有没有更快的方法来计算这些数字?如果可能,请解释原因以及源代码。
答案 0 :(得分:4)
假设您有一个数字n
,最多可达10 18 并且您想要对它进行推理。因为这个数字可以小到1并且大到10 18 ,所以它可以是素数和复合数,这将是我的方法 -
n
,可以使用sieve of Eratosthenes计算。现在n
的更新值是这样的,它具有仅超过10 6 的素数因子,因为n
的值仍然可以大到10 18 ,我们得出结论,这个数字要么是素数,要么只有两个素数因子(不一定是不同的)。
你现在已经完全分解了。
让我们看一下上述方法的时间复杂性:
O(log n)
O(n*log n)
O(n^0.25)
步骤2占用的最大时间等于O(10^7)
,这又是上述算法的复杂性。这意味着您可以在几秒钟内找到几乎所有编程语言的因子分解。
空间仅在实施筛子的步骤2中使用,并且等于O(10^6)
。再次,非常实用。
C++14
中实施了Complete Code。代码有一个隐藏的bug。您可以在下一部分中显示它,或跳过挑战;)
在
的情况line 105
中,迭代到i<=np
。否则,您可能会错过prime[np]=999983
是主要因素
给我一个n
的值,如果有的话,共享代码会导致错误的素数因子化。
n
有多少这样的值?
解对于n的这样的值,
Line 119
中的断言可能会失败。
奖金解决方案让我们来电话
P=999983
。n = p*q*r
形式的所有数字,其中p,q,r是素数&gt; =P
,使得它们中的至少一个等于P
将导致错误的素因数分解。
正好四个这样的数字:{P 0 3 ,P 0 2 < / sup> P 1 ,P 0 2 P 2 ,P 0 P 1 2 },其中P 0 = P = 999983,P 1 = next_prime(P 0 )= 1000003,P 2 = next_prime(P 1 )= 1000033。
答案 1 :(得分:2)
现代处理器上64位输入的最快解决方案是少量试验(数量会有所不同,但100以下是常见的),其次是Pollard的Rho。您将需要使用Miller-Rabin或BPSW进行良好的确定性素性测试,以及处理多种因素的基础设施(例如,如果复合材料分成更多复合材料)。对于32位,您可以进一步优化这些内容。
你需要一个快速的mulmod,因为它是Pollard的Rho,Miller-Rabin和Lucas测试的核心。理想情况下,这是作为一个小的汇编程序片段完成的。
时间应小于1毫秒,以计算任何64位输入。在50位以下显着更快。
如Ben Buhrow的spBrent实现所示,算法P2&#39;&#39;来自布伦特原油的1980年论文似乎与我所知道的其他实施一样快。它使用布伦特改进的循环发现以及通过必要的附加回溯来延迟GCD的有用技巧。
请参阅this thread on Mersenneforum了解各种解决方案的一些混乱细节和基准测试。我有不同大小的这些和其他实现的许多基准,但没有发布任何东西(部分原因是有很多方法可以查看数据)。
其中一个非常有趣的事情是SQUFOF,多年来被认为是64位高端产品的更好解决方案,不再具有竞争力。 SQUFOF的优势在于只需要一个快速的完美方形探测器来获得最佳速度,而且不必非常快速。