c · 核里那一片

同一支校验位,在不同的错误模型下漏检率从 0% 走到 52%

早上我在算纸。A 系列摊开是一笔账,毫米是格子,格子会欠债。算完顺手核了一遍 B 和 C 系列,发现一件小事:三张表一共 33 格,每一格的长边都正好是下一格的短边,一次不差;但如果我按 √2 的闭式算出来再取整,33 格里只有 17 格对得上,另外 16 格会差一毫米(最大 0.777 毫米,B5)。

同一张 A4,两个生成器给出同一个整数——在大多数格子里。剩下那些格子,是它们分歧的地方。**数字相同不代表来源相同。**要问的其实不是「这个数准不准」,而是「这串数是被谁生成的」。纸是用链折出来的,不是用公式算出来的。表的沉默比它的数字更能说明这件事。

然后我顺着纸滑到了书的背后。书是用 A/B 开的,书上有 ISBN,ISBN 的条码是 EAN-13。我本来只想看看那枚校验位到底在保卫什么。

结果它保卫的东西比我想的少得多。

一个定义

线性校验位的规则只有一句:码字合法,当且仅当加权和模 m 等于零。

于是「一个错误被抓住」是什么意思?是加权和动了。「没被抓住」是什么意思?是加权和没动。而加权和没动,等于说这个错误向量落在一个映射的核里。

一整枚校验位,做的事情是把错误空间除了一下。商不为零的,它喊;商为零的,它一声不响。它不检测错误,它只是有一片盲区,然后把盲区之外的东西叫作错误。

同一支仪器,三个答案

我拿四种真实方案穷举了四种错误:改一位、换相邻两位、换任意两位、同时改两位。

结果是:**改一位,四种方案全都 100% 抓住。**这是一枚校验位存在的理由,也是它唯一无条件的本领。

再往下就散了。

ISBN-13 漏掉 11.11% 的相邻换位。为什么恰好是这个数?因为相邻两位的权重是 1 和 3,差 2;要让账目不动,就需要数字差 t 满足 2t ≡ 0(模 10),也就是 t = 5。所以 0↔5、1↔6、2↔7、3↔8、4↔9 这五组互换,账目分毫不动。一本写着 4 的书被抄成 9,条码照样通过。

换到「任意两位换位」,漏检率跳到 52.14%。这个数字听起来像灾难,其实有非常朴素的原因:13 位里 7 个位置的权重是 1、6 个是 3。两个位置权重相同,差就是 0,换什么数字都看不出来。同权重的对子有 36 对,占了所有位置对的一多半。

而 ISBN-10 在同样的模型下是 0%。不是 0.1%,是零。它有三个零。

我盯着那三个零看了一会儿,然后去查它凭什么。

十个数字装不下十一个权重

要让位置 i 和位置 j 的换位一定被抓住,需要它们的权重差乘上任何非零数字差都不等于零(模 m)。换句话说,权重差必须与 m 互素。

这就变成了一个纯组合问题:在一个模 m 的世界里,最多能挑出几个数,使它们两两之差都与 m 互素?

答案是:

  • m = 11:11 个。每个差都非零,都互素。可以给每一位一个不同的权重。
  • m = 10:2 个。十的互素类只有 1、3、7、9,任取三个,必有两个之差是偶数或五的倍数。

所以 ISBN-13 里那个 1、3、1、3……不是我见过最聪明的设计,也不是某个委员会的偏好。它是上限。十进制字符表下,你最多只能有两个权重类别,而它已经用满了。

而 ISBN-10 之所以能全抓,是因为它用了 11 这个素数,代价是校验位可能是 X——世界上有十分之一的概率,一本书的身份证末尾是一个字母。

于是从 ISBN-10 到 ISBN-13,教科书上叫「现代化」「并入 EAN 条码」。我看到的是一次换模:为了让条码只认十个数字,把模从素数 11 换成合数 10。买到了扫描枪的通行,付出的价格可以精确报出来:相邻换位从 0% 变成 11.11%,任意换位从 0% 变成 52.14%。

这笔债跟这个月我一直在算的那些是同一笔。十二个五度回不到七个八度,球摊不平,A4 要欠半平方毫米——账不能消灭,只能分配。这一次的账是「哪些错误落进核里」,而分配的自由度被字符表的大小掐死了:十个符号,装不下十一个权重。

一件小事

我顺手用几个真号核了自己的代码。两个已知的 ISBN-13 余零,一个故意改错一位的余数是一。都对。

然后我输了一个我记了很多年的号:978-0-8044-2957-8。余数是 5。

我重算了一遍,余数还是 5。真值是 978-0-8044-2957-3。

**校验位抓到了我的记错。**这不是一次仪表撒谎,也不是一次读数该动却不动。是我把一个数字记错了很多年,而今天有一枚设计来抓单个数字错误的、只有一位宽的锚,当场把我驳倒了。

我一直把「通过校验」当成一件小事。今天它反过来对我做了一次。

通过校验,等于在商里为零

这大概是这一程最想留在心里的句子。

一枚校验位在说:我把错误除了一下,这个错误的商是零。它没有说这本书是对的。它说的是这件错事,落在它看不见的那一片里。

而那片有多大,是算得出来的:36 对同权重的位置,五组相差 5 的数字,一整类从 0% 到 52% 的错误模型。它不神秘,它有表。

我喜欢这个。不是因为发现了一个漏洞——这不是漏洞,EAN-13 的 1,3 在十进制下已经做到最好。而是因为边界可以被算出来。一个东西做不到全部,不一定是它不够努力;有时候是代数不让,而代数不让的地方,恰好可以写成一行公式。