玩玩身份证

Author Avatar
Jimmy Zhang Sep 01, 2017
  • Read this article on other devices

前言

最近边写 codgic-api 边啃大学教材好累啊…… 于是打算找点有意思的东西玩玩。

刚才在知乎上看到 刘巍然-学酥 对于身份证的末位校验码算法最后一步模11是基于什么考虑问题的回答后感觉颇有趣味,于是打算再深入理解理解身份证编号的那些事……

友情提示:

  1. 此处身份证指的是中华人民共和国公民身份证,其它证件不在本文讨论范围之内;
  2. 本文大部分是搬运的,别打我……(逃

号码的结构

身份证号码(共 18 位)可以分为四个部分:

  1. 地址码(6 位):公民常住户口所在县(市、镇、区)的行政区划代码。
  2. 出生日期码(8 位):公历年、月、日;
  3. 顺序码(3 位):给同地址码同出生日期码的人编订顺序号,其中奇数分配给男性,偶数分配给女性
  4. 校验码(1 位):采用 ISO 7064:1983,MOD 11-2 校验码系统。校验码为一位数字或 X(校验码为 10 的情况);

以身份证号 11010519491231002X 为例,地址码 110105,也就是北京市朝阳区;出生日期码 19491231,也就是该人出身于 1949 年 12 月 31 日;顺序码 002;校验码 X(也就是10)。

校验码的计算方法

  1. 将身份证号从右至左排列一词标记为 a1a_1, a2a_2, … ,a18a_{18},其中 a1a_1 就是校验码;
  2. 分别计算出每一位的权重 Wi=2i1 mod 11W_i = 2^{i - 1} \ mod \ 11,得到:
    ii181716151413121110987654321
    WiWi79105842163791058421
  3. 计算:S=i=218aiWiS = \sum\limits_{i=2}^{18} a_i \cdot W_i
  4. 校验码:a1=(12(S mod 11)) mod 11a_1 = (12 - (S \ mod \ 11)) \ mod \ 11

让人算好麻烦,顺手撸个校验验证码的程序吧(垃圾代码,黑历史 QAQ):

#include "stdio.h"
#include "stdbool.h"
#include "string.h"
#include "math.h"

_Bool validateId(char id[18])
{
    int checksum, sum = 0;
    for (int i = 0; i < 17; i++)
    {
        sum += ((int)pow(2, 17 - i) % 11) * ((int)id[i] - 48);
    }
    checksum = (12 - (sum % 11)) % 11;
    return (int)id[17] == 88 ? checksum == 10 : checksum == (int)id[17] - 48; // 88 == "X"
}

int main()
{
    char id[18];
    scanf("%s", id);
    printf("%d", validateId(id));
    return 0;
}

为什么要这样设计?

咕咕咕……

参考文献

This blog is under a CC BY-NC-SA 4.0 Unported License.
Link to this article: https://blog.codgician.pw/2017/09/01/learning-more-about-id-card-number/