事实上很早之前就遇到过,只不过没有真正做过几道题。
现在把模板总结一下: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);
}