← 返回博客

个人博客

混合系统:乘积折叠,以及现实里的校验码(GB/T 17710 续二)

上篇讲了纯系统(一个模数、加权和取模)。这一篇讲 GB/T 17710 的另一半——混合系统。它的绝活是:用两个模数做"乘积折叠",让校验字符永远留在原生字符集里(数字就是数字、字母就是字母,绝不冒出 X 这种补字符)。

一、为什么还要"混合系统"

纯系统为了在区间里留出"补字符"位置(比如 MOD 11-2 用 X 表示 10),字符集总比模数小一点。混合系统换了个思路:同时用两个模数 M₁ 和 M₂,且 M₁ = M₂ + 1。因为 M₂ 正好等于字符集大小,校验字符直接落在原生集里,不会出现补字符。

标准里混合系统只有三种,每种都只挂 1 位校验字符:

  • MOD 11,10:M₁=11, M₂=10(数字 0-9)
  • MOD 27,26:M₁=27, M₂=26(字母 A-Z)
  • MOD 37,36:M₁=37, M₂=36(0-9、A-Z)

注意:需要 2 位校验时,标准走的是纯系统(MOD 97-10 两位数字、MOD 661-26 两位字母、MOD 1271-36 两位字母数字,见上篇),而不是混合系统。混合系统统一只出 1 位。

二、乘积折叠:核心算法

混合系统不堆权重,而是边走边折。维护一个"乘积" P,初始等于 M₂。对每一个字符(从左到右,取值记为 a):

sum = (a + P) mod M₂
若 sum 为 0,则置为 M₂
P   = (sum × 2) mod M₁      (三种混合系统统一用乘数 2)

全部数据字符走完后,校验字符:

check = (1 − P) mod M₂      (等价于 (M₁ − P) mod M₂,因为 M₁ = M₂ + 1)

验证时把"数据 + 校验字符"整串再走一遍同样的折叠,最后一步的中间和必须等于 1。等于 1 就说明没出错。对数字系统 MOD 11,10,你能在血袋号 "0823"→"08235"、德国税号、克罗地亚 OIB 里见到它。

flowchart LR P0["P 初始 = M2"] --> S1["sum = (a + P) mod M2"] S1 --> S2["sum 为 0 则置 M2"] S2 --> P1["P = (sum * 2) mod M1"] P1 --> Q{"还有字符?"} Q -->|是| S1 Q -->|否| C["check = (1 - P) mod M2"]

三、手算 MOD 11,10

取数据 "079"(数字,M₁=11, M₂=10):

P 起始 = 10
'0': sum = (0+10) mod 10 = 0 → 置 10;P = (10×2) mod 11 = 9
'7': sum = (7+9)  mod 10 = 6;        P = (6×2)  mod 11 = 1
'9': sum = (9+1)  mod 10 = 10;       P = (10×2) mod 11 = 9

数据走完,P = 9。校验字符 check = (1 − 9) mod 10 = 2。完整串 "0792"。

验证 "0792" 再折一遍:最后一步的中间和恰好回到 1,通过。这个算法真实世界在用——德国个人税号 Steuer-IdNr、克罗地亚个人标识 OIB、以及 ISO 血袋号都是它。例如血袋号 "0823" 算出来是 "08235"。

四、另外两种:MOD 27,26 与 MOD 37,36

它们和 MOD 11,10 跑同一套乘积折叠,只是字符集和模数不同:

  • MOD 27,26:纯字母 A-Z(26 个),挂 1 位字母校验——可以看成 MOD 11,10 的"字母版"。
  • MOD 37,36:0-9、A-Z(36 个),挂 1 位校验——MOD 11,10 的"字母数字版"。

例如对字母串 "JEJLMGJ" 跑 MOD 27,26,算出的校验字母是 S(完整串 "JEJLMGJS");对字母数字长串 "B7Q3SFTUSH2QN7BIXBPMNZAM" 跑 MOD 37,36,校验字符是 I。这些都能用同一段折叠代码算出来——差别只在字符取值表和模数。

五、现实里那些"不是 ISO 7064"的校验码

讲完标准,必须泼一盆冷水:很多你天天见的校验码,借了"加权和取模"的思想,但参数各自定制,并不等于 GB/T 17710 的某个系统。看清楚它们的骨架,才不会张冠李戴。

IBAN:确实用 MOD 97-10 的"模 97"思路,但先把字母 A-Z 映射成 10-35(A=10、B=11、…、Z=35),把整串变成纯数字后再做 mod 97。所以它是对 ISO 7064 的定制扩展,不是裸 MOD 97-10。

中国居民身份证(GB 11643):末位校验。它是"固定权重加权 mod 11"——对前 17 位套一组固定权重 [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2],求和后 mod 11,结果映射到 0-9 或 X。注意权重不是 2 的幂,所以不是 GB/T 17710 的 MOD 11-2,只是同属"加权 mod 11"大类。

统一社会信用代码(GB 32100):18 位,末位校验用 mod 31 + 一组固定权重 + 一个自定义 31 字符集 0123456789ABCDEFGHJKLMNPQRTUWXY(去掉了 I、O、S、V、Z)。模数、权重、字符集全是自己定义的,和 ISO 7064 完全不是一套参数。

结论很朴素:GB/T 17710 提供的是"标准件"校验字符系统;身份证、统一社会信用代码、IBAN 都是"借了取模思想、但参数各自定制"的方案。理解 GB/T 17710,能帮你一眼看穿它们的共同骨架——加权和取模、末尾挂校验——但具体权重和字符集,必须按各自的国标或规范来,不能混用。