质数判断方法大全:从入门到高阶(超详细版)

574 阅读12分钟

今天分享一个关于数学中基础概念的问题!

质数(Prime Number),又称素数,是数学中一个非常核心的概念。

⭐ 质数的定义

质数是一个大于 1 的自然数,并且它只有两个正因数(也叫约数),那就是 1 和它本身。


🔍 关键要素解析

  1. 必须是自然数: 质数是 1, 2, 3, 4, \dots$ 等正整数。

  2. 必须大于 1: 数字 1 不是质数(因为它只有一个正因数,就是 1 本身)。

  3. 只有两个正因数:

    • 第一个因数是 1
    • 第二个因数是 它本身

🔢 质数和合数

自然数按照正因数的个数可以分为三类:

类型定义正因数个数示例
质数 (Prime)只有 1 和它本身两个因数。2 个2, 3, 5, 7, 11, 13, 17, 19, ...
合数 (Composite)除了 1 和它本身以外,还有其他因数。3 个或更多4 (因数: 1, 2, 4), 6 (因数: 1, 2, 3, 6), 8, 9, 10, ...
数字 1既不是质数也不是合数。1 个1 (因数: 1)

💡 特别注意

2 是唯一的偶数质数。** 所有其他大于 2 的偶数(如 4, 6, 8...)都能被 2 整除,因此它们至少有 1、2 和自身三个因数,所以都是合数。

💡用 Python 实现一个函数来判断一个数是否为质数,这是一个经典的编程练习。💡

这里是一个简洁高效的 Python 函数:

Python

def is_prime(n):
    """
    判断一个正整数 n 是否为质数。
    
    Args:
        n (int): 需要判断的正整数。
​
    Returns:
        bool: 如果 n 是质数返回 True,否则返回 False。
    """
    # 1. 根据定义,质数必须大于 1
    if n <= 1:
        return False
    
    # 2. 2 是唯一的偶数质数
    if n == 2:
        return True
    
    # 3. 所有大于 2 的偶数都不是质数
    if n % 2 == 0:
        return False
    
    # 4. 检查从 3 开始,到 sqrt(n) 之间的所有奇数
    # 只需要检查到 n 的平方根即可,因为如果 n 有大于 sqrt(n) 的因数 a,
    # 那么它必然有一个小于 sqrt(n) 的因数 b (n = a * b)
    # 并且我们只检查奇数(步长为 2)
    i = 3
    while i * i <= n:
        if n % i == 0:
            return False  # 找到一个因数,所以 n 不是质数
        i += 2  # 只检查奇数
        
    return True  # 循环结束,没有找到其他因数,所以 n 是质数# --- 示例用法 ---
print(f"7 是质数吗? {is_prime(7)}")    # 输出: 7 是质数吗? True
print(f"1 是质数吗? {is_prime(1)}")    # 输出: 1 是质数吗? False
print(f"15 是质数吗? {is_prime(15)}")  # 输出: 15 是质数吗? False
print(f"2 是质数吗? {is_prime(2)}")    # 输出: 2 是质数吗? True
print(f"101 是质数吗? {is_prime(101)}") # 输出: 101 是质数吗? True

🧠 代码解释

  1. 处理特殊情况 (1 和 2): 确保符合质数大于 1 的定义,并处理唯一的偶数质数 2。
  2. 排除偶数: 快速排除所有大于 2 的偶数,节省大量计算时间。
  3. 优化循环范围: 只需检查到 sqrt{n} 即可。
  4. 步进 2: 由于所有偶数已被排除,我们只需检查奇数因数,将循环的步长设置为 2 (i += 2),这样效率更高。

质数判断的方法有很多种,原因不是“数学家喜欢复杂”,而是因为——

不同规模的数字、不同的应用场景,对时间、空间、准确性要求完全不同。

没有一种方法能在所有情况下都“又快又省又好”。

1. 因为数字规模越来越大

● 小数字(如 1 ~ 10000)

用最简单的试除法就够了,0.0001 秒就能判断。

但是——

● 中等数字(10⁶ ~ 10⁹)

试除法就开始变慢了,需要:

  • 成千上万次除法
  • 程序运行几十毫秒甚至更久

因此需要:

  • √n 优化
  • 6k±1

这些能让速度提升 10~100 倍

● 大数字(64-bit,10¹⁸)

用于:

  • 红黑树哈希
  • 安全算法
  • 数据库索引
  • 金融计算

此时简单试除已经做不到,需要:

  • Miller–Rabin(MR)
  • Lucas test

● 超大数字(256-bit,512-bit,1024-bit)

用于:

  • RSA 加密
  • 区块链签名
  • 隐私计算

这些数字大到 试除到地老天荒都跑不完

所以必须用:

  • 高级快速算法(MR,BPSW,AKS)

📌 数字越大 → 算法越复杂
这就是多种算法出现的关键原因。


2. 因为不同应用对“准确率”的要求不同

● 一般编程题

允许偶尔慢一点,但必须 100% 正确
适合:

  • √n
  • 6k±1
  • 埃拉托斯特尼筛

● 密码学(加密)

需要:

  • 超大质数
  • 高速生成
  • 高准确率(几乎 100%)

Miller–Rabin 是概率算法:

错误概率 < 4^-k
一次测试错误率约等于 被雷劈 + 彩票中奖 的概率。

可控制,可忽略。

● 理论研究

数学家想要:

  • 绝对确定性
  • 多项式时间算法

他们不考虑速度快慢,而要“证明质数可判定”。

于是出现了:

  • AKS(Prime = deterministic, polynomial time)

📌 不同领域对“速度 vs 正确性”的要求完全不同


3. 因为判断“一个数是否质数”和“找出大量质数”是两种任务

这是两个完全不同的问题。

任务 A:判断一个数是否为质数

适合:

  • √n
  • 6k±1
  • Miller–Rabin
  • BPSW

一次判断即可 → 注重速度与算法复杂度

任务 B:生成大量质数(如 1~10^7 内全部质数)

试除法太慢了。
必须用:

  • 埃拉托斯特尼筛
  • 线性筛(Euler Sieve)

📌 筛法是批量算法,不适合判断单个大数。
📌 试除法/概率法是单个测试,不适合生成大量质数。

这也导致了两条算法路线。


4. 因为硬件与时代变化,推动算法不断进化

以前(1950s):

  • 计算机算一次除法很慢
  • 数据量小
  • √n 已经够用了

后来(1970s-1980s):

  • 随着密码学发展,需要处理 100 位、200 位、300 位的大数
  • 促成了 Miller–Rabin 等概率算法产生

再后来(2002):

  • 数学家终于证明质数可在 多项式时间 内判断
  • 出现了 AKS 算法(理论上的突破)

📌 每一种算法都是为解决当时代的特定需求而诞生的。


5. 因为没有一种算法可以“在所有情况下最优”

这就是“算法”这个领域里最重要的真理:

没有万能算法,只能在不同场景做最优选择。

例如:

  • √n 对小数很好,但对大数非常慢
  • 筛法对大量小数很好,但不能处理 64-bit 大整数
  • MR 对 64-bit 超快,但不适合生成小质数
  • AKS 是理论最优,但实际比 MR 慢几百万倍

因此必须有多种方案互补。


⭐ 最终总结:为什么质数判断方法这么多?

你可以用一句话记住:

因为质数判断既需要快,又需要准,而“快”和“准”在不同的场景里要求完全不同。
导致没有一种算法能在所有情况下称王,所以才会不断出现新的方法。

简单来说:

  • 小数字 → √n / 6k±1
  • 批量 → 埃氏筛 / 线性筛
  • 大数字 → Miller–Rabin
  • 理论证明 → AKS

汇总一下质数判断方法

1️⃣ 试除法 Trial Division

原理

判断 n 是否有除了 1 和 n 外的因数。

复杂度

  • O(n)

代码

def is_prime_basic(n):
    if n <= 1:
        return False
    for i in range(2, n):
        if n % i == 0:
            return False
    return True

2️⃣ √n 优化

原理

若 n = a × b,则一定有一个因数 ≤ √n。

复杂度

  • O(√n)

代码

def is_prime_sqrt(n):
    if n <= 1:
        return False
    i = 2
    while i * i <= n:
        if n % i == 0:
            return False
        i += 1
    return True

3️⃣ 奇数优化

原理

除了 2 外所有偶数都不是质数。

代码

def is_prime_odd(n):
    if n <= 1:
        return False
    if n % 2 == 0:
        return n == 2
    i = 3
    while i * i <= n:
        if n % i == 0:
            return False
        i += 2
    return True

4️⃣ 6k ± 1 优化

原理

除 2、3 之外的所有质数必为 6k±1。

代码

def is_prime_6k(n):
    if n <= 1:
        return False
    if n <= 3:
        return True
    if n % 2 == 0 or n % 3 == 0:
        return False
    i = 5
    while i * i <= n:
        if n % i == 0 or n % (i + 2) == 0:
            return False
        i += 6
    return True

5️⃣ 埃拉托斯特尼筛 Sieve of Eratosthenes

原理

迭代划掉每个质数的倍数。

代码

def sieve(n):
    is_prime = [True] * (n + 1)
    is_prime[0] = is_prime[1] = False
    p = 2
    while p * p <= n:
        if is_prime[p]:
            for i in range(p*p, n+1, p):
                is_prime[i] = False
        p += 1
    return is_prime

6️⃣ 线性筛 Linear Sieve

原理

每个合数只被最小质因子标记一次。

代码

def linear_sieve(n):
    primes = []
    lp = [0] * (n + 1)
    for i in range(2, n + 1):
        if lp[i] == 0:
            lp[i] = i
            primes.append(i)
        for p in primes:
            if p > lp[i] or i * p > n:
                break
            lp[i * p] = p
    return primes, lp

7️⃣ Miller--Rabin 快速质数测试

原理

利用模幂检测合数特征。

代码

def miller_rabin(n):
    if n < 2:
        return False
    for p in [2,3,5,7,11,13,17,19,23]:
        if n % p == 0:
            return n == p
    d = n - 1
    r = 0
    while d % 2 == 0:
        d //= 2
        r += 1
    def check(a, d, n, r):
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            return True
        for _ in range(r - 1):
            x = (x * x) % n
            if x == n - 1:
                return True
        return False
    for a in [2, 325, 9375, 28178, 450775, 9780504, 1795265022]:
        if a % n == 0:
            continue
        if not check(a, d, n, r):
            return False
    return True

8️⃣ Baillie--PSW

一种极稳健的大整数质数测试组合。


9️⃣ AKS

特点

唯一理论上多项式时间的确定性判定方法,但极慢。


🔟 如何选择

需求 推荐


单个 n < 1e9 6k±1 高频判断 n < 1e10 √n + 奇数优化 批量求质数 埃氏筛 / 线性筛 大数(64bit) Miller--Rabin(确定性) 任意大整数 Miller--Rabin + Baillie--PSW

扩展内容:质数判断的更多深度知识与高级技巧

10. 质数与数论基础:为什么质数这么重要?

质数(Prime Number)是数论的基石,原因包括:

所有整数的“原子”

每个大于 1 的整数都能唯一分解成质数乘积:

n=p1a1p2a2⋯n = p_1^{a_1} p_2^{a_2} \cdotsn=p1a1p2a2⋯

这叫 算术基本定理

现代密码学的根基

RSA 等加密算法建立在:

  • 大质数生成
  • 大整数分解的困难性

因此需要快速判断 “一个 1024-bit 数是不是质数”。

算法与数学中的基础构件

质数与:

  • 离散数学
  • 哈希算法
  • 数学建模
  • 编程竞赛 密切相关。

这就是为何要学习从“简单试除法”到“概率算法”的全部方法。


11. 判断质数的方法选择指南(场景推荐)

不同场景需要不同方法:

场景适用方法
判断一个小数(1e6 以下)是否为质数sqrt(n) 试除法
判断很多小数(例如生成 1~100 万内的质数表)埃拉托斯特尼筛法
判断一个 64 位整数(≤10¹⁸)Miller–Rabin(固定底数,100% 准确)
判断一个非常大的整数(128 位或更多)Miller–Rabin(概率)
生成大量大质数用于加密Miller–Rabin + Maurer’s Algorithm

因此: 编程面试用前几种,密码学与大整数运算用后几种。


12. 连续筛法:埃拉托斯特尼筛的进一步优化

埃氏筛已经非常高效,但还有几种经典进阶:

● 12.1 线性筛(Euler Sieve)

只让每个合数被标记一次(埃氏筛会多次标记)。 复杂度从:

  • 埃氏筛:依然是 O(n log log n)
  • 线性筛:真·O(n)

示例代码(线性筛):

def linear_sieve(n):
    primes = []
    vis = [False] * (n + 1)
    for i in range(2, n + 1):
        if not vis[i]:
            primes.append(i)
        for p in primes:
            if i * p > n:
                break
            vis[i * p] = True
            if i % p == 0:
                break
    return primes

特点:

  • 最适合 生成大量质数
  • 中国 OI(信息奥赛)和 ACM 常用

13. Miller–Rabin(MR)更深入解释:为什么它快?

MR 算法设计思想:

● 13.1 将 n−1 分解成 2ᵏ × d

用快速幂判断:

admod  na^d \mod nadmodn

是不是可能为质数的“证据”。

● 13.2 检查平方序列是否包含 n−1

ad,a2d,a4d,...,a2k−1da^d, a^{2d}, a^{4d}, ..., a^{2^{k-1}d}ad,a2d,a4d,...,a2k−1d

● 13.3 如果所有测试都失败 → 合数

如果某个 a 测试通过 → 很可能是质数

MR 属于 蒙特卡洛概率算法(正确率可控制) 测试底数越多,准确率越高。


14. Lucas Test、AKS 等更多高级方法(了解即可)

● 14.1 Lucas Probably Prime Test

比 MR 更强,但也更复杂。 用于密码学质数生成。

● 14.2 Baillie–PSW Test

结合:

  • Miller–Rabin(固定底数 2)
  • Lucas 测试

至今 没发现反例 是实际应用中最准的“快速质数测试”。

● 14.3 AKS 质数判定法(唯一的多项式时间算法)

复杂度:

O~((log⁡n)6)\tilde{O}((\log n)^6)O~((logn)6)

优点:

  • 完全确定性
  • 多项式时间

缺点:

  • 极慢
  • 不用于实战

了解即可。


15. 质数的分布与概率(为什么试除法足够快)

一个随机大整数 n 为质数的概率约为:

1ln⁡n\frac{1}{\ln n}lnn1

例如:

  • 100 万 ~ 18%
  • 10 亿 ~ 5%
  • 10¹⁸ ~ 2%

这意味着:

🎯 在 √n 范围中找到第一个因数的概率非常高

因此试除法速度比想象中快得多。


16. 进阶技巧:判断质数时减少检查次数

● 16.1 跳过偶数(已提)

只检查奇数 → 速度提高约 2 倍。

● 16.2 使用 6k±1(已提)

任何大于 3 的质数都是 6k±1。 检查数减少到 1/3。

● 16.3 使用预筛(wheel factorization)

跳过所有被已知小质数(2,3,5,7,11)整除的数。

例如 30 轮(2×3×5)的轮结构,可以减少 60% 检查。

超高速算法都会用这个技巧。


17. 完整对比:各方法的“速度 vs. 功能”

方法功能速度场景
简单试除判断单个小 n⭐⭐入门
sqrt 优化判断单个中等 n⭐⭐⭐一般编程
6k±1更快⭐⭐⭐⭐常规优化
埃氏筛求大量质数⭐⭐⭐⭐⭐竞赛、工程
线性筛求大量质数⭐⭐⭐⭐⭐⭐OI/ACM
Miller–Rabin大整数判定(64-bit 精确)⭐⭐⭐⭐⭐⭐实战最强
Lucas / BPSW高精度⭐⭐⭐⭐⭐密码学
AKS理论理论研究

18. 最终总结:如何在你的项目中选择算法?

如果你是:

👶 初学者

用:

  • sqrt(n) 优化
  • 6k±1 即可。

🧑‍💻 竞赛 / 刷题

  • 单个判断:6k±1 或 MR
  • 大量生成:埃氏筛 / 线性筛

🔐 安全 / 加密工程

  • 生成大质数:Miller–Rabin(多底数)
  • 或者 BPSW 测试

📚 数论理论研究

  • AKS