寒假在家看到永乐大帝发的关于身份证最后一位校验码的视频的时候就觉得挺有意思,Barkure也发了一篇文章介绍MOD 11-2 的算法,暑假在家无聊,就来造个轮子吧,另外附上了一些代码。
身份证号码组成 {#menu_index_1}
为了方便,我这里也简单介绍一些关于身份证号码的组成。
我们常使用的18位身份证号码采用的是GB11643-1999规范,从左到右,依次是6位地址码 + 8位出生日期 + 3位顺序码 + 1位校验码,前17位也叫做本体码 (master number),最后一位校验码(check number),也就是本篇文章重点讨论的部分。
MOD 11-2 {#menu_index_2}
身份证号码的最后一位校验码采用MOD 11-2校验方式,通过添加这一个校验位,可以大大降低身份证号码输错的概率。
校验算法 {#menu_index_3}
$$ \sum_{i=1}^{18} a_i \times W_i \equiv 1\;(\rm{mod}\;11) $$
这是最后校验的公式,下面我们来一点一点介绍它:
- $i$,表示身份证号码从右往左的序号(1~18)
- $a_i$,表示序号$i$对应的身份证号码
- $W_i$,是对应序号$i$的加权因子,是一组固定的数字,通过公式$W_i = 2^{i-1}\;(mod\;11)$计算得出
弄清楚他们三个的含义,现在再回来看一下上面的校验公式,即把每个$a_i \times W_i$的结果相加得到的和,再除以11取余数,如果这个余数为1,那么校验正确,反之校验错误。
举个栗子 {#menu_index_4}
身份证号码:340102200003076055
通过表格表现$i$、$a_1$和$W_i$的关系:
| $i$ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | |-------|:-:|:-:|:-:|:-:|:-:|:--:|:-:|:-:|:-:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:| | $a_i$ | 5 | 5 | 0 | 6 | 7 | 0 | 3 | 0 | 0 | 0 | 0 | 2 | 2 | 0 | 1 | 0 | 4 | 3 | | $W_i$ | 1 | 2 | 4 | 8 | 5 | 10 | 9 | 7 | 3 | 6 | 1 | 2 | 4 | 8 | 5 | 10 | 9 | 7 |
计算$a_i$和$W_i$的乘积和:
$$ \sum_{i=1}^{18}a_i \times W_i=5\times1+5\times2+0\times4+\cdots+4\times9+3\times7=199 $$
用这199去除以11,得余为1,校验正确
**PS:**由于校验位共有11种可能,即(0~10),所以有的身份证号校验位是X,其实是罗马数字10。
代码 {#menu_index_5}
#MOD 11-2 身份证号校验
idCode = input("请输入18位身份证号:")
S = 0
for i in range(18):
wi = pow(2,i,11)
if idCode[-i-1] in ["X","x"]:
ai = 10
else:
ai = eval(idCode[-i-1])
S += wi * ai
if S%11 == 1:
print("校验正确")
else:
print("校验错误")
计算校验位 {#menu_index_6}
已知前17位本体码 ,可以通过计算得出最后一位校验码$a_1$:
$$ a_1=(12-\sum_{i=2}^{18}a_i\times W_i \;\rm{mod}\;11)\;\rm{mod}\;11 $$
举个栗子 {#menu_index_7}
同样还是上面的例子,不过这里假设我们只知道前17位:34010220000307605
继续列出$i$、$a_1$和$W_i$的关系表格:
| $i$ | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | |:-----:|:-:|:-:|:-:|:-:|:--:|:-:|:-:|:-:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:| | $a_i$ | 5 | 0 | 6 | 7 | 0 | 3 | 0 | 0 | 0 | 0 | 2 | 2 | 0 | 1 | 0 | 4 | 3 | | $W_i$ | 2 | 4 | 8 | 5 | 10 | 9 | 7 | 3 | 6 | 1 | 2 | 4 | 8 | 5 | 10 | 9 | 7 |
计算$a_i$和$W_i$的乘积和:
$$ \sum_{i=2}^{18}a_i \times W_i=5\times2+0\times4+6\times8+\cdots+4\times9+3\times7=194 $$
然后计算12减去194除以11的余,即:$12-194 \;\rm{mod}\;11=5$
最后$a_1=5\;\rm{mod}\;11=5$,所以完整的身份证号码为:340102200003076055
代码 {#menu_index_8}
#MOD 11-2 计算校验位
idCode = input("请输入身份证号前17位:")
S = 0
for i in range(17):
wi = pow(2,i+1,11)
ai = eval(idCode[-i-1])
S += wi * ai
a1 = (12 - S%11) % 11
print(idCode + str(a1))
参考资料 {#menu_index_9}
Barkure《身份证中的 MOD 11-2 校验》