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<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;
}