(本文大量参考算法竞赛进阶指南)

0、定义

质数定义为:若一个大于 $1$ 的正整数,无法被除了 $1$ 和它自身以外的其他任何正整数整除,即称该数为质数。

相应的,剩下的正整数,除了 $1$ 之外,称为合数。

应当注意的一点:质数的数量不多,分布稀疏,对于一个足够大的正整数 $N$,不超过 $N$ 的质数大约有 $\frac{N}{\ln N}$ 个,即每 $\ln N$ 个正整数中,大约有一个质数。

 

1、素数判定

1.1、试除法

对于要判定的正整数 $N$,用 $2 \sim \lfloor  \sqrt{N} \rfloor$ 进行试除,这是很朴素自然的一种做法,时间复杂度 $O(\sqrt(N))$。

1.2、费马素性检验

费马小定理:如果 $p$ 是一个质数,且正整数 $a$ 与 $p$ 互质,则有 $a^{p-1} ≡ 1 \pmod p$。

这个定理的逆命题,在一定程度上可以用来检测素数,存在反例:

虽然 $2^{340} \bmod 341 = 1$,但 $341=11 \times 31$。后来,人们又发现了 $561, 645, 1105$ 等数都表明 $a=2$ 时费马小定理的逆命题不成立。因此,引出伪素数的概念:若 $n$ 能整除 $2^{n-1}-1$(换句话说,$2^{n-1}-1$ 能被 $n$ 整除),但同时 $n$ 又是合数,那么 $n$ 就是伪素数。因此,伪素数不一定是素数,不满足 $2^{p-1} \bmod p = 1$ 的一定是合数。

当然,$a=3,4,5,\cdots$ 时,又会有不一样的情况,因此称:满足 $a^{n-1} \bmod n = 1$的合数 $n$ 叫做以 $a$ 为底的伪素数

因此,随机选择若干个小于待测数的正整数作为底数 $a$ 进行若干次测试,只要有一次没有通过测试就立刻可判定为合数。这就是费马素性检验。

再有关于卡迈克尔数的定义:对于合数 $n$,如果对于所有满足与 $n$ 互质的正整数 $a$,都有同余式 $a^{n-1} ≡ 1 \pmod n $ 成立,则合数 $n$ 为卡迈克尔数。尽管卡迈克尔数很少(在 $1 \sim 1e8$ 范围内的整数中,只有 $255$ 个卡迈克尔数),但依然使得我们无法使用费马素性检验。

1.3、Miller-Rabin素性检验

定理:如果 $p$ 是素数,$x$ 是小于 $p$ 的正整数,且 $x^2 ≡ 1 \pmod p$,那么要么 $x=1$,要么 $x=p-1$。

证明:因为 $x^2 ≡ 1 \pmod p$ 相当于 $x^2 - 1$ 能被 $p$ 整除,也即 $p$ 能整除 $(x+1)(x-1)$。由于 $p$ 是素数,那么只可能是 $x-1$ 能被 $p$ 整除,此时 $x=1$,或 $x+1$ 能被 $p$ 整除,此时 $x=p-1$。

因此,将上述定理应用于伪素数 $341$ 的费马素性检验:

由于 $2^{340} \bmod 341 = 1$,即 $(2^{170})^2 ≡ 1 \pmod{341}$,假定 $341$ 是素数,那么应当有 $2^{170} ≡ \pm 1 \pmod{341}$,算得 $2^{170} \bmod 341$ 确实等于 $1$ 时,我们可以继续查看 $2^{85} \bmod 341$ 的结果。我们发现,$2^{85} \bmod 341=32$,因此 $341$ 不是素数。

Miller-Rabin素性检验同样有较小的概率把合数错判为质数,我们把可以通过以 $a$ 为底的Miller-Rabin素性检验的合数称作以 $a$ 为底的强伪素数

给出一个Miller-Rabin素性检验模板:

namespace MR
{
    int test[4]={2,3,5,7};
    ll Pow(ll a,ll n,ll mod)
    {
        ll res=1, base=a%mod;
        while(n)
        {
            if(n&1) res*=base, res%=mod;
            base*=base, base%=mod;
            n>>=1;
        }
        return res%mod;
    }
    bool Test(int p)
    {
        if(p<=1) return 0;

        int t=p-1, cnt=0;
        while(t%2==0) t>>=1, cnt++;

        for(int i=0;i<4;i++)
        {
            if(p==test[i]) return 1;
            ll now=Pow(test[i],t,p), nxt;
            for(int j=1;j<=cnt;j++)
            {
                nxt=(now*now)%p;
                if(nxt==1 && now!=1 && now!=p-1) return 0;
                now=nxt;
            }
            if(now!=1) return 0;
        }
        return 1;
    }
}
View Code

相关文章:

猜你喜欢
  • 2021-08-25
  • 2022-12-23
  • 2022-03-02
  • 2021-12-12
  • 2019-09-05
相关资源
相似解决方案