Link
https://www.luogu.org/problemnew/show/P4718
Miller-Rabbin
用于快速检测一个大数 p p p 是否为素数。
0 < a < p 0<a<p 0<a<p
大家都知道费马小定理 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod{p} ap−1≡1(modp) 当 p p p 是素数
就有人猜想,只要存在 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod{p} ap−1≡1(modp) 那么 p p p 就是素数……当然这是错误的
那有人猜想,只要任意 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod{p} ap−1≡1(modp) 那么 p p p 就是素数……但是这也是错误的
但是起码反例变少了?
二次探测: x 2 ≡ 1 ( m o d p ) x^2\equiv1\pmod{p} x2≡1(modp) ( p p p 是素数, 0 ≤ x < p 0\le x<p 0≤x<p ) 的解有且只有 x 1 = 1 , x 2 = p − 1 x_1=1,x_2=p-1 x1=1,x2=p−1
利用二次探测定理,如果存在 x 2 ≡ 1 ( m o d p ) x^2\equiv1\pmod{p} x2≡1(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} ap−1≡1(modp)
然后再根据 a p − 1 2 k a^{\frac{p-1}{2^k}} a2kp−1 和 a p − 1 2 k − 1 a^{\frac{p-1}{2^{k-1}}} a2k−1p−1 二次探测。
比较常见的写法是选取前 ⑨ 个素数(2 ~ 23)作为 a a a 来探测。(还可以(比较)快速判掉小的情况
具体步骤:随机 a a a ,逐个除去 p − 1 p-1 p−1 的 2 2 2 ,判断相邻两个是否满足二次探测
Pollard-ρ
分解大合数 N N N 。
生日悖论的思想核心在于组合
选取 x 1 ⋯ x n x_1\cdots x_n x1⋯xn ;如果 ( ∣ x i − x j ∣ , N ) ∈ ( 1 , N ) (|x_i-x_j|,N)\in(1,N) (∣xi−xj∣,N)∈(1,N) 就找到了一个 N N N 的因数 ∣ x i − x j ∣ |x_i-x_j| ∣xi−xj∣ 。
令 x i x_i xi 完全由 x i − 1 x_{i-1} xi−1 决定那么显然一定会循环。根据生日悖论循环节期望 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) (∣xi−xj∣,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 xi≡xi−12+α ,这样的话咱没法判断出 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 (∣xi−xj∣,N)=1
一般采用批量 gcd 来加速,一般似乎是取 N 10 \sqrt[10]{N} 10N 作为一次批量求 gcd 的步长
可以有效降低复杂度(的确是的。
具体步骤:代码清楚可爱
#include
#include
#include
#include
#include
#include
#include
#include
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;
}