Miller-Rabin算法

发布时间 2023-09-30 14:47:06作者: 不o凡

原文链接:https://blog.csdn.net/qq_43227036/article/details/100336234

OK,前面已经讲了很多判断素数的方法,在判断一个数是否为素数时我们可以采用试除法,但如要求1-n的范围那么时间复杂度很高,所以有了线性的筛法求素数。
但如果为了判断一个大数是否为素数却要消耗很大的空间,这显然会爆空间,那么该如何有效的判断呢?
这里引入两个知识

费马小定理
费马小定理就是说如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。也可以写做a^p ≡ p (mod p).

二次探测
如果p是一个素数,那么使得x^2 ≡ 1 (mod p)的 x的解只有两种可能,就是x = 1 或者 x = p-1。
证明如下:
x^2 ≡ 1 (mod p)可以转换为 x^2-1 = 0(mod p),然后就化为了(x-1)(x+1) = 0 (mod p), 因此p是整除(x-1)(x+1)。因此x = 1 或者 x = p-1。

那么就可以根据二次探测来检验是否为素数,方法如下

判断n是否为素数
1.根据费马小定理,可以得出a(n-1)%n==1,那么可以把n-1转换成n-1=m*2t,因为n是奇数,所以n-1一定是合数,所以一定可以写成m*2^t,权值m为奇数,否则可以再除以2.
2.从[1,n-1]选取一个数作为a,利用二次探测判断先让y=(am)2,判断y1?
2.1 如果其间y
1,但是x!=1||x!=n-1,那么不满足二次探测,是合数
3.循环t次,如果最后x都不等于1,那么一定是合数
4.因为是检测,所以一次判断不一定准确,我们可以多次循环20次,将误差降到最低。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL qul_mul(LL a, LL b, LL mod) {//乘法运算
	LL ans = 0;
	while (b) {
		if (b & 1) ans = (ans + a) % mod;
		a = (a + a) % mod;
		b >>= 1;
	}
	return ans;
}

LL qul_pow(LL a, LL b, LL mod) {//快速幂结合乘法运算
	LL ans = 1;
	while (b) {
		if (b & 1) ans = qul_mul(ans, a, mod)%mod;
		a = qul_mul(a, a, mod) % mod;
		b >>= 1;
	}
	return ans;
}

bool Miller_Rabin(LL n) {
	if (n < 2||!(n&1)) return false;
	if (n == 2) return true;
	int t=0, m=n-1;
	//求n-1=m*2^t
	while (!(m&1)) {
		t++;
		m >>= 1;
	}

	for (int i = 0; i < 20; i++) {
		LL a = rand() % (n - 1) + 1;//[1,n-1]
		LL x = qul_pow(a, m, n);//快速幂
		//判断每一个x^2%n==1?
		for (int j = 0; j < t; j++) {
			LL y = qul_pow(x, 2, n);
			if (y == 1 && x != 1 && x != n - 1) {//如果y==1,但是x不满足二次探测,则是合数
				return false;
			}
			x = y;
		}

		//如果最后x还不等于1,则一定是合数
		if (x != 1) {
			return false;
		}
	}

	return true;
}

int main() {
	int n;
	cin >> n;
	if (Miller_Rabin(n)) {
		cout << "YES\n";
	}
	else cout << "NO\n";
	return 0;
}