标题: 大陆地区18位身份证校验码算法 创建: 2021-09-02 11:42 更新: 2025-11-09 22:22 链接: https://scz.617.cn/python/202109021142.txt 黄健楸贴了个函数 -------------------------------------------------------------------------- # # s取身份证前17位 # def checksum ( s ) : return str( ( 1 - 2 * int( s, 13 ) ) % 11 ).replace( '10', 'X' ) -------------------------------------------------------------------------- >>> ( 1 - 2 * int( '43010119600101131', 13 ) ) % 11 8 虽然我一直知道尾数是校验码,但我从未去关注过这个算法。刚才试了一下,对得上。 居然是用13进制,这么邪门,不知最初选用这个进制的理由是啥。 想了一下13进制这事,补充点讨论。 N=int( s, 13 ) CKSUM≡(1-2N)≡(-13N+1)(mod 11) ID=13N+CKSUM≡(13N+(-13N+1))≡1(mod 11) 前述算法的另一种等价形式: -------------------------------------------------------------------------- # # s取身份证前17位 # def checksum ( s ) : return str( ( int( '-' + s + '0', 13 ) + 1 ) % 11 ).replace( '10', 'X' ) -------------------------------------------------------------------------- 假设18位身份证号是"430101196001011318",这是我杜撰的一个身份证号,勿对号入 座。上述算法实际是将字符串"-430101196001011310"(原尾数用0替换)视为13进制整 数,加1,再模11得到余数作为校验码。为了用单个字符表示校验码,模11得到余数 是10时用X代替。 >>> ( int( '-' + '43010119600101131' + '0', 13 ) + 1 ) % 11 8 -------------------------------------------------------------------------- # # 错误算法示例 # # s取身份证前17位 # def checksum_error ( s ) : return str( int( '-' + s + '1', 13 ) % 11 ).replace( '10', 'X' ) -------------------------------------------------------------------------- checksum与checksum_error不等价,不要误用checksum_error。 >>> int( '-' + '43010119600101131' + '1', 13 ) % 11 '6' 校验18位身份证号时,进行如下运算,结果应该恒为1 >>> int( '430101196001011318', 13 ) % 11 1 由于校验码(尾数)可能是X,无法如此简写,但数学原理如此。 初等数论中定义有"模n的r次同余方程" 设n是正整数,a_i是整数,a_r不是n的倍数(包括0),f(x)是r次多项式: f(x)=a_r*x^r+a_r_1*x^(r-1)+...+a_2*x^2+a_1*x+a_0 则: f(x)≡0(mod n) 称作模n的r次同余方程。 由于a_0可以是[0,n)区间的任意整数,所以"模n的r次同余方程"可以写成: f(x)≡m(mod n) 同余m不一定要求是0。 令 r=17 n=11 m=1 a_17至a_0依次对应左起18位身份证数字 f(x)=a_17*x^17+a_16*x^16+...+a_2*x^2+a_1*x+a_0 求解"模11的17次同余方程": f(x)≡1(mod 11) a_17*x^17+a_16*x^16+...+a_2*x^2+a_1*x+a_0≡1(mod 11) 以"430101196001011318"为例,求解同余方程: 4x^17+3x^16+x^14+x^12+x^11+9x^10+6x^9+x^6+x^4+x^3+3x^2+x+8≡1(mod 11) 可用WolframAlpha求解高次同余方程: https://www.wolframalpha.com/ 在输入框中直接粘贴前述同余方程,点击"等号"求解。 也可用SageMath求解高次同余方程: -------------------------------------------------------------------------- /home/scz/src/sage/sage %autoindent off -------------------------------------------------------------------------- R. = PolynomialRing(GF(11)) f = 4*x^17+3*x^16+x^14+x^12+x^11+9*x^10+6*x^9+x^6+x^4+x^3+3*x^2+x+8 h = f - 1 # print( f ) # print( h ) solutions = h.roots( multiplicities=True ) if solutions : roots = [root[0] for root in solutions] for root in sorted( roots ) : print( f"x≡{root}(mod 11)" ) -------------------------------------------------------------------------- 上例有两个解,其中2是最小正整数解 x≡2(mod 11) x≡3(mod 11) 2是解时,2+11k也是解。 Python的int()的第二形参对应base,身份证号会出现2以上数字,base不能用2,可 用2+11=13。在有限域GF(11)上,13与2是等价的。单说base,确实可以说18位身份证 号是用2进制;之前有人说,就是2进制,误以为TA在装B,是我肤浅了。不过,进制 一般包含base及单个数位可用符号,2进制可用符号只有0、1,因此说用13进制更靠 谱。 假设将来身份证号进一步升位,冪变、模变、同余变,求解高次同余方程,仍可套用 前述算法框架。没看过18位身份证号原始算法文档,仅出于数学爱好瞎讨论一番,万 勿当真。 *** 2025.11.8 有些讲解18位身份证校验码算法的文章,出现下述写法: S=7a1+9a2+10a3+5a4+8a5+4a6+2a7+a8+6a9+3a10+7a11+9a12+10a13+5a14+8a15+4a16+2a17 上式a1对应身份证号最左的数字,a17对应校验码左侧的数字,a18对应校验码。a1到 a17的系数实际是2(或13)的幂模11的结果: -------------------------------------------------------------------------- 2^17 % 11 = 7 // pow(2, 17, 11) 2^16 % 11 = 9 // pow(2, 16, 11) ... 2^1 % 11 = 2 // pow(2, 1, 11) -------------------------------------------------------------------------- 此法求出"S % 11"后,再查一张表得到校验码: S % 11 => 0 1 2 3 4 5 6 7 8 9 10 a18 => 1 0 X 9 8 7 6 5 4 3 2 <= 校验码 并不需要查表,实际有 a18 = ( 1 - S ) % 11 其数学原理是: N=int( s, 13 ) S≡13N≡2N(mod 11) CKSUM≡(1-2N)≡(1-S)(mod 11) S => 0 1 2 3 4 5 6 7 8 9 10 <= 2N % 11 CKSUM => 1 0 X 9 8 7 6 5 4 3 2 <= ( 1 - S ) % 11 如下Python代码等价于checksum() -------------------------------------------------------------------------- def f1 ( s, base=2, module=11 ) : a = [int(c) for c in s] t = sum(a[i] * base ** (17 - i) for i in range(17)) return ( 1 - t ) % module def f2 ( s, module=11 ) : a = [int(c) for c in s] w = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] t = sum(a[i] * w[i] for i in range(17)) return ( 1 - t ) % module # # 取'430101196001011318'前17位 # s = '43010119600101131' f1(s) f1(s,13) f2(s) -------------------------------------------------------------------------- 直接用7、9这样的系数,避免高次幂运算,只有乘法运算,有利于手工计算、电路设 计。但对于编程,意义有限。对于理解数学原理,更不适用。