SQUFOF:比 Pollard-Rho 更好的质因数分解算法
littlez_meow · · 算法·理论
Part 0:前言与算法简介
做数论题时,你遇到了许多需要 Pollard-Rho 分解质因数的题。
你想去学,不过看着那么多前置知识和优化,还是个随机化算法,甚至要卡常。你望而却步。
但是质因数分解算法还是需要的。你在网上检索,发现了一个名为 SQUFOF 的算法。
SQUFOF,全称 Shanks' SQUare FOrm Factorization,是二十纪七八十年代由 Daniel Shanks 发明的算法。有趣的是,他从未正式发表过该算法。
SQUFOF 相比于 Pollard-Rho 有如下优点:
-
在都没有卡常的情况下,SQUFOF 的常数远小于 Pollard-Rho。
-
可以证明 SQUFOF 运算次数上界为
t\times3\sqrt{2\sqrt n}=O(n^{0.25}) ,而非期望。其中t 取决于你的实现,在10^{18} 及以下时为3 。 -
SQUFOF 的实现更简单,不需要倍增判环等等东西,只需递推。
可以说,SQUFOF 是分解
下面记需要分解的数为
Part 1:算法原理
SQUFOF 的使用前提是
假设我现在找到了一对不相等的数
为了找到这样的
设
容易得出递推式
将这些递推式带回原连分数容易验证,在此不过多赘述。
设
简单证明一下。
首先根据连分数写出
既然是关于递推式的命题,考虑数学归纳法。当
写出等式左边为
代入递推式展开得
整理一下有
代入假设得
对
对比等式右边,我们仅需证明
这可以再次使用数学归纳法,然后螺旋归纳得到结果。
综上,
因此,
Part 2:算法流程
Part 3:代码
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
using namespace std;
const int MR_Prime[]={2,325,9375,28178,450775,9780504,1795265022};
inline ll qpow(__int128 base,ll expo,const ll MOD){
ll res(1);
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
inline bool MR(ll n){
if(n<=2) return n==2;
if(!(n&1)) return 0;
ll expo=n-1;
int r=__builtin_ctzll(expo);
expo>>=r;
for(int i:MR_Prime){
ll v=qpow(i,expo,n);
if(v<=1||v==n-1) continue;
F(j,1,r){
v=__int128(v)*v%n;
if(v==n-1&&j!=r){
v=1;
break;
}
if(v==1) return 0;
}
if(v!=1) return 0;
}
return 1;
}
const int SQUFOF_K[]={1,3,5};
inline ll SQUFOF(ll n){
if(!(n&1)) return 2;
ll qwq=sqrt(n);
if(qwq*qwq==n) return qwq;
for(int k:SQUFOF_K){
if(k!=1&&!(n%k)) return k;
ll kn=k*n,rn=sqrt(kn);
auto trans=[&](ll&b,ll&P,ll&Q){
swap(b,Q);
ll pre=P,q=(rn+P)/b;
P=q*b-P;
Q+=q*(pre-P);
return;
};
ll b=0,P=rn,Q=1;
int lim=sqrt(rn<<1);
lim<<=2;
trans(b,P,Q);
Q=(kn-P*P)/b;
for(int i(2);i<lim;i+=2){
trans(b,P,Q);
qwq=sqrt(Q);
if(qwq*qwq==Q){
ll bb,PP,QQ;
PP=-P,QQ=qwq;
trans(bb,PP,QQ);
QQ=(kn-PP*PP)/bb;
do{
qwq=PP;
trans(bb,PP,QQ);
}while(PP!=qwq);
qwq=__gcd(n,qwq);
if(qwq!=1) return qwq;
}
trans(b,P,Q);
}
}
return 1;
}
ll ans;
inline void factorize(ll n){
if(n==1) return;
if(MR(n)) return ans=max(ans,n),void();
ll qaq=SQUFOF(n);
while(qaq==n) qaq=SQUFOF(n);
while(!(n%qaq)) n/=qaq;
factorize(n),factorize(qaq);
return;
}
ll n;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;
for(cin>>T;T;--T){
cin>>n;
ans=0;
if(MR(n)) cout<<"Prime\n";
else factorize(n),cout<<ans<<"\n";
}
return 0;
}
Part \infty :参考文献
[1] Jason E. Gower and Samuel S. Wagstaff, JR., Square form factorization, Mathematics of computation 77 (2008), no.261, 551-588.