素性测试算法 Miller-Rabin

费马小定理

p p p 为质数, a a a 为任意自然数, 则
a p ≡ a ( m o d p )    ⟺    a p − 1 ≡ 1 ( m o d p ) a^{p} \equiv a\pmod p \iff a^{p-1} \equiv 1 \pmod p apa(modp)ap11(modp)

证明略。

二次探测定理

p p p 为质数,则 x 2 ≡ 1 ( m o d p ) x^2 \equiv 1 \pmod p x21(modp) 的解为 x 1 = 1 , x 2 = p − 1 x_1 = 1, x_2 = p-1 x1=1,x2=p1

略证:

x 2 ≡ 1 ( m o d p ) x 2 − 1 ≡ 0 ( m o d p ) ( x + 1 ) ( x − 1 ) ≡ 0 ( m o d p ) p ∣ ( x − 1 ) × ( x + 1 ) x^2 \equiv 1 \pmod p \\ x^2 - 1 \equiv 0 \pmod p \\ (x +1)(x-1) \equiv 0 \pmod p \\ p | (x - 1)\times (x+1) x21(modp)x210(modp)(x+1)(x1)0(modp)p(x1)×(x+1)

Miller-Rabin

我们都知道,如果 p p p 为质数那么有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod p ap11(modp)。但反命题不一定成立,比如令 a = 2 a=2 a=2,那么有 2 340   m o d   341 = 1 2^{340} \bmod 341 = 1 2340mod341=1,但是 341 = 11 × 31 341 = 11 \times 31 341=11×31。那么再试一下 a = 3 a=3 a=3,发现 3 340   m o d   341 = 56 3^{340} \bmod 341 = 56 3340mod341=56,所以 341 341 341 不是质数。

那么能不能多选几个质数去做检测呢?但是有一些合数,叫做 Carmichael 数,比如 561 = 3 × 11 × 17 561 = 3\times11 \times17 561=3×11×17,所有合法的底数都无法将它检测出来。

那么接下来就是正题了——Miller-Rabin 素性测试算法。先拿 341 341 341 举个栗子,看看这个算法在干什么。

先说明一个显而易见的命题,对于任意质数 p p p 2 ∣ p − 1 2| p -1 2p1。那么看看 Miller-Rabin 干了什么。

因为 2 340 ≡ 1 ( m o d 341 ) 2^{340} \equiv 1 \pmod {341} 23401(mod341),那么有 2 170 2 ≡ 1 ( m o d 341 ) 2^{{170}^2} \equiv 1 \pmod {341} 217021(mod341),若将 2 170 2^{170} 2170 看作一个整体,根据二次探测定理,如果 341 341 341 是一个质数,那么 2 170   m o d   341 = 1 / 340 2^{170} \bmod 341 = 1 / 340 2170mod341=1/340。可以发现 2 170   m o d   341 2^{170} \bmod 341 2170mod341 1 1 1。那么还适用二次探测定理,再看看 2 85   m o d   341 2^{85} \bmod 341 285mod341,发现它为 32 32 32,所以 341 341 341 不是质数。

int ksm(int a, int k, int p) {
  	int res = 1;
  	while (k) {
    	if (k & 1) res = res * a % p;
    	a = a * a % p;
    	k >>= 1;
  	}
  	return res % p;
}
int MR(int x, int p) {
	if (ksm(x, p-1, p) != 1) return 0;
	int k = p - 1;
	while (!k & 1) {
		k >>= 1;
		int t = ksm(x, k, p);
		if (t == p - 1) return 1;
		if (t != 1) return 0;
	}
	return 1;
}
int RP(int p) {
	if (p == 2 || p == 3 || p == 5 || p == 7 || p == 11) return 1;
	if (p == 0) return 0;
	return MR(2, p) && MR(3, p) && MR(5, p) && MR(7, p) && MR(11, p);
}
int main() {
	while (1) {
		int x = read();
		printf("%d\n", RP(x));
	}
	return 0;
}

可以发现 Miller-Rabin 的时间按复杂度为 O ( k log ⁡ 2 n ) O(k \log^2 n) O(klog2n),其中 k k k 为测试次数,通常五次就够了。

你可能感兴趣的:(素性测试算法 Miller-Rabin)