☰
ValidX 身份证号校验码算法详解:权重计算的数学原理
2026/10/13 2:58:00 网站建设 项目流程

身份证号校验码算法详解:权重计算的数学原理

引言

“身份证号最后一位校验码怎么算?”——几乎所有做过中国业务系统的开发者都能背出那组权重: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

这个方案有两个致命缺陷:

  1. 位置无关:12 34和43 21的校验和一样,两位交换检测不出来。
  2. 模 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²……):

位置(从右往左)1234567891011121314151617
权重 =2^k mod 112485109736124851097

从左往右读,恰好就是大家熟知的权重表:

位置(从左往右): 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的所有非零剩余类。

原根性质带来两个关键收益:

  1. 权重两两不同(在 10 位周期内)——满足第 2 节的约束wᵢ ≠ 0,且不同位置的权重互不相同;
  2. 权重周期性重复(周期 = 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。逐个代入:

余数 rc = (12 - r) mod 11校验码字符
012 mod 11 = 11
111 mod 11 = 00
210X
399
488
577
666
755
844
933
1022

表格第三列[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 2
S = 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 2
S = 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ᵢ ≢ 0100%b-a与wᵢ均与 11 互素,乘积恒非零
相邻两位交换(a-b)·(wᵢ-wᵢ₊₁) ≢ 0100%相邻权重各不相同
任意两位交换(a-b)·(wᵢ-wⱼ) ≢ 0≈94.9%仅当wᵢ = wⱼ(间隔 10 位)时失效
隔 10 位交换(a-b)·(wᵢ-wⱼ) = 00%权重周期 10,wᵢ = wⱼ,加权和不变

任意交换的失效率:17 位两两组合共C(17,2) = 136对,其中间隔 10 位的7对(如第 1 位与第 11 位)权重相同。检测率129/136 ≈ 94.9%。

6.2 与朴素校验和的对比

对比项朴素校验和(mod 10)ValidX 校验码(mod 11)
权重全部为 12^k mod 11,位置敏感
单字符替换100%(碰巧可行)100%(数学保证)
相邻交换0%(检测不出)100%
任意交换0%≈ 94.9%
全零串误判合法非法(同余 1 设计)
权重含 0/不互素因子有(模 10 缺陷)无

"位置敏感 + 素数模"让检测能力发生质变——这就是全部数学设计的价值所在。


七、同源算法:ISO 7064 家族

身份证校验码采用的是ISO 7064:1983 MOD 11-2标准。这套"加权和 + 素数模 + 非零常数"的思想被广泛应用于全球各类编号系统:

算法模数基数典型应用
MOD 11-2112 的幂中国身份证、ISBN-10 书号
MOD 37-2372 的幂通用字母数字串
MOD 97-109710IBAN 国际银行账号
MOD 11-3113 的幂部分证件号码
GB 32100-201531特定权重统一社会信用代码

它们共享同一套数学骨架:权重用基数 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)

核心要点,就三句话:

  1. 权重来自 2 的幂对 11 取模——原根性保证了"位置敏感 + 权重两两不同";
  2. 模数取素数 11——数学上保证了任何单字符错误 100% 可检测;
  3. 校验码让完整号码同余于 1——验证一步完成,且天然排除全零串。

理解了这三点,你不仅记住了权重表,更掌握了它"为什么是这样"。以后再遇到任何 ISO 7064 家族的校验算法(IBAN、统一社会信用代码),都能一眼看穿其数学本质。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询