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

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


Miller-Rabbin
用於快速檢測一個大數 pp 是否爲素數。

0<a<p0<a<p
大家都知道費馬小定理 ap11(modp)a^{p-1}\equiv1\pmod{p}pp 是素數
就有人猜想,只要存在 ap11(modp)a^{p-1}\equiv1\pmod{p} 那麼 pp 就是素數……當然這是錯誤的
那有人猜想,只要任意 ap11(modp)a^{p-1}\equiv1\pmod{p} 那麼 pp 就是素數……但是這也是錯誤的
但是起碼反例變少了?

二次探測:x21(modp)x^2\equiv1\pmod{p}pp 是素數, 0x<p0\le x<p ) 的解有且只有 x1=1,x2=p1x_1=1,x_2=p-1
利用二次探測定理,如果存在 x21(modp)x^2\equiv1\pmod{p}xx±1\pm1 那麼 pp 不是素數

然後每次隨機一個 aa ,先費馬 ap11(modp)a^{p-1}\equiv1\pmod{p}
然後再根據 ap12ka^{\frac{p-1}{2^k}}ap12k1a^{\frac{p-1}{2^{k-1}}} 二次探測。
比較常見的寫法是選取前 ⑨ 個素數(2 ~ 23)作爲 aa 來探測。(還可以(比較)快速判掉小的情況

具體步驟:隨機 aa ,逐個除去 p1p-122 ,判斷相鄰兩個是否滿足二次探測


Pollard-ρ
分解大合數 NN

生日悖論的思想核心在於組合
選取 x1xnx_1\cdots x_n ;如果 (xixj,N)(1,N)(|x_i-x_j|,N)\in(1,N) 就找到了一個 NN 的因數 xixj|x_i-x_j|
xix_i 完全由 xi1x_{i-1} 決定那麼顯然一定會循環。根據生日悖論循環節期望 O(p)O(\sqrt{p})
我們取 j=2ij=2i ,枚舉 ii ,假設現在 NN 沒被篩出來的最小質因數 pp
(之所以考慮最小的那個,是因爲……模它循環節最小啊,先搞出它的概率大)
並且 yu=xu%py_u=x_u\%p 循環得一定(大概?)比 xux_u 早,於是就有 xi=k1p+y1,x2i=k2p+y2x_i=k_1p+y_1,x_{2i}=k_2p+y_2
這個時候求 (xixj,N)(|x_i-x_j|,N) 就可以得到 pp 了。
因爲 yy 循環節期望 p\sqrt{p} 並且 yy 期望大約 N\le\sqrt{N} 所以期望是 O(N1/4)O(N^{1/4})

在 Pollard-ρ 裏面,我們選取 x1=2x_1=2 ,然後 xixi12+αx_i\equiv x_{i-1}^2+\alpha ,這樣的話咱沒法判斷出 22 所以得特判
(沒法判斷出 22 的證明:注意到模 44 意義下只會產生 α+1\alpha+1 或者 α\alpha

一個可以優化的點在於注意到我們繼續枚舉 ii 當且僅當 (xixj,N)=1(|x_i-x_j|,N)=1
那可以批量 gcd 來玄學加速。效率貌似不錯?一般似乎是取 N10\sqrt[10]{N} 作爲一次批量求 gcd 的步長
(雖然看上去貌似“只省了”求 gcd 而且直觀感覺上好像還多算了;不過搞 gcdgcd 其實挺慢的(大鈣)

具體步驟:代碼清楚可愛


#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;
        if (Prime[i] > x) return false;
        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(19260817);
    }
}
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章