第一阶段:模运算入门 (Foundations)

小提示:你打的是 “g根据” 和 “知识” 写成了 “staff”(在 preferences 里)——小事,继续往下。

Confidence: 0.95

这一阶段的目标是让你彻底搞懂「同余」这个核心语言。学完之后,你看到 不再需要翻译,而是直接「感觉」到它的含义。我会把每个概念讲透,配例子和验证。


1. 同余 (Congruence):核心定义

定义

读作「a 同余于 b,模 n」。它的意思是:a 和 b 除以 n 的余数相同,等价地说,它们的差能被 n 整除

为什么有两种说法?

这两个定义是等价的,理解它们的等价性是入门的第一个「啊哈时刻」:

  • 余数相同:设 (余数都是 r)
  • 差被整除:那么 ,显然

例子

最后一个例子很重要:同余对负数也成立 在「模 7 的世界」里是同一个东西。


2. 余数 vs 同余类 (Remainder vs Congruence Class)

这是第一阶段最容易模糊的概念区别,搞清楚它你就赢了一半。

余数 (Remainder)

余数是一个具体的数——一次除法的结果。,这个 2 就是余数,它是唯一的、确定的。

同余类 (Congruence Class)

同余类是一个集合 (set)——所有模 n 同余于某个数的整数的「大家庭」。

模 5 时,与 2 同余的所有整数组成一个同余类,记作

它们全都「长得一样」——除以 5 都余 2。

关键区别 (Key Distinction)

概念是什么例子
余数 (Remainder)一个数 (a number)
同余类 (Congruence class)一个集合 (a set)

类比 (Analogy):想象一个时钟。「3 点」是一个具体读数(余数),但「所有指向 3 的时刻」——3 点、15 点、27 点……——是一个同余类。时钟就是模 12(或模 24)运算的最直观模型。

模 n 一共有 n 个不同的同余类。它们把全体整数 完美地切成 n 份,不重不漏。


3. 同余是一个等价关系 (Equivalence Relation)

「等价关系」是数学里一个超级重要的结构。一个关系要成为等价关系,必须满足三条性质。同余三条全中——这正是为什么我们能放心地把同余当成「等号」来用。

三大性质

① 自反性 (Reflexive)

任何数都和自己同余。因为 ,而 0 能被任何数整除。✓

② 对称性 (Symmetric)

如果 ,那么 (只是差了个负号)。✓

③ 传递性 (Transitive)

如果 ,把两者相加:。✓

为什么这件事很重要?

因为「等价关系」保证了同余类能干净地划分整数。每个整数恰好属于一个同余类,不会同时属于两个,也不会一个都不属于。这种「不重不漏」的划分叫划分 (partition),它是后面剩余系、群论的根基。


4. 基本运算规则 (Basic Operation Rules)

同余之所以好用,是因为它对加、减、乘「兼容」。

封闭性 (Closure)

,那么:

直观理解:同余可以像普通等式一样「两边一起加 / 减 / 乘」。

简单证明(以乘法为例)

,则: 中间那一大坨都带着因子 ,所以 。✓

⚠️ 关键陷阱:除法不行!(The Big Trap)

新手最大的误区:以为既然加减乘都行,除法也能直接做。不行!

反例:,两边都「除以 2」会得到 ?这是错的)。

除法必须通过模逆元 (modular inverse) 来实现,而逆元只在 时存在。这是第二阶段的核心内容,现在你只需牢记:模运算里没有直接的除法


5. 剩余系 (Residue Systems)

这是第一阶段的收尾,把「同余类」的思想系统化。

完全剩余系 (Complete Residue System, CRS)

从每个同余类里各挑一个代表,凑成的集合就是完全剩余系。

最标准的完全剩余系是 。但代表可以随便换,只要每个同余类恰好被代表一次:

  • 模 5 的标准 CRS:
  • 模 5 的另一个 CRS:(分别

判定标准:n 个整数构成完全剩余系 它们两两模 n 不同余。

简化剩余系 (Reduced Residue System, RRS)

只保留那些与 n 互质的代表(即 )。

  • 模 5 的 RRS:(5 是质数,除了 0 都互质)
  • 8 的 RRS:(只有奇数与 8 互质)
  • 12 的 RRS:

简化剩余系的大小就是大名鼎鼎的欧拉函数 (Euler’s totient function)

这个 是连接第一阶段和后面欧拉定理、RSA 的桥梁——现在先有个印象就好。


Python 验证:动手感受同余

n = 5
 
# 验证同余类:哪些数和 2 同余?
congruence_class_2 = [x for x in range(-10, 20) if x % n == 2]
print("同余类 [2]_5:", congruence_class_2)
# [-8, -3, 2, 7, 12, 17]
 
# 验证简化剩余系 RRS
from math import gcd
def rrs(n):
    return [a for a in range(1, n) if gcd(a, n) == 1]
 
print("模 8 的 RRS:", rrs(8))   # [1, 3, 5, 7]
print("模 12 的 RRS:", rrs(12)) # [1, 5, 7, 11]
print("phi(12) =", len(rrs(12)))  # 4
 
# 验证除法陷阱:6 ≡ 0 (mod 6),但不能两边除以 2
print(6 % 6, "vs", 3 % 6)  # 0 vs 3 —— 不相等!证明除法不能直接做

库安装方式(本例仅用标准库 math,无需安装;若后续做数论实验推荐 sympy):

pip install sympy -i https://pypi.tuna.tsinghua.edu.cn/simple

第一阶段自测清单 (Self-Check)

学完后,问自己能不能不看笔记答出来:

  1. ✅ 用两种方式说出 的定义(余数相同 / 差被整除)
  2. ✅ 区分「余数 2」和「同余类
  3. ✅ 说出等价关系的三条性质,并证明同余满足
  4. ✅ 解释为什么加减乘可以,但除法不行
  5. ✅ 写出模 12 的完全剩余系和简化剩余系

我的观点:第一阶段看起来简单,但有两个点决定了你后面走得多远。第一是**「同余类」的集合视角**——能不能把 自然地看成一个整体,直接决定了你第四阶段理解 这个群时是顺畅还是卡壳。第二是除法陷阱,它不是一个孤立的「注意事项」,而是整个模逆元、线性同余、RSA 体系存在的根本理由。我建议你别急着冲第二阶段,先在脑子里把「时钟模型」和「整数被切成 n 份」这两个画面建立牢固。

关键提醒 (Caveats)

  • 「余数」是数、「同余类」是集合——这个区别后面会反复用到,现在务必分清
  • 简化剩余系大小 ,但 的计算公式是第三阶段内容,现在不必深究

准备好的话,我可以出 5–8 道第一阶段练习题(带详细解答),帮你检验掌握程度;或者直接带你进第二阶段:欧几里得算法与模逆元。你想走哪条?

模运算里的「除法」(Modular Division)

Confidence: 0.95

先说核心结论:模运算里没有真正的除法,所谓「除以 b」其实是「乘以 b 的逆元 (inverse)」

这里 是 b 的模逆元 (modular inverse),它满足:

下面我把「为什么」「什么时候能算」「怎么算」三件事讲透。


1. 为什么除法要变成「乘逆元」?

在普通实数里,「除以 2」就是「乘以 」,因为 。那个 就是 2 的乘法逆元——乘起来等于 1 的伙伴。

模运算的世界里没有分数,但「乘起来等于 1」这个想法可以保留。我们要找一个整数 ,让 。找到它,就能用乘法模拟除法。

例子:在模 7 下,3 的逆元是几?

我们要找 使 。试一下:。✓

所以 。于是「除以 3」就等于「乘以 5」。


2. 关键前提:逆元什么时候存在?

⚠️ 逆元不是永远存在的! 这是最重要的一条规则:

也就是说,b 必须和模数 n 互质 (coprime),逆元才存在。

为什么? 如果 ,那么 乘任何数得到的结果除以 n 的余数,永远是 d 的倍数,凑不出 1。

反例:模 6 下,2 没有逆元。因为 ,你试遍 ,永远得不到 1。所以在模 6 下「除以 2」是非法操作。

这也解释了第一阶段那个陷阱: 不能两边除以 2,正是因为 2 在模 6 下没有逆元。


3. 怎么算逆元?三种方法

方法一:扩展欧几里得算法 (Extended Euclidean) —— 最通用 ✅

这是任何情况都能用的方法(只要逆元存在)。原理:扩展欧几里得能找到整数 使得

两边模 n, 项消失,得到 。所以 x 就是逆元

手算示例:求 ,即解

步骤计算
余数 1
回代
对照
取正

。✓ 和前面试出来的一致。

适用范围:n 是任意数(不要求质数),只要 。时间复杂度


方法二:费马小定理 (Fermat’s Little Theorem) —— n 是质数时最简洁

模数 n 是质数 p 时,费马小定理给出:

也就是说,逆元 次方,用快速幂 (fast exponentiation) 算出来。

示例。✓

适用范围仅当 n 是质数。竞赛里模数常是 这种大质数,这个方法写起来最短,最常用。


方法三:欧拉定理 (Euler’s Theorem) —— 费马的推广

但 n 不一定是质数时:

它是费马小定理的推广(质数时 ,退化成费马)。缺点是要先算 ,所以实战中没扩展欧几里得方便。


4. 三种方法怎么选?

方法适用条件复杂度推荐场景
扩展欧几里得 (ExtGCD),n 任意最通用,n 不是质数时首选
费马小定理n 必须是质数竞赛(模 ),代码最短
欧拉定理,n 任意理论价值大,实战少用

一句话总结:质数模用费马(短),合数模用扩展欧几里得(稳)。


5. 完整除法示例(手算走一遍)

问题:计算

第 1 步:检查 ✓,逆元存在。

第 2 步:求 。试:,所以

第 3 步:除法变乘法:

验证 ✓——确实「5 乘回 4 等于 6」,说明 正确。


Python 实现(三种方法都给)

from math import gcd
 
# 方法一:扩展欧几里得(最通用,n 任意)
def modinv_extgcd(b, n):
    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
    g, x, _ = extgcd(b, n)
    if g != 1:
        return None  # 逆元不存在
    return x % n
 
# 方法二:费马小定理(仅当 n 是质数)
def modinv_fermat(b, p):
    return pow(b, p - 2, p)   # 快速幂
 
# 方法三:Python 3.8+ 内置(底层就是扩展欧几里得)
# pow(b, -1, n)
 
print(modinv_extgcd(3, 7))   # 5
print(modinv_fermat(3, 7))   # 5
print(pow(3, -1, 7))         # 5  ← 最省事
 
# 完整除法 6/4 mod 7
a, b, n = 6, 4, 7
print(a * pow(b, -1, n) % n) # 5
 
# 逆元不存在的情况:2 在模 6 下
print(modinv_extgcd(2, 6))   # None

库安装方式(本例仅用标准库 math,无需安装;做大规模数论实验推荐 sympy,它有现成的 mod_inverse):

pip install sympy -i https://pypi.tuna.tsinghua.edu.cn/simple

我的观点:模除法是整个数论体系里「牵一发动全身」的概念。你只要真正理解了「除法 = 乘逆元」「逆元 互质」这两句话,第二阶段的线性同余方程 ()、CRT、乃至 RSA 的解密公式,本质上都是这同一件事的不同包装。我特别建议你把扩展欧几里得这个方法手推到滚瓜烂熟——费马小定理虽然代码短,但它把「为什么逆元存在」这件事藏起来了;而扩展欧几里得直接把 这个等式摆在你面前,你能亲眼看到逆元是怎么「构造」出来的。这种构造性的理解,在你后面碰到 CRT 合并、ECDLP 时会一直回报你。

关键提醒 (Caveats)

  • 逆元存在的唯一条件是 ,算之前务必先检查,否则结果无意义
  • 费马小定理只对质数模数成立,用在合数上会得到错误答案
  • Python 的 pow(b, -1, n) 需要 3.8 以上版本;逆元不存在时它会抛 ValueError

需要我接着讲扩展欧几里得算法的完整推导(包括为什么回代能得到 ),还是直接进入第二阶段的线性同余方程