算法题密码学质数判定的算法你能想到几种?(上)

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动。本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

大家好,我是披着狼皮的🐏。今天给大家介绍几个用于素性测试(primality test)的确定性算法,适用于判断给定整数 n 是否为质数。注意,这里不考虑 01 等其他不是质数也不是合数的数,在实际应用上大家记得自己加 if 判断。

试除法(Trial Division)+ 优化

最直接的方法,遍历从 2 到 n-1 的所有数字,并检查其是否能整除 n,找到了便返回 False

def is_prime(n):
    for x in range(2, n):
        if n % x == 0:
            return False
    return True
复制代码

优化 1

如果 n 是合数,那么必然存在两个约数

p1p_1

p2p_2

p1np2p_1 \le \sqrt{n} \le p_2

n\sqrt{n}

from math import isqrt  // Python 版本 >= 3.8

def is_prime(n):
    for x in range(2, isqrt(n) + 1):
        if n % x == 0:
            return False
    return True
复制代码

优化 2

如果 n 能被一个偶数整除,那么它一定能被 2 整除,合理吧?因此,我们可以跳过除了 2 以外的所有偶数。

from math import isqrt  // Python 版本 >= 3.8

def is_prime(n):
    if n == 2:
        return True
    elif n % 2 == 0:
        return False
        
    for x in range(3, isqrt(n) + 1, 2):
        if n % x == 0:
            return False
    return True
复制代码

优化 3

除了 23,所有质数一定能表达成 6k+16k+5 的形式,k 为一个正整数。

(质数一定能表达成这个形式,但能表达成这个形式的数不一定是质数。)

论证

  • 6k 能被 6 整除,不是质数;
  • 6k+26k+4 能被 2 整除,不是质数;
  • 6k+3 能被 3 整除,不是质数;

那么就只剩下 6k+16k+5 有可能质数了。

注:6k+5 也可以写成 6k-1,因为

51(mod6)5 \equiv -1 \pmod 6

对于有可能是质数的数,我们还得用试除法,范围还能再进一步的缩小 —— 在能表示成 6k+16k+5 的数中寻找因数就可以了。

为什么?

所有的合数都能分解成质数,所以 n 只需要与可能是质数的数(即 6k+16k+5)判断就可以了。

def is_prime3(n):
    if n == 2 or n == 3:
        return True
    # 不能表达成 6k+1 或 6k+5 的数一定不是质数
    if n % 6 != 1 and n % 6 != 5:
        return False
    # 进一步的判断
    for x in range(5, int(sqrt(n)) + 1, 6):
        if n % x == 0 or n % (x + 2) == 0:
            return False
    return True
复制代码

威尔逊定理(Wilson's Theorem)

数论四大定理之一:

当且仅当

nn

是质数时:

(n1)!1(modn)(n - 1)! \equiv -1 \pmod n

简单来说,如果 n 是质数,则 n-1 的阶乘加 1 除以 n 的余数为 0。

论证

{1,2,,n1}\{1, 2, \dots, n-1\}

aa

都会有一个模

nn

的倒数

a{1,2,,n1}a^* \in \{1,2,\dots,n-1\}

aa1(modn)aa^* \equiv 1 \pmod n

a=aa = a^*

a21(modn)a210(a1)(a+1)0a{1,n1}\begin{aligned} a^2 &\equiv 1 \pmod n \\ a^2 - 1 &\equiv 0 \\ (a - 1)(a + 1) &\equiv 0\\ \\ a &\in \{1,\,n-1\} \end{aligned}

只有 1 和

n1n-1

{2,3,,n2}\{2,3,\dots,n-2\}

nn

等于

11

的对。

(n1)!1(modn)1×[2×3××(n2)]×(n1)11×1×(n1)1n11n0\begin{aligned} (n-1)! &\equiv -1 \pmod n \\ 1 \times [2 \times 3 \times \dots \times (n - 2)] \times (n-1) &\equiv -1 \\ 1 \times 1 \times (n - 1) &\equiv -1 \\ n - 1 &\equiv -1 \\ n &\equiv 0 \end{aligned}

证明完毕,上代码。

from math import factorial

def is_prime(n):
    return (factorial(n - 1) + 1) % n == 0
复制代码

AKS素性测试(Agrawal–Kayal–Saxena Primality Test)

nn

是质数当且仅当:

nn

能整除

(x+1)n(xn+1)(x + 1)^n - (x^n + 1)

论证

利用二项式定理(binomial theorem),展开原式:

(x+1)n(xn+1)=r=0n(nr)xr(xn+1)=r=1n1(nr)xr\begin{aligned} (x + 1)^n - (x^n + 1) &= \sum_{r=0}^{n}{\binom{n}{r} x^r} - (x^n + 1) \\ &= \sum_{r=1}^{n-1}{\binom{n}{r} x^r} \end{aligned}

可以看得出,

xn+1x^n + 1

(x+1)n(x + 1)^n

nn

是质数当且仅当:

nn

能整除

(nr),r=1,2,,n2,n1\binom{n}{r}, r=1,2,\dots,n-2,n-1

N=(nr)N = \binom{n}{r}

N=n!(nr)!r!n!=N(nr)!r!\begin{aligned} N &= \frac{n!}{(n-r)!\,r!} \\ n! &= N(n-r)!\,r! \end{aligned}

显然

nn

能整除

n!n!

,那么

nn

就能整除

N(nr)!r!N(n-r)!\,r!

pp

不能整除


  • (pr)!(p-r)!

    (pk)<p(p-k)<p

    pp

    是质数;


  • r!r!

    ,因为

    k<pk<p

    pp

    是质数;

那么它一定能整除

NN

这个定理对于质数成立。


nn

为合数时,

cc

nn

的因数,

n=cdn=cd

c,d{1,2,,n1}c,d \in \{1,2,\dots,n-1\}

(nc)=n!(nc)!c!=n(n1)(nc+1)(nc)!(nc)!c!=n(n1)(nc+1)c!\begin{aligned} \binom{n}{c} &= \frac{n!}{(n-c)!\,c!} \\ &= \frac{n(n-1)\dots(n-c+1)\cancel{(n-c)!}}{\cancel{(n-c)!}\,c!} \\ &= \frac{n(n-1)\dots(n-c+1)}{c!} \end{aligned}

如果上方的式子可以被

nn

整除,那么必然会有一个正整数

kk

使得

(nc)=kn\binom{n}{c} = kn

n(n1)(nc+1)c!=knk=(n1)(n2)(nc+1)c!\begin{aligned} \frac{\cancel{n}(n-1)\dots(n-c+1)}{c!} &= k\cancel{n} \\ k &= \frac{(n-1)(n-2)\dots(n-c+1)}{c!} \end{aligned}

让我们看看

kk

是不是整数。

(n1)(n2)(nc+1)(p1)×(p2)××1(modp)(p1)!\begin{aligned} (n-1)(n-2)\dots(n-c+1) &\equiv (p-1)\times(p-2)\times\dots\times1 \pmod p \\ &\equiv (p-1)! \end{aligned}

根据威尔逊定理,

(p1)!1(modp)(p-1)! \equiv -1 \pmod p

kk

不可能是整数。这个定理对于合数不成立。

论证完毕,代码如下:

from math import comb  // Python 版本 >= 3.8

def is_prime(n):
    for r in range(1, n):  // (n-1) + 1 = n
        if comb(n, r) % n != 0:
            return False
    return True
复制代码

优化

(nk)=(nnk)\binom{n}{k}=\binom{n}{n-k}

(n1)=(nn1)\binom{n}{1}=\binom{n}{n-1}

(n2)=(nn2)\binom{n}{2}=\binom{n}{n-2}

from math import comb  // Python 版本 >= 3.8

def is_prime(n):
    for r in range(1, n // 2 + 1):
        if comb(n, r) % n != 0:
            return False
    return True
复制代码

赶紧点赞,不然你下次面试遇到的题目就是这题。

下篇文章将给大家介绍几个效率更高的概率性算法(probabilistic test)。就这样,拜拜。