Algorithm 对10^18以内的数字进行素因子分解的最快方法
给定一个数Algorithm 对10^18以内的数字进行素因子分解的最快方法,algorithm,primes,prime-factoring,sieve-of-eratosthenes,factorization,Algorithm,Primes,Prime Factoring,Sieve Of Eratosthenes,Factorization,给定一个数1>=1; 基数=(基数*基数)%mod; } 返回ret; } ull gcd(ull x,ull y){ while(y){ 温度=y; y=x%y; x=温度; } 返回x; } ull pollardrho(ull n){ srand(时间(空)); 如果(n==1) 返回n; ull x=(rand()%(n-2))+2; y=x; ull c=(rand()%(n-1))+1; ull d=1; 而(d==1){ x=(模流(x,2,n)+c+n)%n; y=(模流(y,2
1>=1;
基数=(基数*基数)%mod;
}
返回ret;
}
ull gcd(ull x,ull y){
while(y){
温度=y;
y=x%y;
x=温度;
}
返回x;
}
ull pollardrho(ull n){
srand(时间(空));
如果(n==1)
返回n;
ull x=(rand()%(n-2))+2;
y=x;
ull c=(rand()%(n-1))+1;
ull d=1;
而(d==1){
x=(模流(x,2,n)+c+n)%n;
y=(模流(y,2,n)+c+n)%n;
y=(模流(y,2,n)+c+n)%n;
d=gcd(绝对值(x-y),n);
如果(d==n){
返回pollardrho(n);
}
}
返回d;
}
int main()
{
ios_base::与_stdio同步(false);
cin.tie(0);
initprime();
ulln;
cin>>n;
ull c=n;
向量o;
对于(向量::迭代器i=p.begin();i!=p.end();++i){
ull t=*i;
如果(!(n%t)){
o、 推回(mp(t,0));
}
而(!(n%t)){
n/=t;
o[o.size()-1].y++;
}
}
而(n>1){
ull u=pollardrho(n);
o、 推回(mp(u,0));
而(!(n%u)){
n/=u;
o[o.size()-1].y++;
}
如果(n<10000005){
if(素数[n]){
o、 推回(mp(n,1));
}
}
}
返回0;
}
有没有更快的方法来计算这些数字?如果可能的话,请解释原因以及源代码。在现代处理器上,64位输入的最快解决方案是少量试用除法(数量会有所不同,但通常低于100),然后是Pollard的Rho。您需要使用Miller-Rabin或BPSW进行良好的确定性素性测试,以及处理多个因素的基础设施(例如,如果一个组合被拆分为多个组合)。对于32位,您可以进一步优化这些功能 你会想要一个快速的mulmod,因为它是Pollard的Rho、Miller Rabin和Lucas测试的核心。理想情况下,这是作为一个微小的汇编程序片段来完成的 计算任何64位输入的因数的时间应小于1毫秒。在50位以下速度明显加快 如Ben Buhrow的spBrent实现所示,Brent 1980年论文中的算法P2''似乎与我所知道的其他实现一样快。它使用了布伦特改进的周期发现,以及通过必要的附加回溯延迟GCD的有用技巧 有关各种解决方案的一些混乱细节和基准测试,请参阅。我有许多不同规模的这些和其他实现的基准测试,但还没有发布任何东西(部分原因是有很多方法可以查看数据) 由此产生的一个真正有趣的事情是,多年来被认为是64位范围高端的更好解决方案的SQUFOF不再具有竞争力。SQUFOF的优势在于,它只需要一个快速的完美方形检测器就可以获得最佳速度,而不必在asm中才能达到真正的快速。 假设您有一个数字
n
,它的最大值为1018,并且您希望对其进行素数分解。因为这个数字可以小到单位,也可以大到1018,所以它可以是素数,也可以是复合数,这就是我的方法-
n
使用最大为106的素数,可使用计算n
的更新值使得它只有106以上的素数因子,并且由于n
的值仍然可以大到1018,因此我们得出结论,该数字要么是素数,要么正好有两个素数因子(不一定不同)
- 米勒·拉宾采用
O(对数n)
- Eratosthenes筛取
O(n*logn)
- Pollard rho I shared的实现需要
O(n^0.25)
O(10^7)
,这又是上述算法的复杂性。这意味着您可以在一秒钟内找到几乎所有编程语言的因式分解
空间复杂性
空间仅在步骤2中使用,在步骤2中,筛子被实现,并且等于O(10^6)
。同样,这是非常实用的
实施
在C++14
中实现。代码有一个隐藏的bug。您可以在下一节中显示它,也可以跳过挑战;)
代码中的Bug
在第105行
中,迭代直到i=p
,这样至少有一个等于p
将导致错误的素因子分解
奖金方案
总共有四个这样的数字:{P03,P02P1,P02P2,P0P12},其中P0=p=999983,P1=next_prime(P0)=1000003,P2=next_prime(P1)=1000033
有一些椭圆曲线的东西。我不记得名字了,但网上有代码。你会花很多时间来考虑你的范围内的大素数(>10^14)。你可以在筛选结束后快速进行素数测试,省去寻找大素数因子的麻烦。@rossum我一直在考虑这个问题。但是我找不到任何算法来测试素性,它的复杂度小于O(sqrt(n)),并且不会占用大量内存@Štef Miller Rabin会占用大量内存吗?IIRC该测试可以重复进行,直到假阳性的几率小于计算机硬件故障的几率。它看起来非常好。您可能有该算法的实现吗?@Stef yes我为上面使用的所有算法添加了github repo中的代码片段。如果你要求的话,我可以在一个小时内添加一个完整的工作C++14
代码,因为我现在正在旅行
#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;
}