M28664: 验证身份证号
dict, http://cs101.openjudge.cn/pctbook/M28664/
中华人民共和国公民的身份证号码由 18 位数字或 X 组成,其中最后一位可能是 X。 身份证号码的前6位表示行政区划代码,第7位到第 14 位表示出生日期,第 15 位到第 17 位表示顺序码,第 18 位表示校验码。 现给定若干个身份证号,请检验身份证号是否合法。如果合法,输出 YES,否则输出 NO。 保证前 17 位数字合法,因此你只需要检验第 18 位校验码是否合法即可。
校验码的计算方法如下: - 将前面的身份证号码 17 位数分别乘以不同的系数。从第1位到第 17 位的系数分别为7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2。 - 将这 17位数字和系数相乘的结果相加。 - 用加出来的和除以 11,看余数是多少。 - 余数只可能有0,1,2,3,4,5,6,7,8,9,10这11个数字。其分别对应的最后一位身份证的号码为1,0,X,9,8,7,6,5,4,3,2。(即余数0对应1,余数1对应0,余数2对应X...)
输入
共几+1行。 第一行一个正整数n,保证1≤n≤50. 接下来几行,每一行为一个身份证号。(若最后一位为 X,则为大写字母 x)
输出
输出n行。 每行表示身份证号码是否合法。如果合法,输出 YES,否则输出 NO。
样例输入
2
371311200312247819
130631197601191234样例输出
YES
NO提示
tags: implementation
来源
2024fall-cs101: Algo DS (476)
n=int(input())
orth=[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
prod=[1,0,"X",9,8,7,6,5,4,3,2]
for k in range(n):
iden=input()
res=0
for k in range(0,17):
res=(res+int(iden[k])*orth[k])%11
if iden[17]=="X":
if res==2:
print("YES")
else:
print("NO")
else:
if prod[res]==int(iden[17]):
print("YES")
else:
print("NO")# 定义权重数组
l = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
# 读取测试用例数量
n = int(input())
for _ in range(n):
s = input()
# 检查输入字符串的长度
if len(s) != 18:
print('NO')
continue
# 计算校验码
x = sum(int(s[i]) * l[i] for i in range(17)) % 11
x = (12 - x) % 11
# 处理特殊情况
if x == 10:
x = 'X'
# 比较校验码
if s[17] == str(x):
print('YES')
else:
print('NO')思路:
本题涉及多个一一对应关系,使用 zip()、列表推导式和 dict(zip()) 能够简洁地建立映射关系。
身份证号码的最后一位是校验码,需要单独提取并与计算结果比对。使用 list.pop() 适合。
代码
n = int(input())
ratio = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
ends = [0,1,2,3,4,5,6,7,8,9,10]
pairs = [1,0,'X',9,8,7,6,5,4,3,2]
dictionary = dict(zip(ends,pairs))
for _ in range (n):
line = list(input())
last = line.pop()
line = [int(i) for i in line]
end = sum(a*b for a,b in zip(ratio,line))%11
real_last = str((dictionary[end]))
if last == real_last:
print('YES')
else:
print('NO')这段代码是一个用于验证中国居民身份证号码(18位)校验位的程序。
# 定义加权因子字符串(通过字母编码,避免显式列表)
# 对应的权重为:[7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1]
# 原理:ord('h') - ord('a') = 104 - 97 = 7,以此类推
WEIGHT_CODE = 'hjkfiecbgdhjkfiecb'
# 模11后对应的校验码余数表(用于验证总和模11是否应为1)
# 这是一个数学性质:合法身份证的加权总和 % 11 == 1
# 详细推导见下方说明
def verify_id_card(id_number):
"""
验证一个18位中国居民身份证号码是否合法(仅校验位验证)
参数:
id_number (str): 18位身份证号码,最后一位可以是'X'或'x'
返回:
bool: 合法返回 True,否则返回 False
"""
# 步骤1:检查长度是否为18位
if len(id_number) != 18:
return False
# 步骤2:将输入中的 'X' 或 'x' 替换为 ':',以便 ord(':') - 48 = 10
# 这是一个巧妙的ASCII技巧,避免额外判断
cleaned_id = id_number.replace('X', ':').replace('x', ':')
# 步骤3:检查前17位是否全为数字,第18位是否为数字或':'
if not cleaned_id[:17].isdigit() or not (cleaned_id[17].isdigit() or cleaned_id[17] == ':'):
return False
# 步骤4:计算加权和
total_weighted_sum = 0
for i in range(18):
# 获取第i位的权重(通过字符编码转换)
weight = ord(WEIGHT_CODE[i]) - ord('a')
# 获取第i位的数值(字符转数字,':' 表示10)
digit_value = ord(cleaned_id[i]) - 48 # ord('0') = 48
total_weighted_sum += weight * digit_value
# 每步取模防止整数溢出(可选,但安全)
total_weighted_sum %= 11
# 步骤5:根据数学性质,合法身份证的加权和模11必须等于1
return total_weighted_sum == 1
def main():
"""主函数:读取多个身份证号码并验证"""
try:
n = int(input().strip()) # 输入测试用例数量
for _ in range(n):
identity = input().strip() # 读取身份证号码
if verify_id_card(identity):
print('YES')
else:
print('NO')
except Exception as e:
# 防止输入异常导致程序崩溃
print("NO")
# 运行程序
if __name__ == '__main__':
main()数学原理 :如果整个18位都合法,则总加权和模11恒等于 1。
(Σ_{i=0}^{17} weight[i] * digit[i]) % 11 == 11.
code = 'hjkfiecbgdhjkfiecb'
这是一个长度为18的字符串,但它实际上代表的是身份证校验算法中的 加权因子(Weight Factors)。
在身份证校验中,每一位有一个固定的加权因子,从左到右依次是:
[7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1]但是这里用字母表示,是因为:
ord('h') - ord('a') = 107 - 97 = 10?不对!实际上,我们来验证一下:
'h'->ord('h')=104->104 - 97 = 7'j'->106 - 97 = 9'k'->107 - 97 = 10'f'->102 - 97 = 5'i'->105 - 97 = 8'e'->101 - 97 = 4'c'->99 - 97 = 2'b'->98 - 97 = 1'g'->103 - 97 = 6'd'->100 - 97 = 3所以
code = 'hjkfiecbgdhjkfiecb'实际上对应的是加权因子数组:[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1]即每一位的权重。
2.
identity = input().replace('X', ':')
读入一个身份证号码字符串。
将其中的
'X'替换为':'。这是关键一步,为什么?原因:
- 身份证最后一位可能是
'X'(代表数字10),但在计算时需要当作10处理。ord('X') = 88,而ord('0') = 48,所以ord('X') - 48 = 40,这不对。- 但
ord(':') = 58,而58 - 48 = 10,正好!所以这句的作用是:
把身份证最后一位的
'X'变成一个 ASCII 码为58的字符(即':'),这样在后续计算ord(ch) - 48时就能得到10。✅ 这是一个巧妙的技巧,避免了单独判断
'X'的情况。3.
print('YES' if summ == 1 else 'NO')这是因为:当整个18位都是合法时,总加权和模11的结果恒为1。
我们来验证一下:
假设前17位加权和为
S,第18位为a,则总加权和为S + 1*a(最后一位权重是1)。设
S % 11 = r,则正确校验位应为check[r],其中check = [1,0,'X',9,8,7,6,5,4,3,2]。那么总加权和为:
S + check[r],模11后应为:(S + check[r]) % 11 = (r + check[r]) % 11我们看是否恒为1:
r check[r] (r + check[r]) % 11 0 1 (0+1)%11 = 1 ✅ 1 0 (1+0)%11 = 1 ✅ 2 'X'=10 (2+10)%11 = 12%11=1 ✅ 3 9 12%11=1 ✅ 4 8 12%11=1 ✅ 5 7 12%11=1 ✅ 6 6 12%11=1 ✅ 7 5 12%11=1 ✅ 8 4 12%11=1 ✅ 9 3 12%11=1 ✅ 10 2 12%11=1 ✅ 惊人发现:无论
r是多少,(r + check[r]) % 11 = 1!所以,只要身份证是合法的,总加权和模11一定等于1。
验证一个18位身份证号码是否合法。利用了一个数学性质:
合法身份证号码的每一位数字乘以其对应的加权因子(7,9,10,...,1),然后总和模11的结果恒为1。
补充说明
- 加权因子:
[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1]- 模11后余数对应的校验码:
[1,0,X,9,8,7,6,5,4,3,2]- 本代码通过
replace('X', ':')巧妙处理'X'(因为ord(':')-48=10)- 最终判断
(总加权和 % 11) == 1来决定是否合法。