Miller-Rabin及Pollard-Rho 模板

事实上很早之前就遇到过,只不过没有真正做过几道题。

现在把模板总结一下:Miller-Rabin 及 Pollard-Rho的优化以及long long相乘的标准写法。

Miller-Rabin在普通的费马小定理的基础上新加了这么一个引理用于判断,若 x 2 ≡ 1 ( m o d    p ) x^2\equiv1(\mod p) x21(modp),则 x ≡ 1 ( m o d    p ) x\equiv 1(\mod p) x1(modp) x ≡ − 1 ( m o d    p ) x\equiv -1(\mod p) x1(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(xy)乘在一起进行判断,注意如果 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);
}

你可能感兴趣的:(Miller-Rabin及Pollard-Rho 模板)