身份证号校验码算法详解:权重计算的数学原理
引言
“身份证号最后一位校验码怎么算?”——几乎所有做过中国业务系统的开发者都能背出那组权重:7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2,也知道"加权和 mod 11 再查表"。
但如果追问一句:为什么权重是这串数?为什么取模 11 而不是 10?为什么余数 2 对应字母 X?能答上来的人就少了很多。
本文不打算再贴一遍"怎么算"(那是入门内容),而是把校验码算法背后隐藏的数学原理一次讲透:权重从哪来、模数为什么是素数、校验码表如何推导、这套算法能检测哪些错误、不能检测哪些错误。
一、为什么需要校验码?
1.1 问题背景
身份证号是 18 位数字,靠人工抄录、输入、传输。任何一位写错,都可能把"张三"变成"张王"——如果系统无法识别错误,错误数据会直接进入数据库,成为脏数据。
最朴素的防错思路是加一个"校验位":由前 17 位通过某种规则计算得出第 18 位。校验码的作用是在不需要查库的前提下,检测号码是否被写错。
1.2 校验码能检测什么
以录入错误为例,错误类型可分为:
| 错误类型 | 举例 | 是否常见 |
|---|---|---|
| 单字符替换 | 11010119900101123X→11010119900101126X | 最常见 |
| 相邻两位交换 | ...0112...→...0211... | 常见 |
| 任意两位交换 | 相距较远的两位数对调 | 较少见 |
| 增/删一位 | 多打/漏打一位 | 较少见 |
一个好的校验算法,应当对高概率错误(替换、交换)有极高的检测率。这引出了校验算法的数学设计。
二、从"求和校验"到"加权校验"
2.1 朴素校验和:为什么不够用
最简单的方案是:让前 17 位求和,模 10 得到校验位,使"总和能被 10 整除"。
S = d₁ + d₂ + ... + d₁₇ 校验位 = (10 - S mod 10) mod 10这个方案有两个致命缺陷:
- 位置无关:
12 34和43 21的校验和一样,两位交换检测不出来。 - 模 10 非素数:后文会证明,模数为合数时存在系统性检测盲区。
2.2 加权校验和:让每一位"位置敏感"
改进思路:给每个位置分配一个权重,校验值依赖"每一位的数值和它所在的位置":
S = d₁·w₁ + d₂·w₂ + ... + d₁₇·w₁₇数学上,错误能检测出来的条件是:错误造成的加权和变化量 ≢ 0 (mod m)。
对于"第 i 位从 a 写成 b"的替换错误,变化量是(b - a) · wᵢ。要让这个变化量永不为 0,需要同时满足:
(b - a) ≢ 0 (mod m)—— 要求两个不同数字之差不是模数的倍数;wᵢ ≢ 0 (mod m)—— 要求权重不能为 0。
这就是选择权重和模数的两条硬约束,也是下两节数学论证的起点。
三、权重系数的来源:2 的幂与模 11
3.1 一个惊人的发现:权重表其实就是 2 的幂
把2¹, 2², ..., 2¹⁷分别对 11 取模:
2¹ mod 11 = 2 2² mod 11 = 4 2³ mod 11 = 8 2⁴ mod 11 = 16 mod 11 = 5 2⁵ mod 11 = 32 mod 11 = 10 2⁶ mod 11 = 64 mod 11 = 9 2⁷ mod 11 = 128 mod 11 = 7 2⁸ mod 11 = 256 mod 11 = 3 2⁹ mod 11 = 512 mod 11 = 6 2¹⁰ mod 11 = 1024 mod 11 = 1注意到2¹⁰ ≡ 1 (mod 11)(这正是费马小定理:11 是素数,2¹⁰ = 2¹¹⁻¹ ≡ 1 mod 11),所以2¹¹到2¹⁷只是前面序列的重复:
2¹¹ ≡ 2¹ (mod 11) = 2 2¹² ≡ 2² (mod 11) = 4 ...现在把2¹⁷到2¹从右往左排列(即校验位左边第 1 位权重取2¹,第 2 位取2²……):
| 位置(从右往左) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
权重 =2^k mod 11 | 2 | 4 | 8 | 5 | 10 | 9 | 7 | 3 | 6 | 1 | 2 | 4 | 8 | 5 | 10 | 9 | 7 |
从左往右读,恰好就是大家熟知的权重表:
位置(从左往右): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 权重: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2也就是说:
身份证校验码的权重系数 = 校验位右侧第 k 位的权重取
2^k mod 11。
3.2 为什么序列会"回头":模 11 下的原根
2^1, 2^2, ..., 2^10对 11 取模得到2, 4, 8, 5, 10, 9, 7, 3, 6, 1——注意,这 10 个数恰好把 1~10 全部遍历了一遍,无一重复。
这不是巧合,而是因为2 是模 11 的原根(primitive root):
若 g 是模素数 p 的原根,则
g¹, g², ..., g^(p-1)恰好遍历1 ~ p-1的所有非零剩余类。
原根性质带来两个关键收益:
- 权重两两不同(在 10 位周期内)——满足第 2 节的约束
wᵢ ≠ 0,且不同位置的权重互不相同; - 权重周期性重复(周期 = 10)——17 位的权重表只是前 10 个权重循环扩展,所以
wᵢ = wⱼ当且仅当|i - j|是 10 的倍数。
3.3 权重表的生成公式
综合以上,17 位权重可用一个公式概括:
wᵢ = 2^(18 - i) mod 11 (i = 1, 2, ..., 17,从左到右)代入验证:i=1时2¹⁷ mod 11 = 7;i=17时2¹ mod 11 = 2。与标准权重表完全一致。
四、为什么取模 11 而不是 10?
这是整个算法最精妙的设计决策。答案是:模数必须是素数,且要比数字字母表大。
4.1 模 10 的致命缺陷
假设模 10,权重wᵢ只能取 0~9。由第 2 节的约束,替换错误a → b的检测条件为:
(b - a) · wᵢ ≢ 0 (mod 10)但数字之差b - a取值范围是-9 ~ 9,其中包含±5。而模 10 中,5 与 10 不互素:5 × 2 = 10 ≡ 0 (mod 10)。
于是存在系统性漏洞:
| 权重 wᵢ | 检测不出的替换错误 |
|---|---|
| 2(含因子 2) | a → a±5(差 5 的倍数,5×2≡0) |
| 5(含因子 5) | a → a±2, a±4, a±6, a±8(偶数差,2×5≡0) |
| 0 | 任何替换都检测不出 |
只要权重表里出现 2、5、0,对应的位置就有确定性检测盲区——这不是"概率"问题,是"必然漏掉"。
4.2 模 11 为什么完美
11 是素数,因此:
- 任何非零权重与 11 互素:
wᵢ ≢ 0 (mod 11)时,wᵢ有模逆元; - 任何数字差与 11 互素:
b - a ∈ {±1..±9},绝对值小于 11 且非零,不可能被 11 整除。
所以对任意替换错误:
(b - a) · wᵢ ≢ 0 (mod 11) 恒成立结论:取模 11(素数),保证任意位置的任意单字符替换错误 100% 被检测。而 11 是"大于 10(数字字母表大小)的最小素数",这保证了模空间足够容纳全部校验结果。
4.3 为什么不取更大的素数
取 17、19 也可以,但会产生更多校验码取值(如余数 10 以上需要新符号),破坏"18 位纯数字/单字符"的简洁性。11 是在保证检测能力和保持可读性之间的最优平衡。
五、校验码的生成与验证
5.1 校验码表:不是查出来的,是算出来的
很多人记校验码表[1, 0, X, 9, 8, 7, 6, 5, 4, 3, 2],但这份表其实来自一个公式。
设r = S mod 11(S 为前 17 位加权和),标准规定校验码数值c满足:
(S + c) mod 11 = 1即c ≡ (1 - r) mod 11 ≡ (12 - r) mod 11。逐个代入:
| 余数 r | c = (12 - r) mod 11 | 校验码字符 |
|---|---|---|
| 0 | 12 mod 11 = 1 | 1 |
| 1 | 11 mod 11 = 0 | 0 |
| 2 | 10 | X |
| 3 | 9 | 9 |
| 4 | 8 | 8 |
| 5 | 7 | 7 |
| 6 | 6 | 6 |
| 7 | 5 | 5 |
| 8 | 4 | 4 |
| 9 | 3 | 3 |
| 10 | 2 | 2 |
表格第三列[1, 0, X, 9, 8, 7, 6, 5, 4, 3, 2]——与标准校验码表逐位吻合。当c = 10时,用罗马数字X表示(10 是一个字符能表达的最大值,保持 18 位不变)。
5.2 为什么是同余 1,而不是同余 0?
这是 ISO 7064 系列算法的统一设计:使用一个非零常数(这里取 1)。
若规定"完整号码加权和 ≡ 0",那么全零字符串000000000000000000的加权和恒为 0,天然满足校验——一个全是 0 的"号码"会被当成合法身份证号。而规定同余 1 之后,全零字符串被自然排除。
这也让验证变得非常优雅:不需要反查校验位,直接对完整 18 位(校验位权重隐含为 1)做一次加权求和,检查是否同余于 1 即可。
5.3 完整示例 1:校验码是数字
身份证11010119900101123X,前 17 位为11010119900101123:
位值: 1 1 0 1 0 1 1 9 9 0 0 1 0 1 1 2 3 权重: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2S = 1×7 + 1×9 + 0×10 + 1×5 + 0×8 + 1×4 + 1×2 + 9×1 + 9×6 + 0×3 + 0×7 + 1×9 + 0×10 + 1×5 + 1×8 + 2×4 + 3×2 = 126 r = 126 mod 11 = 5 校验码 = (12 - 5) mod 11 = 7完整号码:110101199001011237✅
5.4 完整示例 2:校验码是 X
身份证11010519491231002X,前 17 位为11010519491231002:
位值: 1 1 0 1 0 5 1 9 4 9 1 2 3 1 0 0 2 权重: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2S = 1×7 + 1×9 + 0×10 + 1×5 + 0×8 + 5×4 + 1×2 + 9×1 + 4×6 + 9×3 + 1×7 + 2×9 + 3×10 + 1×5 + 0×8 + 0×4 + 2×2 = 167 r = 167 mod 11 = 2 校验码 = (12 - 2) mod 11 = 10 → X完整号码:11010519491231002X✅(X 必须大写)
5.5 验证的等价形式
生成时校验位满足(S + c) ≡ 1 (mod 11),因此验证时只需一步:
(前17位加权和 + 校验位数值) mod 11 = 1 ⟺ 校验通过这与源码ChineseIdCardValidator的实现一致:计算前 17 位加权和mod 11得余数,用余数查校验码表比对最后一位。
六、纠错能力分析:这套算法到底有多强?
6.1 数学框架
错误检测的本质是判断"加权和变化量是否 ≡ 0 (mod 11)"。基于前文的原根性质,可以精确推导各类错误的检测率:
| 错误类型 | 检测条件 | 检测率 | 说明 |
|---|---|---|---|
| 单字符替换 | (b-a)·wᵢ ≢ 0 | 100% | b-a与wᵢ均与 11 互素,乘积恒非零 |
| 相邻两位交换 | (a-b)·(wᵢ-wᵢ₊₁) ≢ 0 | 100% | 相邻权重各不相同 |
| 任意两位交换 | (a-b)·(wᵢ-wⱼ) ≢ 0 | ≈94.9% | 仅当wᵢ = wⱼ(间隔 10 位)时失效 |
| 隔 10 位交换 | (a-b)·(wᵢ-wⱼ) = 0 | 0% | 权重周期 10,wᵢ = wⱼ,加权和不变 |
任意交换的失效率:17 位两两组合共
C(17,2) = 136对,其中间隔 10 位的7对(如第 1 位与第 11 位)权重相同。检测率129/136 ≈ 94.9%。
6.2 与朴素校验和的对比
| 对比项 | 朴素校验和(mod 10) | ValidX 校验码(mod 11) |
|---|---|---|
| 权重 | 全部为 1 | 2^k mod 11,位置敏感 |
| 单字符替换 | 100%(碰巧可行) | 100%(数学保证) |
| 相邻交换 | 0%(检测不出) | 100% |
| 任意交换 | 0% | ≈ 94.9% |
| 全零串误判 | 合法 | 非法(同余 1 设计) |
| 权重含 0/不互素因子 | 有(模 10 缺陷) | 无 |
"位置敏感 + 素数模"让检测能力发生质变——这就是全部数学设计的价值所在。
七、同源算法:ISO 7064 家族
身份证校验码采用的是ISO 7064:1983 MOD 11-2标准。这套"加权和 + 素数模 + 非零常数"的思想被广泛应用于全球各类编号系统:
| 算法 | 模数 | 基数 | 典型应用 |
|---|---|---|---|
| MOD 11-2 | 11 | 2 的幂 | 中国身份证、ISBN-10 书号 |
| MOD 37-2 | 37 | 2 的幂 | 通用字母数字串 |
| MOD 97-10 | 97 | 10 | IBAN 国际银行账号 |
| MOD 11-3 | 11 | 3 的幂 | 部分证件号码 |
| GB 32100-2015 | 31 | 特定权重 | 统一社会信用代码 |
它们共享同一套数学骨架:权重用基数 k 的幂次对素数模取模(原根性保证权重两两不同),校验位使完整串满足一个非零同余常数。
有趣的是,统一社会信用代码(18 位)与身份证(18 位)虽然长度相同,但分别采用模 31 与模 11 两套权重体系——这是 ValidX 同时提供
@ChineseIdCard与@UnifiedSocialCreditCode两个独立注解的原因。
八、ValidX 中的实现
8.1 注解方式
publicclassUserDTO{@NotBlank(message="身份证号不能为空")@ChineseIdCardprivateStringidCard;}@ChineseIdCard内部自动完成:格式验证 → 省份码验证 → 出生日期验证(含闰年)→校验码验证,校验逻辑与本文推导的算法完全一致。
8.2 链式 API
ValidXvalidator=ValidX.init().field("身份证号").isChineseIdCard(idCard);if(!validator.passed()){thrownewBusinessException(validator.getErrors());}8.3 源码对照
核心常量与本文推导一一对应:
// 权重系数(= 2^(18-i) mod 11)publicstaticfinalInteger[]idCardWeight={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};// 校验码表(r = S mod 11 时的校验字符)publicstaticfinalString[]idCardCheck={"1","0","X","9","8","7","6","5","4","3","2"};// 验证:加权和 mod 11,按余数查表比对最后一位intlastResult=resultSum%11;returnidCardMap.get(lastResult).equals(lastStr);15 位旧版身份证在校验时会被自动升位为 18 位(年份前补19)后再计算校验码。
九、常见问题
Q1:为什么校验码会有 X?
余数r = 2时,校验码数值为 10。两位数会破坏"18 位"的定长结构,因此借用罗马数字X表示 10,单字符表达。X 只能出现在末位,且必须大写。
Q2:权重表需要背吗?为什么网上有不同写法?
不需要背。如果看到其他文章写的是2, 4, 8, 5, 10, 9, 7, 3, 6, 1, ...,那是从右往左排列;标准权重表7, 9, 10, ...是从左往右排列。两者只是方向不同,计算时必须统一方向。
Q3:校验码能防伪造吗?
不能。校验码的作用是"防错"(检测录入/传输错误),不是"防伪"。算法和权重公开,任何人都能算出合法校验码。真正的防伪依赖公安系统对号码的实名核验。这也提醒我们:校验码通过 ≠ 身份真实。
Q4:全 0 的号码能通过校验吗?
不能。这正是"同余于 1"而非"同余于 0"的设计目的——全 0 串加权和为 0,不满足同余 1,被直接排除。
Q5:校验码算法能检测出"改生日"的篡改吗?
可以。改动出生日期任意一位,都会改变加权和,mod 11后与校验位不匹配,检测率 100%(单字符替换场景)。
Q6:15 位身份证怎么校验?
15 位老身份证没有校验码,算法上先补19升为 17 位,再计算并补上第 18 位校验码,然后按 18 位规则验证。ValidX 自动处理这一转换。
十、总结
用一张图回顾整条数学推导链:
原根性质(2 是模 11 的原根) │ 2^1..2^17 mod 11 遍历 1..10,周期 10 ▼ 权重表 wᵢ = 2^(18-i) mod 11 = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2] │ 素数模 11:任意非零权重 × 任意非零数字差 ≢ 0 ▼ 加权和 S = Σ dᵢ·wᵢ (位置敏感) │ ▼ 校验位 c = (12 - r) mod 11,r = S mod 11 (完整串同余于 1) │ ▼ 校验码表 [1, 0, X, 9, 8, 7, 6, 5, 4, 3, 2] (10 → X)核心要点,就三句话:
- 权重来自 2 的幂对 11 取模——原根性保证了"位置敏感 + 权重两两不同";
- 模数取素数 11——数学上保证了任何单字符错误 100% 可检测;
- 校验码让完整号码同余于 1——验证一步完成,且天然排除全零串。
理解了这三点,你不仅记住了权重表,更掌握了它"为什么是这样"。以后再遇到任何 ISO 7064 家族的校验算法(IBAN、统一社会信用代码),都能一眼看穿其数学本质。