万物皆有编号 书架

第 07 章 / 共 12 章

第七章 · 校验位:编号自带的防伪术

第七章 · 校验位:编号自带的防伪术

先做个思想实验。银行柜员输入你的 16 位卡号,手一滑,把其中一个 3 敲成了 8。如果这个号码是纯粹的流水号,系统会高高兴兴地收下它——钱是实打实扣了,只是记在了别人的账上。可现实中,这种转账几乎不会发生,因为卡号的最后一位根本不是卡号的一部分,它是前 15 位算出来的一个「指纹」。改任何一位,指纹就对不上,系统在钱动之前就把这笔单子拦下了。

这个指纹有个正式名字:校验位,就是编号末尾专门留出来的一位(或几位),它不携带任何业务信息,唯一的作用是根据前面所有位重新算一遍,验证这串数字在抄写、录入、传输的路上没有出错。

亲手算一遍你的身份证第 18 位

光说原理不过瘾,我们拿国家标准 GB 11643 里的示例号码 11010519491231002X 真算一遍。前 17 位每一位都有一个固定的「权重」,从左到右依次是 7、9、10、5、8、4、2、1、6、3、7、9、10、5、8、4、2。算法只有三步:

  1. 每一位乘上自己的权重再相加:1×7 + 1×9 + 0×10 + … + 2×2 = 167
  2. 拿 167 除以 11,余数是 2
  3. 查一张小表:余数 0→1,1→0,2→X,3→9……一直排到 10→2。余数是 2,所以第 18 位是 X

对了,这个号码的尾号确实是 X。整个过程小学生都能算,但设计里藏着两个机关。

第一个机关:为什么除以 11,而不是好算的 10?因为 11 是质数。除以 10 的话,校验位只看总和的个位,权重稍不注意就会「撞车」;而质数 11 和每个权重都互质,保证了任何一位数字变动、任何相邻两位交换,余数几乎必然改变

第二个机关:为什么有人身份证尾号是 X?余数有 11 种可能(0 到 10),但一位十进制数字只有 10 个,装不下。于是标准借用了罗马数字的 X 来表示 10——你的尾号 X 不是什么特殊身份,只是余数恰好落在了那个装不下的格子里。

说人话:校验位就像你出差报销时,财务让你把发票金额逐笔报一遍,再报一个合计。他不用重算每一笔,只要核一下合计对不对——对不上,中间一定有哪笔抄错了。权重和质数除法的作用,是让「抄错一笔」和「两笔写反」这两种最常见的错,都一定会把合计弄乱。

信用卡的 Luhn 算法:一个 1954 年的小发明

身份证那套加权求和叫 ISO 7064 MOD 11-2,而你的银行卡、信用卡用的是另一套更著名的算法:Luhn 算法,IBM 工程师 Hans Peter Luhn 在 1954 年申请专利的小发明。它更简单:从右往左,每隔一位把数字翻倍(翻倍后超过 9 就减 9,比如 8×2=16→7),全部加起来,总和能被 10 整除就算合法。

比如验证 7992739871:翻倍位处理后再求和得 67,距离下一个 10 的倍数还差 3,所以这张卡的完整卡号末位必须是 3,即 79927398713。你现在随便打开一个支付页面,乱敲 16 位数字,页面不用联网就能立刻告诉你「卡号有误」——靠的就是这个本地一秒能算完的校验。

Luhn 的战绩很能说明设计目标:它能抓住 100% 的单数字错误,以及几乎全部相邻两位写反的错误——唯一的漏网之鱼是把 09 写成 90(或者反过来),因为这一对交换后总和恰好不变。这不是疏漏,而是取舍:人类录入最常见的错误就是「打错一位」和「相邻两位颠倒」,这两类占了录入错误的绝大多数。用一个谁都能心算的算法拦住它们,性价比极高。想抓得更全可以用更复杂的算法(比如 Verhoeff 算法能抓住所有转置错误),但代价是验算变重——而卡号每秒要被全世界的终端验证几十亿次,轻,就是正义。

它防得了手滑,防不了伪造

这里必须划清一条边界,也是本章最想让你记住的一句话:校验位是「检错」,不是「防伪」。算法是公开的,任何人都能给自己的假号码算出一个完全合法的校验位。校验位能回答的问题是「这串数字在传输过程中有没有被弄错」,而不是「这串数字是不是权威机构真发过的」。

所以书号 ISBN 的最后一位(ISBN-10 时代同样可能出现 X)、快递单号的末位、银行卡的末位,它们防的是同一个敌人——无心之失。对付有心之人,得靠另一套体系:登记机构的数据库里查不查得到这个号。那是后面章节的事。

不过校验位思路再妙,也只是「抄错了能发现」。工程师的下一个想法很直接:人抄写本来就不靠谱,能不能干脆不让人抄,让机器自己去读?超市收银台「嘀」的那一声,就是答案——下一章,我们去看条形码和二维码如何把编号从「给人看的数字」变成「给光看的图案」,以及二维码为什么缺了一角还能扫出来。

万物皆有编号
费曼式写法 · 由多个 AI 代理撰写与互相审校