返回列表 发帖

编程挑战-缺失的身份证号码

本帖最后由 happy886rr 于 2017-4-10 15:36 编辑

[具有挑战性的问题]
这里给你一些不完整的身份证号码:6110251990062874?0(倒数第二位缺失,用?号替代),看你是否能根据前16位数码和最后一位校验码推测出完整的身份证号?
要求:不限编程语言,不限代码长度,只要能将缺失的?数字补全就行。
比如:
1509231988042396?9
3700001989091894?0
4313021981042877?1
5301021920050801?X
6110251990062874?0
补全后的效果是:
150923198804239619
370000198909189440
431302198104287701
53010219200508011X
611025199006287480COPY
.
.
.
以下是一些辅助信息,有助于你更快的解决该问题。
[身份证号码]
公民的身份证号码,是由前十七位数字码和最后一位校验码构成。其中第17位如果为奇数就是男性,为偶数是女性。COPY
[最后一位校验码是怎么确定的?]
将身份证号前17位数码分别乘以不同的系数,从第一位到第十七位所乘的系数分别为:7 、9 、10 、5 、8 、4 、2 、1 、6 、3 、7 、9 、10 、5 、8 、4 、2;将乘积相加,除以11,看余数是多少?
    余数:0 、1、 2、 3、 4、 5、 6、 7、 8、 9、 10
对应的校验码:1 、0 、X 、9 、8 、7 、6 、5 、4 、3 、2COPY
[校验码计算示例]
某人身份证号码为53010219200508011X,首先我们得出前17位的乘积和(5*7)+(3*9)+(0*10)+(1*5)+(0*8)+(2*4)+(1*2)+(9*1)+(2*6)+(0*3)+(0*7)+(5*9)+(0*10)+(8*5)+(0*8)+(1*4)+(1*2)是189,然后用189除以11得出的结果是189/11=17余2,也就是说余数是2。最后通过对应规则就可以知道余数2对应的校验码是X。所以,可以判定这是一个正确的身份证号码。COPY
[我的代码实现]
cmd脚本
@echo off
call :start 1509231988042396?9
call :start 3700001989091894?0
call :start 4313021981042877?1
call :start 5301021920050801?X
call :start 6110251990062874?0
pause
exit
:start <ID>
setlocal
set ID=%1
set X=10
set QI=79X584216379X58420
for /l %%i in (0,1,15) do (
call set/a "sum+=(%%ID:~%%i,1%%)*(%%QI:~%%i,1%%)"
)
set/a "r=(12-%ID:~-1%)%%11, n=(r+11-sum%%11), 1/(!(n&1))" 2>NUL ||set flag=1
if %n% lss 11 (set/a "n+=11*flag") else (set/a "n-=11*flag")
set/a "n>>=1"
call echo %%ID:?=%n%%%
endlocalCOPY
1

评分人数

本帖最后由 523066680 于 2017-4-10 19:47 编辑

题目好,长见识(不好意思占楼了  
my @list = qw/
1509231988042396?9
3700001989091894?0
4313021981042877?1
5301021920050801?X
6110251990062874?0/;
my @num = qw(7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2);
my %map;
my $n = 0;
grep { $map{ $_ } = $n++ } qw(1 0 X 9 8 7 6 5 4 3 2);
grep { print func($_) , "\n" } @list;
sub func
{
my $s = shift;
my $sum = 0;
my $last = $map{ substr($s, -1, 1) };
my $code;
grep { $sum += substr($s, $_, 1) * $num[$_] } ( 0 .. 15 );
grep { $code = $_ if ( ($_ * 2 + $sum) % 11 == $last) } ( 0..9 );
$s =~s/\?/$code/;
return $s;
}COPY
根据 happy 的提醒,加一个补充,通过判断和计算求出缺失的数字而非遍历
my @list = qw/
1509231988042396?9
3700001989091894?0
4313021981042877?1
5301021920050801?X
6110251990062874?0/;
my $n = 0;
my %map;
my @num = qw(7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2);
grep { $map{ $_ } = $n++ } qw(1 0 X 9 8 7 6 5 4 3 2);
grep { print func($_) , "\n" } @list;
sub func
{
    my $s = shift;
    my $sum = 0;
    my $last = $map{ substr($s, -1, 1) };
    my $code;
    grep { $sum += substr($s, $_, 1) * $num[$_] } ( 0 .. 15 );
    $test = $sum % 11;
    $code = ( $last - $test ) / 2;
    $code += 11/2 if ( $code < 0 );
    $code += 11/2 if ( int($code) != $code );
    $s =~s/\?/$code/;
    return $s;
}COPY
1

评分人数

[url=][/url]

TOP

本帖最后由 pcl_test 于 2017-4-10 18:25 编辑
@echo off
powershell ^
    $a='^
        1509231988042396?9^
        3700001989091894?0^
        4313021981042877?1^
        5301021920050801?X^
        6110251990062874?0';^
    $n=@{0=7;1=9;2=10;3=5;4=8;5=4;6=2;7=1;8=6;9=3;10=7;11=9;12=10;13=5;14=8;15=4};^
    $m=@{'1'=0;'0'=1;'X'=2;'9'=3;'8'=4;'7'=5;'6'=6;'5'=7;'4'=8;'3'=9;'2'=10};^
    $a.trim() -split '\s+'^|%%{^
        $r=$_.trim();$s=0;^
        0..15^|%%{$s+=1*$r[$_].ToString()*$n[$_]};^
        $t=0..9^|?{((($s-$m[''+$r[-1]])+2*$_)%%11) -eq 0};^
        $r.replace('?',$t);^
    }
pauseCOPY
2

评分人数

TOP

回复 2# 523066680
方法很多,最简单的还是0~9一个一个试,但是那样得用两次循环,我是用set/a逆推出数字,因此这道题最少只需for一次。

TOP

本帖最后由 523066680 于 2017-4-10 18:48 编辑

回复 4# happy886rr


    想过, SUM1 + 2*n = LastNumber,通过 SUM1的余数 和 LastNumber映射的余数进行计算,可以得到 n ,不过我猜我用的方案可能不够直接,
需要做 if 判断, 所以还是先粗暴循环了(一般都是先贴,后面再改 )
[url=][/url]

TOP

本帖最后由 happy886rr 于 2017-4-10 18:51 编辑

回复 5# 523066680
是个同余原理,三种情况,比从0~9试10次要快点。这个题身份证只是缺失一位,如果缺失两位,在知道性别的话,也能算出来,但是难度太大了。

TOP

从校验码计算应得余数 -> 减去前16位已得余数, 得余数偏差, 偏差为负则 加 11, 得非负余偏差后, 判断奇偶, 非负余偏差是奇数就加11, -> 最后除以 2(第17位的系数) 商就是第17位的数字
@echo off & setlocal enableDelayedExpansion
for %%i in (
1509231988042396#9
3700001989091894#0
4313021981042877#1
5301021920050801#X
6110251990062874#0
) do (
    set "d=%%i"
    set "c=!d:~-1!"
    set /a "c=!c:x=10!,  t= c - 2>>31, r=t&(c^^1)|~t&(12-c), dr= r - ( (!d:~0,1!+!d:~10,1!)*7+(!d:~1,1!+!d:~11,1!)*9+(!d:~2,1!+!d:~12,1!)*10+(!d:~3,1!+!d:~13,1!)*5+(!d:~4,1!+!d:~14,1!)*8+(!d:~5,1!+!d:~15,1!)*4+!d:~6,1!*2+!d:~7,1!+!d:~8,1!*6+!d:~9,1!*3 ) %% 11, u=dr+(dr>>31&11), v=(u+(u&1)*11)/2"
    echo;!d:~0,-2!!v!!d:~-1!
)
pauseCOPY
2

评分人数

TOP

本帖最后由 老刘1号 于 2017-4-10 22:37 编辑

存批,暴力解法
'&@(cls&cscript -e:vbscript -nologo "%~0"&pause&exit)
Dim ID_Card(5)
ID_Card(1) = "1509231988042396?9"
ID_Card(2) = "3700001989091894?0"
ID_Card(3) = "4313021981042877?1"
ID_Card(4) = "5301021920050801?X"
ID_Card(5) = "6110251990062874?0"
Dim NUMBER_Multiplie(17)
NUMBER_Multiplie(1) = 7
NUMBER_Multiplie(2) = 9
NUMBER_Multiplie(3) =10
NUMBER_Multiplie(4) = 5
NUMBER_Multiplie(5) = 8
NUMBER_Multiplie(6) = 4
NUMBER_Multiplie(7) = 2
NUMBER_Multiplie(8) = 1
NUMBER_Multiplie(9) = 6
NUMBER_Multiplie(10)= 3
NUMBER_Multiplie(11)= 7
NUMBER_Multiplie(12)= 9
NUMBER_Multiplie(13)=10
NUMBER_Multiplie(14)= 5
NUMBER_Multiplie(15)= 8
NUMBER_Multiplie(16)= 4
NUMBER_Multiplie(17)= 2
Dim Check_Code(10)
Check_Code(0) = 1
Check_Code(1) = 0
Check_Code(2) ="X"
Check_Code(3) = 9
Check_Code(4) = 8
Check_Code(5) = 7
Check_Code(6) = 6
Check_Code(7) = 5
Check_Code(8) = 4
Check_Code(9) = 3
Check_Code(10)= 2
Rem 代码开始:
For NO = 1 To 5
Result = Empty
Calculate = 0
For Multiplie = 1 To 16
Calculate = Calculate + Mid(ID_Card(NO),Multiplie,1) * NUMBER_Multiplie(Multiplie)
Next
For Test = 0 To 10
If CStr(Check_Code((Calculate + Test * NUMBER_Multiplie(17)) Mod 11)) = Mid(ID_Card(NO),18,1) Then Result = Test
Next
Result=Left(ID_Card(NO),16)&Result&Right(ID_Card(NO),1)
WScript.Echo ID_Card(NO) & " -- > " & Result
next
COPY
1

评分人数

TOP

多个循环了
@echo off & setlocal enabledelayedexpansion
set "s=10X98765432"
set "sx=79a584216379a5842"
for /l %%i in (0,1,10) do set #!s:~%%i,1!=%%i
for /f "delims=#" %%i in ('findstr /ib # %0') do (
    set id=%%i
    set e=!id:~-1!
    set n=0&set "?="
    for /l %%k in (0,1,16) do (
        set /a n+=!id:~%%k,1!*0x!sx:~%%k,1!
        set "!id:~%%k,1!=!sx:~%%k,1!"
    )
    set /a n%%=11
    if defined ? for /l %%i in (0,1,9) do (
       set/a "x=(%%i*?+n) %% 11"
       for %%j in (!e!) do if !x! equ !#%%j! set "id=!id:?=%%i!"
    )
    echo;!id!
)
pause & exit
#15?923198804239619
#3700001989091894?0
#4313021981042877?1
#5301021920050801?X
#611?25199006287480COPY
1

评分人数

TOP

@echo off
setlocal enabledelayedexpansion
set idbox=1509231988042396?9;3700001989091894?0;4313021981042877?1;5301021920050801?X;6110251990062874?0
set muli=7910584216379105842
set idbox=%idbox:?=#%
set muli=%muli:10=t%
set muli=%muli: 、=%
set /a t=10
set yushu=0123456789t
set code=10X98765432
for /l %%i in (0 1 10) do call set ".%%code:~%%i,1%%=%%yushu:~%%i,1%%"
for %%a in (%idbox%) do (
set id=%%a
set/p=!id:#=?!<nul
for /l %%b in (0 1 15) do (
call set /a "sum+=%%id:~%%b,1%%*%%muli:~%%b,1%%"
)
for %%c in (0 1 2 3 4 5 6 7 8 9) do (
if not defined yes (
set /a t_sum=sum+%%c*2
set /a t_mod=t_sum%%11
set t_mod=!t_mod:10=t!
for /f "delims==" %%d in ('set .^|findstr /e !t_mod!') do (
if "%%d"==".!id:~-1!" (
echo;         应该是:!id:#=%%c!
set yes=1
)
set "t_sum=" & set "t_mod="
)
)
)
set "sum="
set "yes="
)
echo;over&pause>nul&exit /bCOPY
1

评分人数

TOP

返回列表