Pollard-Rho学习笔记
Pollard-Rho 还是很容易理解的,其作用就是找一个数 \(n\) 的一个非平凡约数。(非平凡约数即不是 \(1\) 也不是它本身的约数)。
复杂度大致是 \(n^{\frac{1}{4}} \log n\)。
首先引入生日悖论:一年 \(365\) 天,当一个房间中的人数至少为 \(23\) 人时,两个人的生日日期相同的概率大于等于 \(50 \%\)。
这个就让我们明白了在值域为 \(N\) 时,随机 \(O(\sqrt N)\) 次就会出现一对相同的数。
于是我们设计一个伪随机数 \(f(x) = x^2 + c \pmod n\),\(c\) 为随机的一个数。
为什么要这样做呢?我们设 \(n\) 最小的非平凡约数时 \(p\),首先能发现一个很好的性质,若我们得到这个序列: \(x_1,x_2=f(x_1),x_3=f(x_2),....\),最终会形成一个循环节,也就像 \(\rho\) 一样。而且还满足:若 \(x \equiv y \pmod p\),\(f(x) \equiv f(y) \pmod p\)。由上文的生日悖论,可以得到出现 \(x_i \equiv x_j \pmod p\) 的期望复杂度是 \(O(\sqrt p)\) 的,但我们并不知道 \(p\) 为多少,我们只知道当 \(x_i \equiv x_j \pmod p\) 时,\(\gcd(|x_i - x_j|,n)\) 不为 \(1\),而这也就是我们对重复了的情况的判断标准。(记住是不为 \(1\),所以找到的约数可能是 \(n\)。)
然后我们相当于就是需要在较快的时间内判断环是否出现。
这里简单介绍一下 floyd 判环,因为这是主要思想,其他做法也只是这个做法的优化。我们把 \(x_i \equiv x_j \pmod p\) 当成两个人在环上跑,其中一个人超了另一个人一圈。所以我们使用两个指针,一个每次跳一步,另一个每次跳两步,然后每次做差判断是出现环。
至于优化,这里讲倍增优化,我们第 \(k\) 轮让后面那个指针跳 \(2^k\) 步,前面指针不动,如果出现 \(gcd\) 不为 \(1\) 的情况,就说明出现了环。
我们考虑到如果每次都要判 \(\gcd\) 就要带一个 \(\log\),也就是 \(O(\sqrt p \log n)\),而我们知道若 \(\gcd(|x_i - x_j|,n)\) 不为 \(1\),那 \(\gcd(\Pi|x_i - x_j| \bmod n,n)\) 也不为 \(1\),所以我们考虑计算乘积与 \(n\) 的 \(\gcd\),我们考虑每 \(k\) 次就算一次 \(\gcd\),那复杂度就变成了 \(O(\frac{\sqrt p \log n}{k} + \sqrt p)\),这里 \(k\) 是一个常数,自己定义即可。
分解质因数还需要 Miller-Rabin 的辅助,Miller-Rabin 相对简单,故不再赘述。
分解质因数代码:
点击查看代码
#define ll __int128
#define int long long
struct Miller_Rabin{
int pri[13]={0,2,3,5,7,11,13,17,19,23,29,31,37},cnt=12;
inline bool chk(ll n,ll p){
if(n%p==0) return false;
ll num=n-1,t=0;
while(num%2==0) num/=2,t++;
ll v=qpow(p,num,n),s=0;
if(v==1) return true;
for(;s<t;s++){
if(v==n-1) return true;
v=v*v%n;
}
if(s==t) return false;
return true;
}
inline bool is_pri(int n){
for(int i=1;i<=cnt;i++){
if(pri[i]==n) return true;
if(!chk(n,pri[i])) return false;
}
return true;
}
}Test;
struct Pollard_Rho{
int k=127;
inline ll F(ll x,ll c,ll mod){
x%=mod;
return (x*x+c)%mod;
}
inline ll abs(ll x){return (x>0)?x:-x;}
inline int gt_div(ll x){
int c=rand()%(x-1)+1;
ll s=0,t=0,v=1;
ll tt=0;
for(int w=1;;w<<=1,s=t){
for(int num=1;num<=w;num++){
tt++;
t=F(t,c,x);
v=v*(abs(t-s)%x)%x;
if(!v) return x;
if(num%k==0){
int d=__gcd(v,x);
if(d>1) return d;
}
}
int d=__gcd(v,x);
if(d>1) return d;
}
}
}Pr;
inline void fac(int x){
if(x==1) return ;
if(Test.is_pri(x)){
cout<<x<<' ';
return ;
}
int now=x; while(now==x) now=Pr.gt_div(x);
x/=now;
fac(x); fac(now);
}

浙公网安备 33010602011771号