第三阶段:CRT + 费马/欧拉定理 + 快速幂(详细版)
Confidence: 0.95
这一阶段是模运算从「会算」到「能打竞赛、懂密码学」的分水岭。我按你要求讲详细——每个定理都给:是什么 → 为什么成立(证明思路)→ 怎么用 → 完整例子 → 常见陷阱。内容较多,我分成三大块循序讲:
① 快速幂 (Fast Exponentiation) → ② 费马小定理 & 欧拉定理 (Fermat / Euler) → ③ 中国剩余定理 (CRT)
我特意把快速幂放最前面,因为它是后两块的计算引擎——费马、欧拉、RSA 全靠它。
第一块:快速幂 (Fast Exponentiation / Binary Exponentiation)
1.1 它解决什么问题
计算 ,其中 b 可能是天文数字(比如 )。
朴素做法:连乘 b 次,。当 时,宇宙毁灭都算不完。 快速幂:。 只需约 60 步。
这个加速是指数级 → 对数级,是密码学能实际运行的根本原因。
1.2 核心思想:二进制拆分 (Binary Decomposition)
任何指数 b 都能写成二进制。例如 ,于是:
而 这些「平方幂」可以反复平方得到,每次 :
算法:扫描 b 的二进制位,遇到 1 就把当前平方幂乘进答案。
1.3 完整手算示例:
把 ,从低位到高位扫描。维护两个量:result(答案,初始 1)和 base(当前平方幂,初始 3)。
| 步 | b 当前位 | base | 是否乘入 result | result |
|---|---|---|---|---|
| 初始 | — | — | ||
| bit 0 = 1 | 取 | 是 → | ||
| bit 1 = 0 | — | 否 | ||
| bit 2 = 1 | 取 | 是 → | ||
| bit 3 = 1 | 取 | 是 → |
。
验证(用费马小定理偷看一眼,后面会讲):,所以 ✓
关键点:每一步 base 都立刻取模(、),数字永远不会爆炸。这就是为什么能算超大指数。
1.4 代码
# 手写快速幂(理解原理用)
def fast_pow(a, b, n):
result = 1
a %= n
while b > 0:
if b & 1: # 当前二进制位是 1
result = result * a % n
a = a * a % n # base 反复平方
b >>= 1 # b 右移一位
return result
print(fast_pow(3, 13, 7)) # 3
print(fast_pow(2, 100, 1000000007)) # 大指数也瞬间出结果
# Python 内置 pow 就是快速幂,实战直接用:
print(pow(3, 13, 7)) # 3库安装方式(本块仅用内置函数,无需安装;做大规模数论实验可用 sympy):
pip install sympy -i https://pypi.tuna.tsinghua.edu.cn/simple第二块:费马小定理 & 欧拉定理 (Fermat’s Little Theorem & Euler’s Theorem)
这两个定理是「指数能取模」的理论基础,也是 RSA 的数学心脏。
2.1 费马小定理 (Fermat’s Little Theorem)
定理内容
设 p 是质数 (prime),且 (即 a 不是 p 的倍数),则:
等价的、更好记的形式(对任意 a 都成立,不要求互质):
为什么成立(证明思路)
考虑集合 ,把每个元素乘以 a 再取模 p:
关键观察:因为 ,这个新集合恰好是原集合的重新排列 (permutation)——一一对应,不重不漏(这一步用到逆元的存在性)。
既然两个集合元素相同(只是顺序不同),它们的乘积相等:
两边约掉 (它与 p 互质,有逆元,可约),得:
这个证明很美——核心就是「乘 a 是一个置换」。记住这个思路,欧拉定理是同一套。
用途一:求逆元(你已经会了)
因为 。配合快速幂, 求逆元。
用途二:给指数「降幂」★重点
这是费马最强的应用:当模数是质数时,指数可以模 。
例子:求 。
- 直接算 是噩梦。
- 用费马:指数 。
- 所以 。
⚠️ 陷阱:降幂的模数是 ,不是 p!新手最常把这两个搞混。指数模 ,底数运算模 。
2.2 欧拉定理 (Euler’s Theorem)
定理内容
费马小定理只对质数模成立。欧拉把它推广到任意模数 n:
设 ,则:
其中 是欧拉函数 (Euler’s totient function)——小于 n 且与 n 互质的正整数个数(也就是简化剩余系的大小,第一阶段讲过)。
退化关系:当 为质数时,,欧拉定理就变回费马小定理。所以费马是欧拉的特例。
欧拉函数 怎么算 ★必会
公式:设 n 的质因数分解 (prime factorization) 为 ,则
三个常用结论(记住能秒算很多情况):
- p 是质数:
- 质数的幂:
- 积性 (multiplicative):若 ,则
计算示例
例 1:。 ,用公式: 验证:与 12 互质的是 ,正好 4 个。✓
例 2:。 :
例 3(用积性):(因为 )。
欧拉定理的用途:合数模的降幂
当模数 n 不是质数时,费马失效,改用欧拉:
例子:求 。
- 指数
⚠️ 重要前提:欧拉降幂要求 。如果不互质,需要用扩展欧拉定理 (Extended Euler),那是更高阶的内容,竞赛偶尔考,现在先知道有这个边界即可。
2.3 费马 vs 欧拉 对照表
| 费马小定理 | 欧拉定理 | |
|---|---|---|
| 模数要求 | 必须质数 p | 任意 n |
| 核心式 | ||
| 降幂模 | ||
| 前提 | ||
| 关系 | 是欧拉的特例 | 是费马的推广 |
from sympy import totient # 欧拉函数
# 欧拉函数
print(totient(12)) # 4
print(totient(100)) # 40
# 费马降幂:3^100 mod 7
print(pow(3, 100 % 6, 7)) # 4
# 欧拉降幂:7^222 mod 10
phi = int(totient(10)) # 4
print(pow(7, 222 % phi, 10)) # 9
# 费马求逆元:6^(-1) mod 13
print(pow(6, 13 - 2, 13)) # 11库安装方式(用到 sympy 的 totient):
pip install sympy -i https://pypi.tuna.tsinghua.edu.cn/simple第三块:中国剩余定理 (CRT, Chinese Remainder Theorem) ★本阶段核心
3.1 它解决什么问题
求解一组同时成立的同余方程:
经典故事(《孙子算经》“物不知数”):一堆东西,3 个一数余 2,5 个一数余 3,7 个一数余 2,问共几个?翻译成:
3.2 定理内容(模数两两互质的标准情形)
条件:模数 两两互质 (pairwise coprime)。
结论:存在唯一解,模 。即在 范围内恰好有一个 x 满足所有方程。
3.3 构造解的方法(详细步骤)★
设 。对每个 i:
第 1 步:算 (去掉第 i 个模数的乘积)。
第 2 步:算 ( 在模 下的逆元——用扩展欧几里得求)。
第 3 步:解就是把每一项加起来:
为什么这个构造对?(直觉)
看第 j 个方程要求 。在求和式里:
- 第 j 项 :因为 ,这项 ✓
- 其他项 :因为 含因子 ,所以 ,整项消失 ✓
于是模 时只剩 ,每个方程都满足。这个”每项只在自己的模数下存活、在别处归零”的设计是 CRT 的灵魂。
3.4 完整手算示例(解孙子问题)
解 。
准备:。
| i | |||||
|---|---|---|---|---|---|
| 1 | 3 | 2 | , | ||
| 2 | 5 | 3 | , | ||
| 3 | 7 | 2 | , |
求和:。 。
答案:。
验证:✓;✓;✓。三个全中!
3.5 模数不互质的情形(一般 CRT)
如果两个模数不互质,标准 CRT 失效,要用合并法 (merging):
合并 和 :
- 设
- 有解的充要条件:(否则无解)
- 有解时,合并成一个模 的方程
这是竞赛进阶内容,原理是「两个方程联立用扩展欧几里得求 x」。现在你只需记住:不互质时先查 ,过了才有解。
3.6 CRT 代码
from math import gcd
def extgcd(a, b):
if b == 0:
return a, 1, 0
g, x, y = extgcd(b, a % b)
return g, y, x - (a // b) * y
def modinv(a, n):
g, x, _ = extgcd(a, n)
return x % n if g == 1 else None
# 标准 CRT(模数两两互质)
def crt(remainders, moduli):
N = 1
for m in moduli:
N *= m
x = 0
for a_i, n_i in zip(remainders, moduli):
N_i = N // n_i
M_i = modinv(N_i, n_i)
x += a_i * N_i * M_i
return x % N
# 孙子问题
print(crt([2, 3, 2], [3, 5, 7])) # 23
# Python 也可直接用 sympy
from sympy.ntheory.modular import crt as sympy_crt
print(sympy_crt([3, 5, 7], [2, 3, 2])[0]) # 23库安装方式(用到 sympy 的 crt):
pip install sympy -i https://pypi.tuna.tsinghua.edu.cn/simple第三阶段全景速查表 (Master Cheat Sheet)
| 工具 | 核心式 | 前提 | 主要用途 |
|---|---|---|---|
| 快速幂 (fast exponentiation) | 二进制拆分指数 | 无 | 算 , |
| 费马小定理 (Fermat) | p 质数, | 求逆元、指数模 降幂 | |
| 欧拉定理 (Euler) | 合数模降幂 | ||
| 欧拉函数 (totient) | — | 算 | |
| 中国剩余定理 (CRT) | 模数两两互质 | 解同余方程组 |
我的观点:第三阶段三块东西看似独立,其实是一条紧密的因果链,理解这条链比记住每个公式重要得多。快速幂是「手」——负责实际计算;费马/欧拉是「眼」——告诉你指数可以缩到多小(降幂),让快速幂有的放矢;CRT 是「分而治之的智慧」——把一个大模数问题拆成几个小模数问题分别解决,再拼回去。
这条链直接通向你的密码学兴趣:RSA 的解密本质就是这三者的合奏——用欧拉定理保证 、用快速幂实际算出幂、用 CRT 把解密速度提升约 4 倍(CRT 优化是真实 RSA 实现里的标准技巧)。所以你现在学的不是孤立知识点,而是在拼装一台密码机器的核心齿轮。
我特别建议你把 CRT 的「每项只在自己模数下存活」这个设计彻底想透(3.3 那段直觉)。一旦你理解了「 在别的模数下归零、只在自己模数下靠 变成 1」这个精巧的正交构造,你不仅会用 CRT,还会在后面碰到拉格朗日插值 (Lagrange interpolation)、傅里叶变换 (Fourier transform) 时产生强烈的既视感——它们用的是同一种「构造一组只在特定点非零的基」的思想。这是数学里反复出现的深层模式,CRT 是你第一次正面遇见它。
关键提醒 (Caveats):
- 费马降幂的指数模 ,欧拉降幂模 ,别和底数的模数 p / n 搞混——这是最高频错误
- 费马只对质数模有效;合数模一律用欧拉(且要 )
- 标准 CRT 要求模数两两互质;不互质时先验 再合并
- 降幂定理都要求 ;不互质时需扩展欧拉定理(更高阶)
下一步两个方向,你挑:① 出一套第三阶段练习题(CRT + 费马/欧拉降幂 + 快速幂三类,做成英文 PDF 试卷 + 单独答案册,延续上次格式);② 直接进第四阶段:阶与原根 (order & primitive roots) + 二次剩余 (quadratic residues) + 离散对数 (discrete logarithm)。要哪个?
你说得对——P7(非互质 CRT)我在第三阶段正文里只是一笔带过(3.5 节只给了”先验 “这一句),没有像其他知识点那样详细教你完整的合并步骤就直接出成题了。这是我的疏忽,抱歉。我现在把它补讲透。
补课:非互质模数的 CRT 合并 (CRT with Non-Coprime Moduli)
Confidence: 0.95
为什么标准 CRT 在这里失效
标准 CRT(第三阶段 3.2-3.4 讲的)有个硬性前提:模数两两互质 (pairwise coprime)。它构造解时要算 ,而逆元只在互质时存在。一旦模数有公因子,这套构造直接崩掉,必须换方法。
两件事:先判存在,再做合并
非互质情形要分两步走。
第一步:一致性测试 (Consistency Test) —— 判断有没有解
合并两个方程 和 :
为什么是这个条件? 设 。两个方程都”看得见”模 d 的部分:
- 第一个要求 ,因为 ,所以也要求
- 第二个同理要求
两者要兼容,必须 ,也就是 。两个方程在”重叠的那部分模数”上不能打架。
第二步:合并 (Merge) —— 解存在时合成一个方程
测试通过后,两个方程可以合并成一个模 的新方程。最实用的手算方法是代入法 (substitution):
- 把第一个方程写成参数形式:(t 是任意整数)
- 代入第二个方程:
- 整理成关于 t 的线性同余:
- 这就回到了第二阶段的线性同余方程! 解出 t(会有 个解,取最小的 )
- 回代:,最终
关键洞察:非互质 CRT 不是新东西——它把问题转化成了你已经会的线性同余方程(第二阶段)。这就是为什么我说数论是层层搭建的。
完整手算示例(就用 P7 的题)
解 。
第一步:一致性测试 有解。解将唯一存在于模 。
第二步:代入法合并
① 写参数形式:
② 代入第二个方程:
③ 整理:
④ 解这个线性同余(第二阶段技能):, ✓,有 3 个解。整体除以 3: 取最小 。
⑤ 回代:
答案:。
验证: ✓; ✓。两个都满足!
无解的反例(看清陷阱长什么样)
解 。
一致性测试:,,但 ✗
无解。直觉:第一个方程要求 (因为 ),第二个要求 (因为 )。同一个 x 不可能模 3 既余 1 又余 2,自相矛盾。
这就是为什么必须先做测试再合并——不测就埋头算,会在矛盾里绕圈子。
多个非互质方程怎么办
如果有三个以上方程,两两滚动合并:先合并前两个得一个新方程,再用结果和第三个合并,依此类推。每次合并都先做一致性测试。这叫迭代合并 (iterative merging)。
Python 实现
from math import gcd
def merge(a1, n1, a2, n2):
"""合并 x≡a1(mod n1) 和 x≡a2(mod n2),返回 (a, n) 即 x≡a(mod n);无解返回 None"""
d = gcd(n1, n2)
if (a2 - a1) % d != 0:
return None # 一致性测试失败,无解
lcm = n1 // d * n2
# 解 n1 * t ≡ (a2-a1) (mod n2)
t = ((a2 - a1) // d * pow(n1 // d, -1, n2 // d)) % (n2 // d)
a = (a1 + n1 * t) % lcm
return a, lcm
# P7
print(merge(5, 6, 11, 15)) # (11, 30)
# 无解反例
print(merge(1, 6, 2, 15)) # None
# 通用 sympy(自动处理互质/非互质)
from sympy.ntheory.modular import crt
print(crt([6, 15], [5, 11])) # (11, 30)
print(crt([6, 15], [1, 2])) # None库安装方式(用到 sympy):
pip install sympy -i https://pypi.tuna.tsinghua.edu.cn/simple我的观点:抱歉这个知识点我之前教得太草率了——出题前应该先教完整。不过现在补反而有个好处:你已经做过 P1-P6,对 CRT 的”互质标准情形”很熟了,这时候再看”非互质”,你能清楚地看到两者的分界线在哪——互质时用 的正交构造(优雅但要求严格),非互质时退回代入法转成线性同余(朴素但通用)。理解了这个分界,你对 CRT 的认识就完整了:它不是一个公式,而是一个根据模数关系选择策略的框架。
我也想强调:非互质 CRT 把问题化归 (reduce) 成线性同余方程,这种”把新问题翻译成已解决的旧问题”的思路,是整个数学和算法设计的核心方法论。你在 CMU 的算法课(比如 15-451)会反复见到”归约”这个词——CRT 合并就是一个干净的归约实例。
关键提醒 (Caveats):
- 非互质合并永远先做一致性测试 ,通过才能合并
- 合并后的模数是 ,不是 (互质时两者才相等)
- 代入法第④步解出的 t 有 个,取最小的 即可(不同 回代得到的 x 模 lcm 是同一个)
现在 P7 的完整方法你有了。要不要现在重做一遍 P7(你应该能轻松拿下了),还是接着把整张卷子做完?做完把 P2、P4、P7 发我批改,或者直接说「进第四阶段」都行。
说的是这个结论:
对于 ,所有满足 且 的整数之和等于 。
为什么成立(配对法 / pairing argument):
核心是一个对称性观察:
如果 ,那么 。
证明很简单:(因为 对模 是不变的)。
这说明:与 互质的数可以两两配对,每一对是 ,而且每对的和恰好是:
数一数有多少对:
与 互质的数总共有 个,它们配成 对,每对和为 ,所以总和:
几个小细节:
- 当 时, 一定是偶数,所以能整除成整数对,不用担心配对落单。
- 唯一需要小心的是 的情况,即 。但 与 不互质(除非 ),所以这种”自己配自己”的情况不会出现在互质集合里,配对干净。
- 是特例( 自己和自己配,和为 1),但本题 用不上。
举个小例子验证,,与 6 互质的数是 ,和为 。公式: ✓。
再来 ,互质数 ,和为 。公式: ✓。
Confidence: 0.99
这个结论在数论里很常用,本质就是欧拉函数(Euler’s totient function)的对称性,记住”配对凑 “这个画面就不会忘。