[Luogu4718] 【模板】Pollard-Rho算法 [Pollrd-ρ][Miller-Rabbin]

Link
https://www.luogu.org/problemnew/show/P4718


Miller-Rabbin
用于快速检测一个大数 p p p 是否为素数。

0 &lt; a &lt; p 0&lt;a&lt;p 0<a<p
大家都知道费马小定理 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod{p} ap11(modp) p p p 是素数
就有人猜想,只要存在 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod{p} ap11(modp) 那么 p p p 就是素数……当然这是错误的
那有人猜想,只要任意 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod{p} ap11(modp) 那么 p p p 就是素数……但是这也是错误的
但是起码反例变少了?

二次探测: x 2 ≡ 1 ( m o d p ) x^2\equiv1\pmod{p} x21(modp) p p p 是素数, 0 ≤ x &lt; p 0\le x&lt;p 0x<p ) 的解有且只有 x 1 = 1 , x 2 = p − 1 x_1=1,x_2=p-1 x1=1,x2=p1
利用二次探测定理,如果存在 x 2 ≡ 1 ( m o d p ) x^2\equiv1\pmod{p} x21(modp) x x x ± 1 \pm1 ±1 那么 p p p 不是素数

然后每次随机一个 a a a ,先费马 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod{p} ap11(modp)
然后再根据 a p − 1 2 k a^{\frac{p-1}{2^k}} a2kp1 a p − 1 2 k − 1 a^{\frac{p-1}{2^{k-1}}} a2k1p1 二次探测。
比较常见的写法是选取前 ⑨ 个素数(2 ~ 23)作为 a a a 来探测。(还可以(比较)快速判掉小的情况

具体步骤:随机 a a a ,逐个除去 p − 1 p-1 p1 2 2 2 ,判断相邻两个是否满足二次探测


Pollard-ρ
分解大合数 N N N

生日悖论的思想核心在于组合
选取 x 1 ⋯ x n x_1\cdots x_n x1xn ;如果 ( ∣ x i − x j ∣ , N ) ∈ ( 1 , N ) (|x_i-x_j|,N)\in(1,N) (xixj,N)(1,N) 就找到了一个 N N N 的因数 ∣ x i − x j ∣ |x_i-x_j| xixj
x i x_i xi 完全由 x i − 1 x_{i-1} xi1 决定那么显然一定会循环。根据生日悖论循环节期望 O ( p ) O(\sqrt{p}) O(p )
我们取 j = 2 i j=2i j=2i ,枚举 i i i ,假设现在 N N N 没被筛出来的最小质因数 p p p
(之所以考虑最小的那个,是因为……模它循环节最小啊,先搞出它的概率大)
并且 y u = x u % p y_u=x_u\%p yu=xu%p 循环得一定(大概?)比 x u x_u xu 早,于是就有 x i = k 1 p + y 1 , x 2 i = k 2 p + y 2 x_i=k_1p+y_1,x_{2i}=k_2p+y_2 xi=k1p+y1,x2i=k2p+y2
这个时候求 ( ∣ x i − x j ∣ , N ) (|x_i-x_j|,N) (xixj,N) 就可以得到 p p p 了。
因为 y y y 循环节期望 p \sqrt{p} p 并且 y y y 期望大约 ≤ N \le\sqrt{N} N 所以期望是 O ( N 1 / 4 ) O(N^{1/4}) O(N1/4)

在 Pollard-ρ 里面,我们选取 x 1 = 2 x_1=2 x1=2 ,然后 x i ≡ x i − 1 2 + α x_i\equiv x_{i-1}^2+\alpha xixi12+α ,这样的话咱没法判断出 2 2 2 所以得特判
(没法判断出 2 2 2 的证明:注意到模 4 4 4 意义下只会产生 α + 1 \alpha+1 α+1 或者 α \alpha α

一个可以优化的点在于注意到我们继续枚举 i i i 当且仅当 ( ∣ x i − x j ∣ , N ) = 1 (|x_i-x_j|,N)=1 (xixj,N)=1
一般采用批量 gcd 来加速,一般似乎是取 N 10 \sqrt[10]{N} 10N 作为一次批量求 gcd 的步长
可以有效降低复杂度(的确是的。

具体步骤:代码清楚可爱


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12], *frS=frBB, *frT=frBB;
template<typename T>
inline void read(T& x)
{
    x=0;char ch=getchar();bool w=0;
    while(!isdigit(ch))w|=(ch=='-'),ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    w?(x=-x):0;
}
inline long long gcd(long long a, long long b)
{
    return !b?a:gcd(b,a%b);
}
inline void exgcd(long long a, long long b, long long& d, long long& x, long long& y)
{
    if (!b) { d = a; x = 1; y = 0; return;}
    exgcd(b, a%b, d, y, x); y -= x * (a / b);
}
inline long long ReMul(long long a, const long long b, const long long& p)
{
    static long long t;
    t = (long double) a * b / p;
    return ((a * b - t * p) % p + p) % p;
}
inline long long qpowm(long long a, long long b, const long long& p)
{
    if (b <= 0) return 1;
    long long ret = 1;
    while (b)
    {
        if (b & 1) ret = ReMul(ret, a, p);
        a = ReMul(a, a, p);
        b >>= 1;
    }
    return ret;
}
long long Prime[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
long long PowList[80];
inline bool Miller(const long long& x)
{
    long long tem;
    for (register int i = 0; i < 9; ++i)
    {
        if (Prime[i] == x) return true; // == 2, 3, 5, 7, 11, 13, 17, 19, 23
        if (Prime[i] > x) return false; // != 2, 3, 5, 7, 11, 13, 17, 19, 23
        tem = x - 1;
        PowList[0] = 0;
        while (tem % 2 == 0) ++PowList[0], tem /= 2;
        PowList[++PowList[0]] = qpowm(Prime[i], tem, x);
        for (register int j = PowList[0] - 1; j >= 1; --j)
        {
            PowList[j] = ReMul(PowList[j+1], PowList[j+1], x);
        }
        for (register int j = 1; j <= PowList[0]; ++j)
        {
            if (PowList[j] == x - 1) break;
            if (PowList[j] != 1) return false;
        }
    }
    return true;
}
#define SeMul(a, b) a = ReMul(a, a, b)
inline long long IdAbs(const long long& x)
{
    return (x<0)?(-x):x;
}
inline long long Pollard_Find(const long long& N, const int& Step, const int& Alpha)
{
    if (!(N&1)) return 2;
    register long long LastX, LastY, x = 2, y = 2, Prod;
    while (true)
    {
        LastX = x, LastY = y, Prod = 1;
        for (register int i = 1; i <= Step; ++i)
        {
            SeMul(x, N), x += Alpha; (x>=N)?(x-=N):0;
            SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
            SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
            Prod = ReMul(Prod, IdAbs(x - y), N);
        }
        Prod = gcd(N, Prod);
        if (Prod == 1) continue;
        if (Prod != N) return Prod; // Prod(Before GCD) == 0
        x = LastX, y = LastY;
        for (register int i = 1; i <= Step; ++i)
        {
            SeMul(x, N), x += Alpha; (x>=N)?(x-=N):0;
            SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
            SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
            Prod = gcd(N, IdAbs(x - y));
            if (Prod == 1) continue;
            if (Prod == N) return 0;
            return Prod;
        }
        
        //WHY
        exit(1);
    }
}
inline long long Pollard_Init(const long long& N)
{
    if (Miller(N)) return N;
    int Step = pow(N, 0.1), Alpha = 1; long long P = 0;
    while (!P) P = Pollard_Find(N, Step, Alpha), ++Alpha;
    return max(Pollard_Init(P), Pollard_Init(N/P));
}
int main()
{
    int T;
    long long n, p;
    read(T);
    while(T--)
    {
        read(n);
        p = Pollard_Init(n);
        if (p == n) puts("Prime");
        else printf("%lld\n", p);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值