Caloogy 文章
English

弃九法与校验位

日期
2025 年 5 月 4 日
系列
密码学
阅读
12 分钟
主题
Check digitCasting out ninesModular arithmeticRing homomorphism

一句话版本

把一个整数换成它的余数再计算,结果和"先算再取余数"一样。这件事成立,是因为取模映射 π:Z→Z/nZ\pi:\mathbb{Z}\to\mathbb{Z}/n\mathbb{Z} 是环同态。 弃九法、条形码、银行卡号、身份证最后一位、IBAN,原理都一样:给数据加一个"指纹",抄错一位时指纹大概率会变。凡是差值落在同态的核 nZn\mathbb{Z} 里的错误,指纹都不会变,所以查不出来。

全文分三层:

  1. 通俗理解:弃九法、弃十一法,以及它们能查和不能查的错误
  2. 实际用处:ISBN、EAN/UPC 条形码、银行卡 Luhn、身份证、IBAN,最后简单延伸到 CRC 和 Verhoeff
  3. 数学原理:Z/nZ\mathbb{Z}/n\mathbb{Z}、环同态、核,以及为什么"模素数"能查得更全,每种方法都配手算例子

关于例子里的号码

文中所有书号、卡号、证件号、账号都是公开的测试号、标准文档里的示例号,或者虚构的号码,不对应任何真实的个人。所有算例都用 Python 验算过。


第一层:通俗理解

1.1 弃九法:用"数字和"检查算术

小学算 3847×5263847\times 526,得出 20235222023522,怎么快速检查对不对?

弃九法这样做:

  1. 每个数的各位数字加起来,一直加到只剩一位(也可以中途随时扔掉 99,"弃九"就是这个意思):
    • 38473847:3+8+4+7=223+8+4+7=22,2+2=42+2=4
    • 526526:5+2+6=135+2+6=13,1+3=41+3=4
  2. 对这两个"代表数"做同样的运算:4×4=164\times 4=16,1+6=71+6=7
  3. 算答案的数字和:20235222023522 是 2+0+2+3+5+2+2=162+0+2+3+5+2+2=16,1+6=71+6=7
  4. 两边都是 77,通过检查。

加法、减法也一样:

为什么数字和能代表一个数? 因为"各位数字之和"和原数除以 99 的余数相同。比如 3847÷9=4273847\div 9=427 余 44,正好等于数字和 22→422\to 4。原因是 1010 除以 99 余 11,所以 100,1000,…100,1000,\dots 除以 99 也都余 11,每一位只按它"本身的大小"贡献余数。第三层会严格说明。

而"余数"有一个好性质:先算再取余,等于先取余再算。所以如果真答案除以 99 余 77,你的答案却余 55,那答案一定错了。

1.2 弃九法能查出什么,查不出什么

检查通过不代表一定对

弃九法只能证明"错了",不能证明"对了"。余数不一致说明一定有错;余数一致只能说明可能对。

能查出来的:大多数单个数字写错。比如把 20235222023522 写成 20235322023532,数字和变成 17→8≠717\to 8\ne 7,立刻发现。

查不出来的:

错误类型 例子 为什么查不出
两位数字对调 20235222023522 写成 22035222203522 数字还是那几个,数字和不变
把 00 写成 99 或反过来 20235222023522 写成 29235222923522 数字和刚好变 99,弃九后一样
某一位差 99 的整数倍 20235222023522 写成 20325222032522(多了 90009000) 差值是 99 的倍数
多写或漏写一个 00 20235222023522 写成 2023522020235220 末尾的 00 不改变数字和

总结一下:只要错答案和正确答案的差是 99 的倍数,弃九法就看不出来。 第一行"对调"是最常见的手误,它恰好就属于这一类:把相邻两位 a,ba,b 对调,差值是 9×(a−b)×10k9\times(a-b)\times 10^k。

1.3 弃十一法:用"交错和"补漏

换成除以 1111 的余数可以弥补对调的问题。因为 1010 除以 1111 余 −1-1(也就是余 1010),所以

一个数除以 1111 的余数 == 从个位开始,一加一减的交错和

例如:

再看刚才弃九法漏掉的两个错误:

但弃十一法也有盲区:差值是 1111 的倍数的错误它查不出来,比如隔一位的两个数字对调(两位同号,交错和不变)。所以实际中常常两个一起用。差值必须同时是 99 和 1111 的倍数,也就是 9999 的倍数,错误才会同时漏掉两个检查。

不一样

一样

要检查的计算
a × b = c ?

各自取余数
(数字和 / 交错和)

对余数做同样的运算

和 c 的余数
一样吗?

一定算错了

可能对
(差值 ∈ 9ℤ 时会漏)

1.4 从"检查算术"到"检查号码"

校验位把这个想法反过来用。弃九法检查的是"计算结果",校验位检查的是"抄写或输入的号码":

  1. 规定一个"指纹函数",例如"加权求和再除以 1111 取余"
  2. 发号码时多加一位(校验位),让整个号码的指纹等于某个固定值,比如 00
  3. 有人抄号码或者扫描号码时,重新算一遍指纹。不对,就说明号码错了

人抄写时最常见的两类错误是:

设计校验位就是要选好"模数"和"权重",让这两类错误全部能查出来。下面每种方案都会看它在这两点上表现如何。


第二层:实际用处

先给一张总表,后面逐个解释:

方案 模数 权重 / 规则 单个数字错误 相邻对调
弃九法 99 全是 11 漏 0↔90\leftrightarrow 9 全漏
ISBN-10 1111 10,9,…,110,9,\dots,1 全查出 全查出
ISBN-13 / EAN / UPC 1010 1,31,3 交替 全查出 漏差为 55 的对(如 16↔6116\leftrightarrow 61)
Luhn(银行卡) 1010 隔位"加倍后取数字和" 全查出 只漏 09↔9009\leftrightarrow 90
身份证 ISO 7064 MOD 11-2 1111 2k mod 112^k \bmod 11 全查出 全查出
IBAN ISO 7064 MOD 97-10 9797 10k mod 9710^k \bmod 97 全查出 全查出
Verhoeff 二面体群 D5D_5 置换 全查出 全查出

规律很明显:模数是素数(1111、9797)的方案效果最好。第三层会解释原因。

2.1 ISBN-10:模 1111,校验位可以是 X

2007 年以前的书号有 1010 位 a1a2⋯a10a_1a_2\cdots a_{10},规则是

10a1+9a2+8a3+⋯+2a9+1⋅a10≡0(mod11).10a_1+9a_2+8a_3+\cdots+2a_9+1\cdot a_{10}\equiv 0 \pmod{11}.

因为模 1111 的余数有 00 到 1010 共 1111 种,校验位 a10a_{10} 有时需要等于 1010,这时用罗马数字 X 表示。所以有些老书的书号以 X 结尾。

例子:ISBN 0-306-40615-2(维基百科等资料常用的示例书号)

位 00 33 00 66 44 00 66 11 55 22
权重 1010 99 88 77 66 55 44 33 22 11
乘积 00 2727 00 4242 2424 00 2424 33 1010 22

总和 =132=12×11≡0(mod11)=132=12\times 11\equiv 0\pmod{11} ✓

带 X 的例子:0-8044-2957-X。前 99 位的加权和是 0+72+0+28+24+10+36+15+14=1990+72+0+28+24+10+36+15+14=199,而 199≡1(mod11)199\equiv 1\pmod{11}。校验位 cc 要满足 1+c≡01+c\equiv 0,所以 c=10c=10,写成 X。

查错能力:所有单个数字错误、所有对调(不只是相邻对调)都能查出来。

2.2 ISBN-13、EAN-13、UPC:模 1010,权重 11 和 33

超市条形码和现在的书号用的是这一套。规则是从左往右,权重 1,3,1,3,…1,3,1,3,\dots 交替:

a1+3a2+a3+3a4+⋯+a13≡0(mod10).a_1+3a_2+a_3+3a_4+\cdots+a_{13}\equiv 0\pmod{10}.

(1212 位的 UPC-A 从左起权重是 3,1,3,1,…3,1,3,1,\dots,原理相同。)

例子:ISBN-13 978-0-306-40615-7

位 99 77 88 00 33 00 66 44 00 66 11 55 77
权重 11 33 11 33 11 33 11 33 11 33 11 33 11
乘积 99 2121 88 00 33 00 66 1212 00 1818 11 1515 77

总和 =100≡0(mod10)=100\equiv 0\pmod{10} ✓。UPC 示例 036000291452 用同样方法验算也通过。

查错能力:

为了兼容条形码系统,ISBN-13 放弃了 ISBN-10 的模 1111,所以查错能力反而变弱了。

2.3 银行卡号:Luhn 算法

Visa、Mastercard、银联等卡号的最后一位都是 Luhn 校验位(IBM 的 Hans Peter Luhn 1954 年提出)。规则是:

  1. 从右往左数,第 2,4,6,…2,4,6,\dots 位乘以 22
  2. 如果乘出来是两位数,就把两位数字加起来(等价于减 99),例如 7×2=14→1+4=57\times 2=14\to 1+4=5
  3. 把所有位加起来,总和必须 ≡0(mod10)\equiv 0\pmod{10}

例子:4111 1111 1111 1111(Visa 公开的测试卡号,常用于支付开发测试,不是真卡)

从右往左,偶数位(从左数的第 1,3,5,…,151,3,5,\dots,15 位)要加倍:

总和 =8+14+8=30≡0(mod10)=8+14+8=30\equiv 0\pmod{10} ✓

如果手误把倒数第二位写成 22(...1121),这一位在加倍位,变成 44。总和变为 32≢032\not\equiv 0,查出。

查错能力:

Luhn 不是安全措施

校验位只防手误,不防伪造。算法是公开的,任何人都能算出合法的校验位,所以真正的安全性靠卡组织的数据库、CVV 和 3-D Secure。

2.4 中国居民身份证第 18 位:ISO 7064 MOD 11-2

1818 位身份证号 a1a2⋯a18a_1a_2\cdots a_{18} 中,前 66 位是地区码,接下来 88 位是出生日期,再 33 位是顺序码,最后 a18a_{18} 是校验码。规则是

∑i=118ai⋅218−i≡1(mod11),\sum_{i=1}^{18} a_i\cdot 2^{18-i}\equiv 1\pmod{11},

其中 a18a_{18} 如果需要等于 1010,就写成 X。这就是为什么有些人的身份证号以 X 结尾,原因和 ISBN-10 完全一样。

把 218−i2^{18-i} 对 1111 取余,前 1717 位的权重是

7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2,7,\ 9,\ 10,\ 5,\ 8,\ 4,\ 2,\ 1,\ 6,\ 3,\ 7,\ 9,\ 10,\ 5,\ 8,\ 4,\ 2,

最后一位的权重是 20=12^0=1。实际计算时先求前 1717 位的加权和 SS,再查表:

S mod 11S\bmod 11 00 11 22 33 44 55 66 77 88 99 1010
校验码 11 00 X 99 88 77 66 55 44 33 22

(这张表就是解方程 S+c≡1(mod11)S+c\equiv 1\pmod{11} 得到的结果。)

例子:11010519491231002X。这是国家标准 GB 11643—1999 里的示例号码,不对应真实个人。

位 11 11 00 11 00 55 11 99 44 99 11 22 33 11 00 00 22
权重 77 99 1010 55 88 44 22 11 66 33 77 99 1010 55 88 44 22
乘积 77 99 00 55 00 2020 22 99 2424 2727 77 1818 3030 55 00 00 44

S=167=15×11+2S=167=15\times 11+2,所以 S≡2S\equiv 2,查表得校验码 X ✓。验证:167+10=177=16×11+1≡1167+10=177=16\times 11+1\equiv 1 ✓。

如果把生日 1231 抄成 1213(相邻的 3,13,1 对调),算出的校验码会变成 99,与 X 不符,查出。

查错能力:所有单个数字错误和所有相邻对调都能查出来。

2.5 IBAN:模 9797

国际银行账号(IBAN,欧洲、中东等地使用)用两位校验数字,规则是 ISO 7064 MOD 97-10:

  1. 把开头 44 个字符(国家代码和两位校验数字)挪到末尾
  2. 字母换成两位数:A=10, B=11, …, Z=35\mathrm{A}=10,\ \mathrm{B}=11,\ \dots,\ \mathrm{Z}=35
  3. 得到的大整数必须 ≡1(mod97)\equiv 1\pmod{97}

例子:GB82 WEST 1234 5698 7654 32。这是 IBAN 标准文档常用的虚构示例账号。

N=3214282912345698765432161182N=3214282912345698765432161182

321428291≡70(mod97)702345698≡29(mod97)297654321≡24(mod97)2461182≡1(mod97) ✓\begin{aligned} 321428291 &\equiv 70 \pmod{97}\\ 702345698 &\equiv 29 \pmod{97}\\ 297654321 &\equiv 24 \pmod{97}\\ 2461182 &\equiv 1 \pmod{97}\ \checkmark \end{aligned}

这种分段算法成立,也是因为取模是环同态,第三层会说明。

生成校验位时,先把校验位当成 00 算出 N0N_0,校验位就是 98−(N0 mod 97)98-(N_0\bmod 97)。这个例子里算出来正好是 8282。

为什么选 9797:9797 是小于 100100 的最大素数。两位校验数字最多能表示 100100 种值,9797 几乎全用上了,而且它是素数,查错能力最强。IBAN 能查出所有单字符错误和所有相邻数字对调,实际漏检率约为 1/97≈1%1/97\approx 1\%。

2.6 延伸:CRC 和 Verhoeff

CRC(循环冗余校验):把"模一个数"换成"模一个多项式"

以太网帧、ZIP 压缩包、PNG 图片都用 CRC。它把比特串 bm−1⋯b1b0b_{m-1}\cdots b_1b_0 看成 F2[x]\mathbb{F}_2[x] 里的多项式 ∑bixi\sum b_i x^i(F2=Z/2Z\mathbb{F}_2=\mathbb{Z}/2\mathbb{Z}),然后对一个固定的生成多项式 g(x)g(x) 取余: F2[x] ⟶ F2[x]/(g(x)).\mathbb{F}_2[x]\ \longrightarrow\ \mathbb{F}_2[x]/(g(x)). 这也是一个环同态,核是理想 (g)(g)。只要错误多项式 e(x)e(x) 不被 g(x)g(x) 整除,错误就能查出来。选好 gg(比如 CRC-32 的那个 3232 次多项式)可以保证查出所有长度 ≤32\le 32 的连续错误(突发错误)。这和弃九法是同一个模式,只是把 Z\mathbb{Z} 换成了 F2[x]\mathbb{F}_2[x]。

Verhoeff 算法:用非交换群 D5D_5 一次解决两类错误

前面的方案都在交换环 Z/nZ\mathbb{Z}/n\mathbb{Z} 里做加权求和。只要模数是 1010,就总会漏掉一些错误:EAN 漏 55 对,Luhn 漏 11 对。可以证明,在 Z/10Z\mathbb{Z}/10\mathbb{Z} 里用"加权和"不可能同时查出所有单个错误和所有相邻对调。

Jacobus Verhoeff 1969 年的做法是:把 1010 个数字对应到二面体群 D5D_5(正五边形的 1010 个对称:55 个旋转加 55 个翻转)的 1010 个元素,再配一个精心设计的置换 σ\sigma。规则是 σ1(a1)∗σ2(a2)∗⋯∗σn(an)=e,\sigma^{1}(a_1) * \sigma^{2}(a_2) * \cdots * \sigma^{n}(a_n) = e, 其中 ∗* 是 D5D_5 里的乘法(不交换)。因为 xy≠yxxy\ne yx,对调相邻两位一般会改变乘积,于是只用一位十进制校验位就做到了单个错误和相邻对调全部查出。

例子:数字串 236 的 Verhoeff 校验位是 33,2363 通过检查,而把它改成 2633 就不能通过。印度的 Aadhaar 身份号码用的就是这个算法。


第三层:数学原理

3.1 同余与 Z/nZ\mathbb{Z}/n\mathbb{Z}

固定正整数 nn。如果 n∣a−bn\mid a-b,就说 a≡b(modn)a\equiv b\pmod n。这是一个等价关系,aa 的等价类记为

aˉ=a+nZ={…,a−n, a, a+n, … }.\bar a = a+n\mathbb{Z}=\{\dots,a-n,\ a,\ a+n,\ \dots\}.

全部等价类构成 Z/nZ={0ˉ,1ˉ,…,n−1‾}\mathbb{Z}/n\mathbb{Z}=\{\bar 0,\bar 1,\dots,\overline{n-1}\},运算定义为

aˉ+bˉ=a+b‾,aˉ bˉ=ab‾.\bar a+\bar b=\overline{a+b},\qquad \bar a\,\bar b=\overline{ab}.

良定义

要说明上面的运算不依赖代表元的选取:若 a≡a′a\equiv a'、b≡b′b\equiv b',则 ab−a′b′=a(b−b′)+b′(a−a′)∈nZ,ab-a'b' = a(b-b')+b'(a-a')\in n\mathbb{Z}, 因为 nZn\mathbb{Z} 是理想:它对加法封闭,并且吸收乘法(r⋅nZ⊆nZr\cdot n\mathbb{Z}\subseteq n\mathbb{Z})。这正是商环 R/IR/I 能定义乘法的原因。

3.2 取模映射是环同态

定义

π:Z→Z/nZ,π(a)=aˉ.\pi:\mathbb{Z}\to\mathbb{Z}/n\mathbb{Z},\qquad \pi(a)=\bar a.

由 3.1 的运算定义直接可得

π(a+b)=π(a)+π(b),π(ab)=π(a) π(b),π(1)=1ˉ,\pi(a+b)=\pi(a)+\pi(b),\qquad \pi(ab)=\pi(a)\,\pi(b),\qquad \pi(1)=\bar 1,

所以 π\pi 是一个(满的)环同态。用文字说就是"先算再取余 == 先取余再算",也就是

ab‾=aˉ bˉ.\overline{ab}=\bar a\,\bar b .

弃九法就是取 n=9n=9 的情形:要检查 c=?abc\stackrel{?}{=}ab,只需检查 cˉ=?aˉ bˉ\bar c\stackrel{?}{=}\bar a\,\bar b 在 Z/9Z\mathbb{Z}/9\mathbb{Z} 里是否成立。Z/9Z\mathbb{Z}/9\mathbb{Z} 只有 99 个元素,计算量很小。

逻辑方向要注意:

c=ab ⟹ π(c)=π(a)π(b),c=ab\ \Longrightarrow\ \pi(c)=\pi(a)\pi(b),

但反过来不成立。所以弃九法只能否定,不能肯定。

1.1 的例子用同态语言重写

π(3847)=4ˉ\pi(3847)=\bar 4,π(526)=4ˉ\pi(526)=\bar 4,4ˉ⋅4ˉ=16‾=7ˉ\bar 4\cdot\bar 4=\overline{16}=\bar 7,π(2023522)=7ˉ\pi(2023522)=\bar 7。两边相等,同态没有否定这个等式。

IBAN 的分段求余也是同态性质的应用。设 N=A⋅10k+BN=A\cdot 10^k+B,那么

π(N)=π(A) π(10)k+π(B),\pi(N)=\pi(A)\,\pi(10)^k+\pi(B),

所以可以先把 AA 换成它的余数 rr,再算 r⋅10k+Br\cdot 10^k+B 的余数,结果不变。

3.3 为什么"10≡110\equiv 1"推出数字和

设 N=∑k=0mdk 10kN=\sum_{k=0}^{m} d_k\,10^k,其中 dk∈{0,…,9}d_k\in\{0,\dots,9\} 是各位数字。用同态 π:Z→Z/9Z\pi:\mathbb{Z}\to\mathbb{Z}/9\mathbb{Z},

π(N)=∑k=0mπ(dk) π(10)k=∑k=0mdˉk⋅1ˉ k=∑kdk‾,\pi(N)=\sum_{k=0}^{m}\pi(d_k)\,\pi(10)^k=\sum_{k=0}^{m}\bar d_k\cdot\bar 1^{\,k}=\overline{\textstyle\sum_k d_k},

因为 10=9+110=9+1,所以 π(10)=1ˉ\pi(10)=\bar 1。也就是说,

N≡d0+d1+⋯+dm(mod9).N\equiv d_0+d_1+\cdots+d_m\pmod 9 .

模 33 同理,因为 10≡1(mod3)10\equiv 1\pmod 3。这就是"数字和被 33 整除则原数被 33 整除"的原因。

换成模 1111:10=11−110=11-1,所以 π(10)=−1‾\pi(10)=\overline{-1},于是

N≡∑k=0m(−1)kdk=d0−d1+d2−⋯(mod11),N\equiv\sum_{k=0}^{m}(-1)^k d_k = d_0-d_1+d_2-\cdots\pmod{11},

这就是交错和。

手算

N=2023522N=2023522,其中 d0=2,d1=2,d2=5,d3=3,d4=2,d5=0,d6=2d_0=2, d_1=2, d_2=5, d_3=3, d_4=2, d_5=0, d_6=2: ∑kdk=16≡7(mod9),∑k(−1)kdk=2−2+5−3+2−0+2=6(mod11).\sum_k d_k=16\equiv 7\pmod 9,\qquad \sum_k(-1)^kd_k=2-2+5-3+2-0+2=6\pmod{11}. 实际上 2023522=224835×9+7=183956×11+62023522=224835\times 9+7=183956\times 11+6 ✓

3.4 核是 9Z9\mathbb{Z}:哪些错误查不出

ker⁡π={a∈Z: π(a)=0ˉ}=9Z.\ker\pi=\{a\in\mathbb{Z}:\ \pi(a)=\bar 0\}=9\mathbb{Z}.

设正确答案是 cc,写下的答案是 c′c',错误量为 e=c′−ce=c'-c。那么

π(c′)=π(c)  ⟺  π(e)=0ˉ  ⟺  e∈ker⁡π=9Z.\pi(c')=\pi(c)\iff \pi(e)=\bar 0\iff e\in\ker\pi=9\mathbb{Z}.

所以一个错误查不出来,当且仅当错误量落在核里。这是同态最基本的性质:π(x)=π(y)  ⟺  x−y∈ker⁡π\pi(x)=\pi(y)\iff x-y\in\ker\pi。由第一同构定理,Z/ker⁡π≅Z/9Z\mathbb{Z}/\ker\pi\cong\mathbb{Z}/9\mathbb{Z},核里的差别在像里完全看不出来。

逐一对照 1.2 的表格:

3.5 加权校验位的一般框架,以及素数为什么重要

所有"加权和"型校验位都可以写成

φ(a1,…,am)=∑i=1mwi ai ∈ Z/nZ,合法号码  ⟺  φ=tˉ.\varphi(a_1,\dots,a_m)=\sum_{i=1}^{m} w_i\,a_i\ \in\ \mathbb{Z}/n\mathbb{Z},\qquad \text{合法号码}\iff\varphi=\bar t .

φ\varphi 是从 (Z/nZ)m(\mathbb{Z}/n\mathbb{Z})^m 到 Z/nZ\mathbb{Z}/n\mathbb{Z} 的模同态(线性映射),所以查错分析和 3.4 一样:错误向量 ee 查不出来,当且仅当 e∈ker⁡φe\in\ker\varphi。

(1) 单个数字错误:第 ii 位从 aa 变成 bb,φ\varphi 的变化量是 wi(b−a)w_i(b-a)。

(2) 第 i,ji,j 位对调:设原来分别是 a,ba,b,变化量是

wi(b−a)+wj(a−b)=(wi−wj)(b−a).w_i(b-a)+w_j(a-b)=(w_i-w_j)(b-a).

因为数字满足 0≤a,b≤90\le a,b\le 9,所以 a≠ba\ne b 时 b−a∈{±1,…,±9}b-a\in\{\pm1,\dots,\pm9\}。

定理(模素数时的查错能力)

设 n=pn=p 是素数且 p>9p>9(例如 1111 或 9797)。

  1. 若所有权重 wi≢0(modp)w_i\not\equiv 0\pmod p,则所有单个数字错误都能查出。
  2. 若 wi≢wj(modp)w_i\not\equiv w_j\pmod p,则第 i,ji,j 位的对调都能查出。

证明:pp 是素数,所以 Z/pZ\mathbb{Z}/p\mathbb{Z} 是域,没有零因子:xy=0⇒x=0xy=0\Rightarrow x=0 或 y=0y=0。 由于 p>9p>9,0<∣b−a∣≤9<p0<|b-a|\le 9<p,所以 b−a‾≠0ˉ\overline{b-a}\ne\bar 0,即 b−a‾\overline{b-a} 可逆。

  • (1) wiw_i 和 b−ab-a 都可逆,乘积 wi(b−a)≠0ˉw_i(b-a)\ne\bar 0。
  • (2) wi−wjw_i-w_j 和 b−ab-a 都可逆,乘积 (wi−wj)(b−a)≠0ˉ(w_i-w_j)(b-a)\ne\bar 0。■\blacksquare

逐一检查:

反例:模数不是素数时,零因子会造成漏检

一句话总结

校验位的设计要点,是让权重 wiw_i 和权重差 wi−wjw_i-w_j 在 Z/nZ\mathbb{Z}/n\mathbb{Z} 里都是单位(可逆元)。nn 是素数时,Z/nZ\mathbb{Z}/n\mathbb{Z} 是域,非零元都是单位,这个要求很容易满足。n=10n=10 时有零因子 22 和 55,就总会留下漏洞。

3.6 Luhn 为什么只漏 09↔9009\leftrightarrow 90

Luhn 不是纯粹的加权和。设"加倍取数字和"函数为

δ(x)={2x,0≤x≤4,2x−9,5≤x≤9,\delta(x)=\begin{cases}2x, & 0\le x\le 4,\\ 2x-9, & 5\le x\le 9,\end{cases}

它的取值表是

xx 00 11 22 33 44 55 66 77 88 99
δ(x)\delta(x) 00 22 44 66 88 11 33 55 77 99

(注意 δ(x)≡2x(mod9)\delta(x)\equiv 2x\pmod 9,因为数字和不改变模 99 的余数,这里又用到了 3.3。)

单个错误:如果发生在不加倍的位置,变化量是 b−a≢0(mod10)b-a\not\equiv 0\pmod{10};如果发生在加倍的位置,变化量是 δ(b)−δ(a)\delta(b)-\delta(a)。从上表可以看出 δ\delta 是 {0,…,9}\{0,\dots,9\} 上的置换(单射),所以 a≠ba\ne b 时 δ(a)≠δ(b)\delta(a)\ne\delta(b),并且两者都在 00 到 99 之间,差不可能是 1010 的倍数。因此单个错误全部能查出。

相邻对调:相邻两位中恰好一位加倍。设原来加倍位是 aa、普通位是 bb,对调后加倍位是 bb、普通位是 aa。总和的变化量为

(δ(b)+a)−(δ(a)+b)=f(b)−f(a),f(x):=δ(x)−x.\big(\delta(b)+a\big)-\big(\delta(a)+b\big)=f(b)-f(a),\qquad f(x):=\delta(x)-x .

计算 f(x) mod 10f(x)\bmod 10:

xx 00 11 22 33 44 55 66 77 88 99
f(x)=δ(x)−xf(x)=\delta(x)-x 00 11 22 33 44 −4-4 −3-3 −2-2 −1-1 00
f(x) mod 10f(x)\bmod 10 00 11 22 33 44 66 77 88 99 00

在 Z/10Z\mathbb{Z}/10\mathbb{Z} 中,ff 只有一次"撞车":f(0)=f(9)=0f(0)=f(9)=0。所以 a≠ba\ne b 时,

f(a)≡f(b)(mod10)  ⟺  {a,b}={0,9},f(a)\equiv f(b)\pmod{10}\iff\{a,b\}=\{0,9\},

也就是说,Luhn 只漏掉 09↔9009\leftrightarrow 90 这一种相邻对调。直观上,00 加倍还是 00,99 加倍是 18→918\to 9,两者都是 δ\delta 的不动点,所以无论哪一位加倍,总和都一样。

手算:测试卡号 4111 1111 1111 11114111\ 1111\ 1111\ 1111

从右往左编号 i=1,…,16i=1,\dots,16,偶数 ii 加倍: ∑i 奇ai=8⋅1=8,∑i 偶δ(ai)=δ(4)+7 δ(1)=8+14=22,\sum_{i\ \text{奇}} a_i=8\cdot 1=8,\qquad \sum_{i\ \text{偶}}\delta(a_i)=\delta(4)+7\,\delta(1)=8+14=22, 合计 30≡0(mod10)30\equiv 0\pmod{10} ✓。 反例(虚构的号码):4090 0000 0000 00034090\ 0000\ 0000\ 0003 和 4900 0000 0000 00034900\ 0000\ 0000\ 0003 两个都能通过检查,因为 f(0)=f(9)f(0)=f(9)。

3.7 全景图

环/模同态 π

核 = n ℤ(或 (g))

是:ℤ/pℤ 是域
非零元都可逆

否:有零因子
如 2·5=0 in ℤ/10ℤ

换成非交换群 D₅

ℤ(或 (ℤ/nℤ)^m, 𝔽₂[x])

ℤ/nℤ(或 𝔽₂[x]/(g))

错误量落在核里
⇒ 查不出

n 是素数?

单个错误 + 对调
全部查出
(ISBN-10, 身份证, IBAN)

总有漏洞
(EAN 漏差5, Luhn 漏 09↔90)

Verhoeff:全部查出

和抽象代数的对应

代数概念 在本文里的角色
环同态 π:Z→Z/nZ\pi:\mathbb{Z}\to\mathbb{Z}/n\mathbb{Z} "先取余再算 == 先算再取余",是弃九法的依据
核 ker⁡π=nZ\ker\pi=n\mathbb{Z} 是理想 查不出的错误恰好是错误量落在核里的那些
商环 Z/nZ\mathbb{Z}/n\mathbb{Z} 的良定义 分段求余、数字和、交错和都能算
Z/pZ\mathbb{Z}/p\mathbb{Z} 是域(pp 素数) 非零元可逆、无零因子,所以 ISBN-10、身份证、IBAN 能全部查出
零因子(2ˉ⋅5ˉ=0ˉ\bar2\cdot\bar5=\bar0) EAN 漏掉差为 55 的对调
多项式环商 F2[x]/(g)\mathbb{F}_2[x]/(g) CRC
非交换群 D5D_5 Verhoeff 校验