原文发布于自己的博客平台【http://www.jetchen.cn/id-card/】
上篇介绍身份证信息的文章因为贴了微信公众号相关的信息导致文章被转为了私密文章,然而没有好好珍惜申诉的机会,所以在此重新再发一遍原文。
对于身份证号码,你是否还停留在可以知晓出生地和出生年月日的层面上?其实它背后也有着丰富而优美的数学知识,本文带你深度剖析它。
背景
身份证号码是中国大陆每个人的身份标识,是唯一的(除开一些老的没有更换的身份证),它记录每个人所有的信息。在日常的银行卡开户、社保开户等都必须使用到,是每个人至关重要的身份信息。
目前大陆的第二代身份证号码是由 18 位数字(最后一位如果是 x 其实代表数字 11)组成的,这 18个数字,每一个都有着深刻的意义。
本文借此 2020 年的契机,以今年的幸运身份证号码为例来进行下解读,这个身份证号码有多幸运呢?因为它的组成只有数字 2 和 0,当你给别人报身份证号的时候,别人还以为你在开玩笑呢。
案例身份证号:220202 20200202 002 2
前 17 位
身份证号码的前 17 位,其实是比较简单的,绝大部分人也是可以看懂的,我们就以上面的案例身份证为例来解读。
220202:这最前面的 6 位,代表了出生的省市区,即 吉林省 吉林市 昌邑区
20200202:中间的这 8 位,代表了出生的年月日,即 2020 年 2 月 2 日
002:排除末位后倒数的这三个数,是序列号,是按序来排的,也就是按照登记顺序来排序,但是其中的最后一位代表了性别,即奇数代表了男性,偶数代表了女性。
所以说,上述的幸运儿肯定是个位女性。
最后一位数字的意义
最后一位,其实是校验码,主要作用是用于校验前面 17 位数字的正确与否。
先说下整个的数学公式吧:
"",称为“同余”
"mod 11",称为“对 11 取模”
上述公式的意思是:
按公式
对 18 位身份证号码进行求和,得出来的值对 11 取模,得到的值必为 1
下标 i:
最后一位是 1,然后依次往左,即左边第一位数字的下标是 18
ai:
ai 代表每一位身份证号码,比如上述的身份证号码案例,从左到右为:
, 。。。
wi:
wi 的计算公式是:
,
比如上述案例,从左到右依次为:
,
,
。。。
,
用表格罗列如下:
身份证号码 | 2 | 2 | 0 | 2 | 0 | 2 | 2 | 0 | 2 | 0 | 0 | 2 | 0 | 2 | 0 | 0 | 2 | 2 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
ai | 2 | 2 | 0 | 2 | 0 | 2 | 2 | 0 | 2 | 0 | 0 | 2 | 0 | 2 | 0 | 0 | 2 | 2 |
wi | 7 | 9 | 10 | 5 | 8 | 4 | 2 | 1 | 6 | 3 | 7 | 9 | 10 | 5 | 8 | 4 | 2 | 1 |
所以求和为:
将求和得出的 100 对 11 取模,得出结果为 1,这正是上述公式期望得到的结果。
到此,验证完毕。
所以,赶快掏出自己的身份证号码来验证下吧,是不是打开了新世纪的大门。
最后一位数字的生成
上面是使用整个 18 位号码来进行验证的,接下来我们来实现:通过前 17 位数字来计算第 18 位数字。
当然,你进行穷举,即最多计算 11 次,也可以计算末尾数字,但是那样岂不是太弱了。
还是老样子,先公布下公式:
公式其实也不难,我们来解读下:
- 将前 17 位号码(ai)分别和对应的系数(wi)想乘
- 得到的结果进行相加
- 将求和得到的值对 11 取模运算
- 用 12 减去上面第 4 步得到的值
- 再次将上面第 4 步得到的值对 11 取模,得到的数便是身份证号码的末位,该数值必然是 0~10 之间的数字,如果是 10,则使用 X 来表示
还是以上面提到的身份证为例,我们试着来根据前面的 17 位来推算末位数,还是使用表格来画一下:
身份证号码 | 2 | 2 | 0 | 2 | 0 | 2 | 2 | 0 | 2 | 0 | 0 | 2 | 0 | 2 | 0 | 0 | 2 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
ai | 2 | 2 | 0 | 2 | 0 | 2 | 2 | 0 | 2 | 0 | 0 | 2 | 0 | 2 | 0 | 0 | 2 |
wi | 7 | 9 | 10 | 5 | 8 | 4 | 2 | 1 | 6 | 3 | 7 | 9 | 10 | 5 | 8 | 4 | 2 |
然后按公式来进行计算:
所以,末位数字就是 2 啦。
很简单吧,快快快,拿出你的身份证来验证下吧。
杂谈
很期待看到上面列出来的这个神奇的身份证号的鼠宝宝。
另外,除末位以外的倒数三位,虽然说是按登记顺序进行排序的,但是还有一个规则,就是每个区都会分配一个编号区间,比如上面的 002,其实就是属于 孤店子 这个区域的。
其实,对于上面的案例身份证号码,它并不是唯一的一个只有 2 和 0 的身份证号码,更多的案例,本文暂不赘述。
后记
作为程序猿嘛,肯定要用代码来撸一遍算法的,于是手动撸了一套身份证号码的验证程序,代码如下:
PS:只按照上述算法进行校验,地区和年月日等在此不进行校验
import java.util.Scanner;
/**
* @ClassName: IdCardVerify
* @Description:
* @Author: Jet.Chen
* @Date: 2020/1/20 14:35
* @Version: 1.0
**/
public class IdCardVerify {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入身份证号码,按回车键确定,\r\n输入q则退出。\r\n(如果输入17位,则帮您计算出第18位,如果输入18位,则帮您校验。)");
while (true) {
// 输入项校验
if (scanner.hasNext()) {
String next = scanner.next();
if ("q".equals(next.toLowerCase())) break;
if (next.matches("^\\d{17}$")) {
System.out.println("末位身份证号码为:" + calculateLastDigit(next));
} else if (next.matches("^\\d{17}(\\d|x|X)$")) {
System.out.println(checkIdCard(next) ? "身份证号码校验正确!" : "身份证号码校验错误!");
} else {
System.out.println("身份证号码格式有误,请重新输入:");
}
}
}
scanner.close();
}
/**
* @Description: 计算最后一位
* @Param: []
* @return: int
* @Author: Jet.Chen
* @Date: 2020/1/20 14:54
*/
private static int calculateLastDigit(String str) {
char[] chars = str.toCharArray();
int sum = sum(chars);
return (12 - sum%11)%11;
}
/**
* @Description: 校验身份证
* @Param: []
* @return: boolean
* @Author: Jet.Chen
* @Date: 2020/1/20 14:55
*/
private static boolean checkIdCard(String str) {
char[] chars = str.toCharArray();
int sum = sum(chars);
int last;
if ('x' == chars[17] || 'X' == chars[17]) {
last = 10;
} else {
last = (int)chars[17] - (int)('0');
}
sum += last;
return sum % 11 == 1;
}
private static int sum(char[] chars) {
int sum = 0;
for (int i = 18; i > 1; i--) {
int ai = (int)chars[18-i] - (int)('0');
int wi = (2 << (i-2)) % 11;
sum += ai * wi;
}
return sum;
}
}