Part 7.4:数论算法——从质数到密码学的基石

引言:代码世界里的数学幽灵

当我们在讨论算法时,通常会想到排序、搜索、图论等直观的应用。然而,在这些具体问题的背后,存在一个更古老、更抽象的数学分支,它像一个幽灵一样,深刻地影响着计算机科学的每一个角落——这就是**数论 (Number Theory)**。

你可能会问,研究整数性质的数论,与编程有什么关系?关系重大!

  • 你每次访问 https:// 网站,背后都有基于数论的 RSA 或 ECC 加密算法在保护你的数据安全。
  • 你使用的哈希表,其哈希函数的设计往往需要数论知识来减少冲突
  • 分布式系统中的唯一 ID 生成、游戏中的随机数,甚至高性能计算中的某些优化,都离不开数论的智慧。

为什么数论算法如此重要?

  • 面试价值:在顶级公司的面试中,数论问题是区分候选人数学思维和底层能力的重要工具。涉及质数、最大公约数、模运算、快速幂等问题屡见不鲜。
  • 学术与工程价值
    • 密码学基石:没有数论,就没有现代公钥密码体系,整个互联网的安全大厦将不复存在。
    • 算法竞赛:数论是 ACM/ICPC 等算法竞赛中的一个独立且重要的分支。
    • 底层优化:理解数论可以帮助你设计出更高效、更巧妙的算法来解决特定问题。

本文将为你揭开数论在算法世界中的面纱,从最基础、最核心的三个问题入手:如何高效判断和筛选质数?如何快速计算两个数的最大公约数?以及如何处理天文数字级别的幂运算

背景知识:模运算的钟表世界

在深入数论算法之前,我们必须掌握一个核心概念:**模运算 (Modular Arithmetic)**。

直觉:钟表算术

模运算就像一个钟表。假设现在是 10 点,4 个小时后是几点?10 + 4 = 14,但在 12 小时的钟表上,14 点就是 2 点。这个过程就是 14 mod 12 = 2

严谨原理:同余关系

a ≡ b (mod m),读作 “a 同余于 b 模 m”,意味着 ab 除以 m余数相同

核心性质:模运算的美妙之处在于,加、减、乘运算都可以在模的世界里“封闭”进行,这对于防止计算结果溢出至关重要。

  • (a + b) mod m = ((a mod m) + (b mod m)) mod m
  • (a - b) mod m = ((a mod m) - (b mod m) + m) mod m (加上 m 是为了防止结果为负)
  • (a * b) mod m = ((a mod m) * (b mod m)) mod m

Part A:质数 (Prime Number) - 构建数字世界的原子

质数是只能被 1 和自身整除的正整数。它们是数论的基石,就像化学中的元素一样。

1. 质数判定:试除法

原理:一个数 n 如果是合数,那么它必然有一个小于或等于 √n 的质因子。

步骤:要判断 n 是否为质数,只需检查从 2√n 的所有整数是否能整除 n

def is_prime(n):
    if n <= 1: return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return False
    return True

时间复杂度O(√n)

2. 质数筛选:埃拉托斯特尼筛法 (Sieve of Eratosthenes)

场景:如果需要找出从 2 到 n 之间的所有质数,对每个数都进行一次试除法太慢了。我们需要一种更高效的“筛选”方法。

直觉:想象一个数字列表。我们从第一个质数 2 开始,把所有 2 的倍数(4, 6, 8…)都划掉。然后找到下一个没被划掉的数 3(它一定是质数),再把所有 3 的倍数(6, 9, 12…)划掉。不断重复这个过程。

步骤分解

  1. 创建一个从 2 到 n 的布尔数组 is_prime,初始值全为 True
  2. p = 2 开始遍历。
  3. 如果 is_prime[p]True,则 p 是一个质数。
  4. p 的所有倍数(2p, 3p, 4p… 直到 n)在 is_prime 数组中标记为 False
  5. 继续寻找下一个 p,重复 3-4 步,直到 p² > n

Python 实现

def sieve_of_eratosthenes(n):
    is_prime = [True] * (n + 1)
    is_prime[0] = is_prime[1] = False
    for p in range(2, int(n**0.5) + 1):
        if is_prime[p]:
            for multiple in range(p*p, n + 1, p):
                is_prime[multiple] = False
    
    primes = [i for i, is_p in enumerate(is_prime) if is_p]
    return primes

时间复杂度O(n log log n),非常接近线性。

Part B:最大公约数 (GCD) - 欧几里得算法的永恒之美

最大公约数 (Greatest Common Divisor) 是指能同时整除两个或多个整数的最大正整数。

欧几里得算法 (Euclidean Algorithm)

这是人类历史上最古老的算法之一,至今仍在广泛使用,其优雅和高效令人惊叹。

核心原理gcd(a, b) = gcd(b, a mod b)

直觉:两个数的最大公约数,也一定是它们中较大数与较小数之差的最大公约数。通过不断用余数代替较大数,可以迅速缩小问题规模。

动画式案例:计算 gcd(48, 18)

  1. gcd(48, 18) -> a=48, b=1848 mod 18 = 12。问题转化为 gcd(18, 12)
  2. gcd(18, 12) -> a=18, b=1218 mod 12 = 6。问题转化为 gcd(12, 6)
  3. gcd(12, 6) -> a=12, b=612 mod 6 = 0。问题转化为 gcd(6, 0)
  4. b=0 时,a 就是最大公约数。所以 gcd(48, 18) = 6

Python 实现

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a

时间复杂度O(log(min(a, b)))。每次迭代,数字规模至少减半,因此复杂度是对数级的。

Part C:模幂运算 - 快速幂算法

场景:在密码学和组合数学中,我们经常需要计算 (a^b) mod m。如果 ab 都很大,直接计算 a^b 会导致数据溢出,而且非常慢。

快速幂算法 (Exponentiation by Squaring)

核心思想:利用指数的二进制表示来加速计算。

直觉:任何一个数 b 都可以写成 2 的幂次之和。例如 13 = 8 + 4 + 1 = 2³ + 2² + 2⁰
所以 a¹³ = a⁸ * a⁴ * a¹
我们可以通过反复平方 a 来得到 , , a⁴, a⁸… 然后只把 b 的二进制表示中为 1 的那些项乘起来。

动画式案例:计算 3¹³ mod 7

  • b = 13 的二进制是 1101
  • 3¹³ = 3⁸ * 3⁴ * 3¹
b (二进制)操作baseresult
...1101b 为奇数, result *= base33
...110b >>= 1, base *= base9 ≡ 23
...11b 为奇数, result *= base26
...1b >>= 1, base *= base46
...b 为奇数, result *= base424 ≡ 3

最终结果是 3。

Python 实现

def fast_power(base, power, mod):
    result = 1
    base %= mod
    while power > 0:
        # 如果 power 是奇数,需要乘上当前的 base
        if power % 2 == 1:
            result = (result * base) % mod
        # base 自我平方
        base = (base * base) % mod
        # power 整除 2
        power //= 2
    return result

时间复杂度O(log b)。循环次数取决于 b 的二进制位数。

总结

数论算法是计算机科学的“内功心法”,它虽然抽象,但应用极其广泛。掌握基础的数论算法,能让你在解决问题时拥有更广阔的思路和更深刻的洞察力。

本文核心要点

质数
- 试除法O(√n) 判定单个质数。
- 埃氏筛O(n log log n) 筛选出一定范围内的所有质数。

最大公约数
- 欧几里得算法O(log(min(a, b))) 的高效解法,基于 gcd(a, b) = gcd(b, a mod b)

模幂运算
- 快速幂算法O(log b) 计算 (a^b) mod m,避免溢出和超时。

扩展与进阶

  • 扩展欧几里得算法:求解 ax + by = gcd(a, b) 的整数解,用于计算模逆元。
  • 费马小定理与欧拉定理:在密码学中有重要应用,是 RSA 算法的理论基础。
  • 中国剩余定理:解决一组同余方程。
  • Miller-Rabin 素性检验:一种高效的随机化算法,用于检验大数是否为质数。

在下一篇文章中,我们将探讨一个在信号处理和多项式计算中至关重要的分治算法——**快速傅里叶变换 (FFT)**,看看它是如何将某些计算的复杂度从 O(n²) 奇迹般地降到 O(n log n) 的。