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 ,那么前面是什么我们就不能确定了

image-20250705105300952

所以我们可以大致分为两种情况

  • \(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;
    }
posted @ 2025-08-11 10:18  michaele  阅读(23)  评论(0)    收藏  举报