Miller-Rabin素性测试

可以去看本文的部分来源:数论部分第一节:素数与素性测试

在判断一个数是否为质数时,我们通常选择根号 n 的试除法来进行判断

但这是在询问比较少/数据比较小的前提下的

Miller-Rabin 素性测试为我们提供了一种 logn 的做法

我们知道 Fermat 小定理 a^(p - 1) mod p = 1 是对于 p 为质数且 a,p 互质的情况成立的

那么其逆命题成立吗?

过去人们是这样认为的

但由于伪素数的发现,人们终于认识到它是错误的

不过这样的数还是占据少数的

所以我们至少可以用它来先验证出一部分数,尽管这个集合中有伪素数

可以通过多取一些 a 的值来降低错误概率

以上是Fermat素性测试。

那么我们自然会这样想,如果我们考虑了所有的小于 n 的底数 a ,是否就足够区分素数和其他数呢?

很可惜,这样的合数依然存在

他们被称为“Carmichael数

你一定会认为这样极端的伪素数一定很大

然而第一个 Carmichael数仅仅是一个三位数:561

而且前 10 亿个自然数中 Carmichael数有600个之多

Carmichael数的存在说明,我们还需要继续加强素性判断的算法。

Miller和Rabin两个人的工作让Fermat素性测试迈出了革命性的一步,建立了传说中的Miller-Rabin素性测试算法

新的测试基于下面的定理:

  如果 p 是素数,x 是小于 p 的整数,且 x^2 mod p = 1 ,那么要么 x = 1 ,要么 x = p - 1,这是显然的,因为 x^2 mod p = 1 相当于 p 能整除 x^2 - 1,也即p能整除 (x + 1)(x - 1) 。由于 p 是素数,那么只可能是 x - 1 能被 p 整除(此时 x = 1 )或 x + 1 能被 p 整除(此时 x = p - 1 )。

下面用 341 来演示一下上面的定理如何运用到 Fermat定理上:

   2^340 mod 341 = 1  => (2^170)^2 mod 341 = 1

  若 341 素数,则上面方程的解可能是 1 或 340,若等于 1,我们继续上面的变形得:

  (2^85)^2 mod 341 = 1

  然而计算得 2^85 mod 341 = 32

  那么 341 不是素数

这就是 Miller-Rabin 素性测试的方法,通过不断提取指数 p - 1中的因子 2 ,将方程化为 x^2 mod p = 1 的形式, 根据解 只能为 1 或 p - 1 ,若解为 1,继续重复上述过程,解为 p - 1,停止,这个数目前为止我们认为它可能是一个素数,解为其他数,则这个数不是素数

时间复杂度 O(logn)

Miller-Rabin素性测试同样为不确定算法,我们把可以通过以a为底的Miller-Rabin测试的合数称作以a为底的强伪素数(strong pseudoprime)。第一个以2为底的强伪素数为2047。第一个以2和3为底的强伪素数则大到1 373 653。

以上是 Miller-Rabin 素性测试算法。

通常我们使用该算法时,都是要来判断 long long 级别的数字,由于可能在快速幂里乘法的步骤乘爆了,需要快速乘, O(logn),写起来跟快速幂差不多

附上一道例题:hihocoder 1287 数论一·Miller-Rabin质数测试

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
using namespace std;

typedef long long ll;

int m, n;
ll ps[12] = {2ll, 3ll, 5ll, 7ll, 11ll, 13ll, 17ll, 19ll, 23ll, 29ll, 31ll, 37ll};

inline ll fastmul(ll a, ll b, ll mod) {
	ll ans = 0ll;
	while(b) {
		if(b & 1) ans = (ans + a) % mod;
		a = (a << 1) % mod;
		b >>= 1;
	}
	return ans;
}
inline ll fastpow(ll low, ll up, ll mod) {
	ll ans = 1ll;
	low %= mod;
	while(up) {
		if(up & 1ll) ans = fastmul(ans, low, mod);
		up >>= 1;
		low = fastmul(low, low, mod);
	}
	return ans;
}
bool dvdchk(ll low, ll up, ll mod) {
	if(!(up & 1)) {
		ll tmp = fastpow(low, (up >> 1), mod);
		if(tmp == 1ll) return dvdchk(low, (up >> 1), mod);
		else if(tmp == mod - 1ll) return true;
		else return false; 
	}
	return true;
}
inline bool Test(ll a) {
    if(a <= 1) return false;
	if(a == 2ll) return true;
	if(!(a & 1ll)) return false;
	for(int i = 0; i < 12; ++i) {
		if(ps[i] == a) 
			return true;	
		if(fastpow(ps[i], a - 1ll, a) != 1ll) 
			return false;	
		else if(!dvdchk(ps[i], a - 1ll, a)) 
			return false;
	}
	return true;
}

int main() {
	scanf("%d", &m);
	ll num;
	while(m--) {
		scanf("%lld", &num);
		if(Test(num)) puts("Yes");
		else puts("No");
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/xcysblog/p/9261949.html
今日推荐