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);
}
posted @ 2025-01-17 15:19  ~Cyan~  阅读(36)  评论(0)    收藏  举报