5
\$\begingroup\$

From Wikipedia:

PESEL is the national identification number used in Poland since 1979. It always has 11 digits, identifies just one person and cannot be changed to another one.

It has the form of YYMMDDZZZXQ, where YYMMDD is the date of birth (with century encoded in month field), ZZZ is the personal identification number, X denotes sex (even number for females, odd number for males) and Q is a control digit, which is used to verify whether a given PESEL is correct or not.

Having a PESEL in the form of ABCDEF GHIJK, one can check the vailidity of the number by computing the following expression:

A*1 + B*3 + C*7 + D*9 + E*1 + F*3 + G*7 + H*9 + I*1 + J*3

Then the last digit of the result should be subtracted from 10. If the result of the last operation is not equal to the last digit of a given PESEL, the PESEL is incorrect. This system works reliably well for catching one-digit mistakes and digit swaps.

Provide a short program to generate random, valid PESEL number:

  1. It has to be a PESEL a person born in either 20th or 21st century
  2. The birth birth date has to be random but doesn't have to validate leap years or number of days in a month (i.e. the numbers representing the day number have to be between 01 and 31)
  3. All valid PESELs in this date range (01.01.1990-31.12.2099) must be possible
  4. The shortest code (i.e. complete program code), in bytes, wins.
\$\endgroup\$
  • 2
    \$\begingroup\$ How random? eg, must all digits vary, must eg all 20th century birthdays be possible, and no others? Seems like this can be subverted by solutions that just coin toss between male and female. \$\endgroup\$ – bazzargh Apr 13 '14 at 9:29
  • 2
    \$\begingroup\$ Actually my point is more that 'random' can mean 'one of a fixed set of choices'. If you pick a PESEL of A001100000Q then Q=10-A, it's still 'random', with a random date, but way less code. Saying that all valid PESELs for your date range must be possible blocks tricks like this. \$\endgroup\$ – bazzargh Apr 13 '14 at 9:44
  • 1
    \$\begingroup\$ Please correct the algorithm: The current version allows the last digit to be 10. The Polish Wikipedia version uses a modulo 10 operation to get 0 in this case. \$\endgroup\$ – Heiko Oberdiek Apr 13 '14 at 12:20
  • 1
    \$\begingroup\$ The gender entry uses the full range 0 to 9 to encode male and female (1 bit). What's the purpose of this "random noise"? Or are there further specification on the gender field that I have missed? \$\endgroup\$ – Heiko Oberdiek Apr 13 '14 at 12:24
  • 3
    \$\begingroup\$ The century is encoded in the month field. This is neither specified in the question or links. For example, it is explained in the Polish Wikipedia article. \$\endgroup\$ – Heiko Oberdiek Apr 13 '14 at 12:26
2
\$\begingroup\$

Perl, 162 bytes

@a=map{$==rand 10}0..9;$"='';$a[5]%=2if($a[4]%=4)==3;"@a[4,5]"||$a[5]++;$a[3]=($a[2]%=4)%2?$a[3]%3:1+$a[3]%9;$f=9;for(@a){$x+=$f*$_;$f-=$f%10==7?4:2}print@a,$x%10

Ungolfed:

# Array with random digits
# ------------------------
@a = map { $= = rand 10} 0..9;
# do not insert a space, if arrays are interpolated into strings
$" = ''; #"

# Day correction
# --------------
# The day must be in the range of "01" to "31":
# * map first digit to "0" to "3": $a[4] %= 4
# * map second digit to "0" to "1" if first digit is "3"
$a[5] %= 2 if ($a[4] %= 4 ) == 3;
# * set day to "01" if day is "00"
"@a[4,5]" || $a[5]++;

# Month correction
# ----------------
# The month must be in the range "01" to "12" or "21" to "32"
# to get a valid month and century:
# * map the month to "00" to "39"
# * if the first digit is odd (catching both centuries at once)
#   * then map the second digit to "0" to "2"      (for months: 10..12)
#   * otherwise map the second digit to "1" to "9" (for months: 01-09)
$a[3] = ($a[2] %= 4) % 2 ? $a[3] % 3: 1 + $a[3] % 9;

# Calculate the control digit
# ---------------------------
# Because of the modulo 10 operations, the formula for the calculation
# of the control digit is rewritten from
# Q = (10 - ((A + 3B + 7C + 9D + E + 3F + 7G + 9H + I + J) % 10)) % 10
# to
# Q = (9A + 7B + 3C + D - E - 3F -7G - 9H - 11I - 11J) % 10
$f = 9;
for (@a) {
    $x += $f * $_;
    $f -= $f % 10 == 7 ? 4 : 2
}

# Print the result and control digit
print @a, $x % 10


# TEST section
# ------------

;$Q = $x % 10; # remember control digit in $Q

# Pretty print PESEL
print "\n\n",
  "YY MM DD ZZZ X Q\n",
  "@a[0,1] @a[2,3] @a[4,5] @a[6,7,8] $a[9] $Q\n";
print "\n";

# Pretty print the date
$month = "@a[2,3]";
$century = "??";
$century = "19" if $month >= 1 and $month <= 12;
$century = "20" if $month >= 21 and $month <= 32;
printf "Date: $century@a[0,1]-%02d-@a[4,5]\n", $month % 20;
print "\n";

# Pretty print the gender
print "Gender: ", $a[9] % 2 ? "male" : "female", "\n";
print "\n";

# Calculate the control date the official way
print "Control digit:\n";
$sum = 0;
$f = 0;
for (my ($i, $f) = (0, 1); $i<10; $i++, $f+=2) {
    $f %= 10;
    $f += 2 if $f == 5;
    printf "  $a[$i] * $f = %2d\n", $a[$i] * $f;
    $sum += $a[$i] * $f;
}
print "  ", "-" x 10, "\n";
printf "  %10d\n", $sum;
printf "  $Q = (10 - ($sum % 10)) % 10 = %d\n", (10 - ($sum % 10)) % 10;

Examples

83102570819

YY MM DD ZZZ X Q
83 10 25 708 1 9

Date: 1983-10-25

Gender: male

Control digit:
  8 * 1 =  8
  3 * 3 =  9
  1 * 7 =  7
  0 * 9 =  0
  2 * 1 =  2
  5 * 3 = 15
  7 * 7 = 49
  0 * 9 =  0
  8 * 1 =  8
  1 * 3 =  3
  ----------
         101
  9 = (10 - (101 % 10)) % 10 = 9
20233143600

YY MM DD ZZZ X Q
20 23 31 436 0 0

Date: 2020-03-31

Gender: female

Control digit:
  2 * 1 =  2
  0 * 3 =  0
  2 * 7 = 14
  3 * 9 = 27
  3 * 1 =  3
  1 * 3 =  3
  4 * 7 = 28
  3 * 9 = 27
  6 * 1 =  6
  0 * 3 =  0
  ----------
         110
  0 = (10 - (110 % 10)) % 10 = 0
\$\endgroup\$
1
\$\begingroup\$

Mathematica 247 270

A bit long but there are many details to take into consideration:

  • Allow any valid birth date between Jan. 1, 1900 and Dec. 31, 2099; Feb. 29 included in leap years.
  • Pad all numbers left with zeros: e.g. Month 3 becomes "03" so any PESEL will have exactly 11 digits.
  • Add 20 to month if in 21st century (2000-2099)

Ungolfed

Below the components are named for convenience.

g:=Module[{year,month,day,centuryIncrement,id,controlDigit,pesel},
{year,month,day}=DatePlus[{1900,1,1},RandomInteger[73048]];
centuryIncrement=20*Boole[year>= 2000];
Print[Row@{{year,month,day},"  ",DateString[{year,month,day},{"MonthName"," ","Day",", ","Year"}]},
"\ncentury increment: ",centuryIncrement,
"\nID: ",id=PadLeft[IntegerDigits@RandomInteger[999],3],
"\nsex: ",sex=RandomInteger[1]];
f=Flatten[{{IntegerDigits@year,PadLeft[IntegerDigits[month+centuryIncrement],2],
PadLeft[IntegerDigits@day,2]},id,sex}][[3;;12]];
Print["control digit: ",controlDigit=Mod[10-IntegerDigits[Tr@Thread[Times[f,{1,3,7,9,1,3,7,9,1,3}]]][[-1]],10],
"\nPESEL: ",""<>ToString/@PadLeft[Append[f,controlDigit]]]]

Examples

g

{1931,2,9} February 09, 1931
century increment: 0
ID: {0,8,8}
sex: 0
control digit: 9
PESEL: 31020908809


g

{2067,6,27} June 27, 2067
century increment: 20
ID: {9,0,8}
sex: 0
control digit: 1   PESEL: 67262790801  


g

{1967,9,5} September 05, 1967
century increment: 0
ID: {5,0,0}
sex: 0
control digit: 2
PESEL: 67090550002


Golfed

g:=Module[{y,m,d,i=IntegerDigits,p=PadLeft,r=RandomInteger},{y,m,d}=DatePlus[{1900,1,1},r[73048]];
f=Flatten[{{i@y,p[i[m+20*Boole[y>= 2000]],2],p[i@d,2]},p[i@r[999],3],r[1]}][[3;;12]];
""<>ToString/@p[Append[f,Mod[10-i[Tr@Thread[Times[f,{1,3,7,9,1,3,7,9,1,3}]]][[-1]],10]]]]
\$\endgroup\$
0
\$\begingroup\$

PHP, 151

echo($n=date("ymd",rand(-2e9,4e9)).rand(101,999).rand()%10).(10-($n[0]+3*$n[1]+7*$n[2]+9*$n[3]+$n[4]+3*$n[5]+7*$n[6]+9*$n[7]+$n[8]+3*$n[9])%10);

See it in action: http://ideone.com/DYeXf9

\$\endgroup\$
  • 1
    \$\begingroup\$ It doesn't generate valid PESEL number. \$\endgroup\$ – syntagma Apr 13 '14 at 9:32
  • \$\begingroup\$ Oh, ok... fixing it. \$\endgroup\$ – Mathieu Rodic Apr 13 '14 at 9:34
  • 1
    \$\begingroup\$ The output it too long: 14 digits instead of 11. \$\endgroup\$ – syntagma Apr 13 '14 at 9:35
  • \$\begingroup\$ How do you ensure the validity of a date? \$\endgroup\$ – DavidC Apr 13 '14 at 14:36
0
\$\begingroup\$

Mathematica, 220

This generates random date between 1900-01-01 and 2099-12-31, but it does not check whether the number of days in the month is valid.

Value for gender ranges from 0 - 9.

This is as few characters as I could get it:

r=RandomInteger;a=r@9;b=r@9;m=r@11+1+r@1*20;c=Floor[m/10];d=m~Mod~10;n=r@30+1;e=Floor[n/10];f=n~Mod~10;g=r@9;h=r@9;i=r@9;j=r@9;k=Mod[10-Mod[a+3b+7c+9d+e+3f+7g+9h+i+3j,10],10];StringJoin[ToString/@{a,b,c,d,e,f,g,h,i,j,k}]

Sample output:

04020506179
34230487249
26321891950
45260298217

Ungolfed:

r=RandomInteger;

a=r@9; (*year*)
b=r@9;

month=r@11+1+r@1*20;
c=Floor[month/10];
d=month~Mod~10;

day=r@30+1;
e=Floor[day/10];
f=day~Mod~10;

g=r@9; (*personal ID*)
h=r@9;
i=r@9;

j=r@9; (*gender*)

k=Mod[10-Mod[a+3b+7c+9d+e+3f+7g+9h+i+3j,10],10]; (*control digit*)

StringJoin[ToString/@{a,b,c,d,e,f,g,h,i,j,k}]
\$\endgroup\$
  • \$\begingroup\$ day=r@30+1 assumes that any month can have 31 days? \$\endgroup\$ – DavidC Apr 14 '14 at 0:58
0
\$\begingroup\$

JavaScript - 246

Basic JS generator:

z=Math.random;y=(z()*200|0)+1900;o=(""+y).slice(-2)+("0"+(z()*12|0+(y>1999?21:1))).slice(-2);o+=("0"+(z()*31|0+1)).slice(-2);o+=("00"+(z()*999|0)).slice(-3);o+=z()*10|0;o+=(o[0]*1+o[1]*3+o[2]*7+o[3]*9+o[4]*1+o[5]*3+o[6]*7+o[7]*9+o[8]*1+o[9]*3)%10

Edit: was a bug in the 0 padding of the random 3 digit ID. Edit: Bug in the checksum and generation of all possible dates fixed.

\$\endgroup\$
  • \$\begingroup\$ There are still some errors. E.g. generated incorrect pesel numbers: 44211950174, 81070127743, 32051356638... \$\endgroup\$ – Wojciech Jasiński Nov 25 '14 at 11:17
0
\$\begingroup\$

Mathematica, 157 chars

""<>ToString/@Join[#,{Mod[-#.{1,3,7,9,1,3,7,9,1,3},10]}]&@Flatten@{IntegerDigits[#,10,2]&/@RandomChoice@DateRange[{1990,1,1},{2099,12,31}],9~RandomInteger~4}
\$\endgroup\$
0
\$\begingroup\$

Matlab, 156 bytes

a=datestr(693961+randi(73049),'yyyymmdd');a(5)=a(5)+2*(a(1)>49);A=a(3:8)-48;b=randi(10,1,4);[[A b mod(10-mod(sum(('1379137913'-48).*[A b]),10),10)]+48 '']

Ungolfed with some explanation:

a=datestr(693961+randi(73049),'yyyymmdd');
  -- generates date serial number in desired range and converts do string
a(5)=a(5)+2*(a(1)>49);
  -- modifies the first digit of the month if year >= 2000 (first digit >1)
A=a(3:8)-48;
  -- gets the "YYMMDD" part and converts from string to array of numbers
b=randi(10,1,4)-1;
  -- gets remaining 4 random digits ("ZZZX")
[[A b mod(10-mod(sum(('1379137913'-48).*[A b]),10),10)]+48 '']
  -- computes the last digit and converts the whole thing to string
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.