事实上很早之前就遇到过,只不过没有真正做过几道题。
现在把模板总结一下:Miller-Rabin 及 Pollard-Rho的优化
以及long long相乘
的标准写法。
Miller-Rabin
在普通的费马小定理的基础上新加了这么一个引理用于判断,若
x
2
≡
1
(
m
o
d
p
)
x^2\equiv1(\mod p)
x2≡1(modp),则
x
≡
1
(
m
o
d
p
)
x\equiv 1(\mod p)
x≡1(modp)或
x
≡
−
1
(
m
o
d
p
)
x\equiv -1(\mod p)
x≡−1(modp)。于是只要选取判断的质数
a
=
2
,
3
,
5
,
7
,
11
,
13
,
17
,
19
,
23
,
29
,
31
,
37
a=2,3,5,7,11,13,17,19,23,29,31,37
a=2,3,5,7,11,13,17,19,23,29,31,37即可在
N
<
18
,
446
,
744
,
073
,
709
,
551
,
616
=
2
64
N < 18,446,744,073,709,551,616 = 2^{64}
N<18,446,744,073,709,551,616=264内保证正确性。
Pollard-Rho
的优化也比较简单:由于gcd的时间复杂度比较大,我们可以每次多把几个
a
b
s
(
x
−
y
)
abs(x-y)
abs(x−y)乘在一起进行判断,注意如果
g
c
d
=
n
gcd=n
gcd=n时需要倒回去一步步来。
#define ll long long
ll times(ll a, ll b, ll P) {
a = a >= P ? a % P : a;
b = b >= P ? b % P : b; //注意先模,不然精度会有问题
ll c = (ld) a * b / P; //转一下long double,前20位没有问题
return ((a * b - c * P) % P + P) % P; //P不要太大,10^18内没有问题
}
bool Miller_Rabin(ll n) {
F(i, 1, z[0]) {
if (z[i] == n) return 1;
if (z[i] > n) return 0;
ll tmp = ksm(z[i], n - 1, n), tnp = n - 1;
if (tmp != 1)
return 0;
while (tmp == 1 && tnp % 2 == 0) {
tnp /= 2;
tmp = ksm(z[i], tnp, n);
if (tmp != 1 && tmp != n - 1)
return 0;
}
}
return 1;
}
ll work(ll n, ll c) {
if (n % 2 == 0) return 2;
ll x = 2, y = 2;
while (1) {
x = times(x, x, n) + c;
y = times(y, y, n) + c;
y = times(y, y, n) + c;
if (x == y) break; //判环
ll d = gcd(abs(x - y), n); //由于gcd的时间复杂度比较大,我们可以每次多把几个abs(x-y)乘在一起进行判断,注意如果d=n时需要倒回去一步步来。
if (d > 1) return d;
}
return 0;
}
void Pollard_Rho(ll n) {
if (Miller_Rabin(n)) {
Q[++ L] = n; //找到一个质因子
return;
}
ll tmp = 0, c = 0;
while (tmp == 0)
tmp = work(n, ++ c);
Pollard_Rho(tmp), Pollard_Rho(n / tmp);
}