6.1.1 素数的判定
素数的判定
试除法
最常用的判素数方法
一个数如果不是质数,那么一定能被一个小于它的数整除。假设 \(a \mid n\) 那么 \(\frac n a \mid n\)
不妨设 \(a \le \frac n a\) 则有 \(a^2 \le n,\ a \le \sqrt{n}\) ,所以我们枚举所有 \(2 \le x \le \sqrt n\) 的 \(x\) ,看是否 \(x \mid n\) 即可,时间复杂度 \(O(\sqrt n)\)
bool isp (int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
\(kn + i\) 法
还没学会,咕咕咕
\(Miller-Rabin\) 判定法
对于一个很大的数,我们无法用上面提到的方法快速求解它是否是质数,目前比较稳定的大质数判定方法是 \(Miller-Rabin\) 判定法,它的正确率极高(当然是有概率出错的)
下面我们来说说它的原理,它是基于 费马小定理 & 二次探测法 的素数判定法
费马小定理
首先,我们回顾一下费马小定理
对于任意素数 \(p\) ,若 \(a\) 不是 \(p\) 的倍数(\(gcd (a, p) = 1\)),有 \(a^{p - 1} \equiv 1 (\bmod p)\)
那么它的逆命题:若存在某个 \(a\) ,满足 \(gcd (a, p) = 1\), \(a^{p - 1} \equiv 1 (\bmod p)\) ,是否就能说明 \(p \in prime\) ?
并不行,例如 \(2^{341 - 1} \equiv 1 (\bmod 341)\) ,但 341 是个合数,我们把这一类数称作 费马伪素数 ,但好在费马伪素数出现的概率并不高,我们可以多测试几个 \(a\) ,只要存在某个 \(a^{p - 1} \not\equiv 1 (\bmod p)\) ,即可说明 \(p\) 不是素数,如果多组测试都满足,那么 \(p\) 就很有可能是素数
mt19937 eng(time(0));
int randint(int a, int b) // 生成a到b之间的随机数
{
uniform_int_distribution<int> dis(a, b);
return dis(eng);
}
bool is_prime(int x)
{
if (x < 3) // 因为生成的a∈[2,x-1],要求x≥3,所以特判1,2
return x == 2;
for (int i = 0; i < 12; ++i) // 随机测试12个a
{
int a = randint(2, x - 1);
if (qpow(a, x - 1, x) != 1) // qpow是快速幂
return false;
}
return true;
}
这个算法被称为费马素性检验,时间复杂度为 \(O(k\log n)\) ,其中 \(k\) 为测试的次数
在 \(2^{32}\) 以内,它的准确性还可以,但到了 \(2^{64}\) 级别,出错的概率就非常高了,所以在此基础上,有了准确率更高的 \(Miller-Rabin\) 算法
二次探测法
因为费马素性检验的准确性不够高,所以我们需要 二次探测法 来辅助
假设 \(x^2 \equiv 1 \ (\bmod p)\) 且 \(p \in prime\) ,那么 \(x\) 的解为 \(1 \quad or \quad p- 1\)
证明
因为 \(x^2 \equiv 1 \ (\bmod p)\)
所以 \(x^2 - 1 \equiv 0 \ (\bmod p)\)
所以 \((x - 1)(x + 1) \equiv 0 \ (\bmod p)\)
又 \(p \in prime\)
所以 \(x = \pm 1 \ (\bmod p)\)
结合
对于待检测的奇数 \(p\) (偶数直接判掉),我们可以把 \(a^{x - 1}\) 写成 \(a^{2^kd}\) ,我们考虑当 \(p\) 为素数时 \(a^d , a^{2d}, a^{4d},...,a^{2^kd}\) 这样一串数的性质 ,因为 \(a^{2^kd} \equiv 1 \ (\bmod p)\) 所以这段数一定是以 1 结尾 ,它前面的 \(a^{2^{k - 1}d} \equiv \pm 1 \ (\bmod p)\) ,如果是 1 ,那么还可以继续向前,如果是 -1 ,那么前面是什么我们就不能确定了
所以我们可以大致分为两种情况
- \(a^{d} \equiv \pm 1 \ (\bmod p)\) ,那么它后面肯定全是 1 ,我们没有继续检测的必要
- \(a^{d} \equiv x \ (\bmod p)\) ,那么我们还要继续检测,后面的某个位置(不能是最后一个)必须出现 -1 ,然后后面全是 1(如果直接出现了一个 1 ,那么也就是 \(a^{2^id} \equiv 1 \ (\bmod p)\) 并且 \(a^{2^{i - 1}d} \not\equiv \pm 1 \ (\bmod p)\) ,不满足二次探测)
code
bool MR (ll x) {
if (x < 3) return x == 2;
if (x % 2 == 0) return false;
mod = x;
ll d = x - 1, k = 0;
while (!(d & 1)) d >>= 1, k++;
for (int i = 0; i < 7; i++) {
ll a = pw (pri[i], d);
//a == 0 , a 是 p 的倍数
//a == 1 ,后面全都是 1
//a == p - 1,后面全都是 1
if (a <= 1 || a == mod - 1) continue;
for (int j = 1; j <= k; j++) {
a = moji (a, a);
if (a == mod - 1 && j != k) {
a = 1;
break;
}
//出现了 1 但前面没有出现 p - 1
if (a == 1) return false;
}
if (a != 1) return false;
}
return 1;
}