素数检测的进阶方法——米勒-罗宾素性测试

前言

一般情况下,判断素数常用的方法是试除法。它的时间复杂度是 \(O(\sqrt{V})\)。( \(V\) 是待检测数的大小)。该方法多数情况下是够用的,但在一些情况下仍然不够(比如下面的情况),会超时。这时就需要进阶的方法了。

  • 判断\(n\)个整数是不是素数,(\(1 \leq n \leq 10^5\)\(1 \leq a_i \leq 10^9\)

米勒-罗宾素性测试(Miller-Rabin primality test)用于判断一个给定的数是否为素数。它的原理是费马小定理和二次探测定理。

米勒-罗宾素性测试是一种概率性算法,它通过对检测数 \(n\) 用多个底数进行检测,如果对于某个底数 \(a\),判断条件不成立,那么 \(n\) 一定是合数;否则,\(n\) 可能是素数。

但是在特定的底数组合下,米勒-罗宾素性测试是可以实现确定性检测,不会测错。
对于小于 \(2^{64}\) 的数,选择特定的底数集合可以使测试变为确定性测试:

  • 对于 32 位整数,进行米勒-罗宾素性测试时使用的底数集合为:\(\{2, 7, 61\}\)
  • 对于 64 位整数,进行米勒-罗宾素性测试时使用的底数集合为:\(\{2, 325, 9375, 28178, 450775, 9780504, 1795265022\}\)

基本原理

米勒-罗宾测试它将 \(n-1\) 分解为 \(2^s \times d\) 的形式(其中 \(d\) 是奇数),然后检查以下条件是否成立:

  • 存在某个 \(r\)\(0 \leq r < s\))使得 \(a^{2^r \times d} \equiv 1 \pmod{n}\)\(a^{2^r \times d} \equiv -1 \pmod{n}\)

如果对于某个底数 \(a\),上述条件不成立,那么 \(n\) 一定是合数;否则,\(n\) 可能是素数。

为什么米勒-罗宾测试的条件可以判断素数?

米勒-罗宾素性测试的有效性基于数论中的两个重要定理:费马小定理和二次探测定理。以下详细解释:

1. 理论基础

费马小定理 (Fermat's Little Theorem)

如果 \(p\) 是一个素数,且 \(a\) 是不可被 \(p\) 整除的整数,那么:

\[a^{p-1} \equiv 1 \pmod{p} \]

二次探测定理 (Quadratic Residue Theorem)

如果 \(p\) 是一个奇素数,那么方程:\(x^2 \equiv 1 \pmod{p}\)
只有两个解:\(x \equiv 1 \pmod{p}\)\(x \equiv -1 \pmod{p}\)

2. 米勒-罗宾测试的推导

对于要测试的数 \(n\),我们首先将 \(n-1\) 分解:

\[n-1 = 2^s \times d \]

其中 \(d\) 是奇数。

根据费马小定理,如果 \(n\) 是素数,那么对于任意与 \(n\) 互质的 \(a\),有:

\[a^{n-1} = a^{2^s \times d} \equiv 1 \pmod{n} \]

构建\(a^r\)序列,让 \(r\) 分别取(\(0 \leq r \leq s\))得到:

\[a^d,\ a^{2d},\ a^{4d},\ \dots,\ a^{2^s d} \pmod{n} \]

这个序列的最后一个数是 \(a^{n-1}\),根据费马小定理,如果 \(n\) 是素数,它应该等于 \(1\)

  • 若序列中的第一个数 \(a^d \equiv 1 \pmod{n}\),则整个序列都是 \(1\)(因每次平方后仍为 \(1\)),符合二次探测定理。
  • 若序列中的第一个数不是 \(1\),因最后一个数是 \(1\),如果要符合二次探测定理,序列中必须出现 \(-1\)(在模意义下为 \(n-1\))。

所以若 \(n\) 是素数,为了符合费马小定理和二次探测定理,必然存在某个 \(r\)\(0 \leq r < s\))使得 \(a^{2^r \times d} \equiv -1 \pmod{n}\)\(a^{2^r \times d} \equiv 1 \pmod{n}\)

因此,若对某个底数 \(a\),该条件均不成立,则 \(n\) 一定是合数。

总结

米勒-罗宾测试通过检查基于费马小定理和二次探测定理推导出的两个条件来判断素数。若 n 是素数,那么这两个条件至少有一个必须成立。如果不成立,那么 n 一定是合数。通过使用多个底数,可以极大地降低错误概率,对于特定范围内的数,甚至可以做到完全确定。

例题

我是素数吗?

参考代码

点击查看代码
int mul(int a,int b,int mod){ // 乘法运算 
	return (a%mod)*(b%mod)%mod;
}

int pow(int a,int b,int mod){ // 快速幂 
	int c=1;
	a%=mod;
	while(b){
		if(b&1) c=mul(a,c,mod);
		a=mul(a,a,mod);
		b>>=1;
	}
	return c;
}

bool is_prime(int n){
	if(n==2) return 1;
	if(n<2||n%2==0) return 0;
	
	int d=n-1,s=0;
	while(d%2==0){
		s++;
		d/=2;
	}
	vector<int> bases={2,7,61};
	for(auto a:bases){
		if(a>=n) continue;
		int x=pow(a,d,n);
		if(x==1||x==n-1) continue;
		bool flag=1;
		for(int i=1;i<=s-1;i++){
			x=mul(x,x,n);
			if(x==1||x==n-1){
				flag=0;
				break;
			}
		} 
		if(flag) return false;
	}
	return true;
}
posted @ 2025-08-19 23:54  _hu  阅读(123)  评论(0)    收藏  举报