20
\$\begingroup\$

Is this a valid PZN?

A Pharmazentralnummer, or PZN, is an eight-digit code which uniquely identifies medications, medical products, and similar items in the German healthcare system. Examples include ibuprofen tablets, insulin injectors, toothpaste, warm-and-cold compresses, all sorts of things. When a doctor issues a prescription, it's almost always using a PZN, which then is fulfilled by the Apotheke.

The challenge

Write a program or function that takes an eight-digit code as input, and returns a truthy if it is a valid PZN, or falsy if it is not.

A PZN consists of seven code-digits, and a check-digit. Each code-digit is multiplied by a weight based on its position, from 1 to 7, then each value is summed up, and reduced modulo 11. The result is checked against the check-digit. If they are equal, then the PZN is valid.

When reducing the value modulo 11, the result could be 10. In that case, the PZN isn't valid.

Example: PZN 13724857.

The code-digits are 1, 3, 7, 2, 4, 8, 5, and the check-digit is 7.

Step 1: Multiply each code-digit by its weight.

The weight is the position of the code-digit in the PZN, from 1 to 7.

Digit | Weight | Value
1     | 1      | 1*1 = 1
3     | 2      | 3*2 = 6
7     | 3      | 7*3 = 21
2     | 4      | 2*4 = 8
4     | 5      | 4*5 = 20
8     | 6      | 8*6 = 48
5     | 7      | 5*7 = 35

Step 2: Add all the weighted values together, and reduce modulo 11

The total of the weighted values is 1 + 6 + 21 + 8 + 20 + 48 + 35 = 139

Then reduce the total modulo 11: 139 mod 11 = 7

The computed result is the same as our check-digit 7, so the PZN is valid.

Test cases

PZN      | Is valid?
00000000 | True
13724857 | True
08108754 | True
04861880 | True
16086334 | True
01349035 | True
00000001 | False
00000002 | False
06765774 | False
08108759 | False
00100010 | False

Rules

  • You can assume that the input will always be a code precisely eight digits long - you don't need to account for grossly invalid inputs (e.g. with letters or spaces).
  • However, your solution must accept a code that is precisely eight digits long, including leading zeros.

Finally, this is - the shortest solution by number of bytes wins!

\$\endgroup\$
7
  • \$\begingroup\$ Yes, the 3rd test case was wrong. I've corrected it. Thanks! \$\endgroup\$ Commented Jun 6 at 17:43
  • 7
    \$\begingroup\$ @cairdcoinheringaahing I don't really agree. It is indeed 'some arithmetic manipulation', but the manipulation is very different: in the linked challenge, ever other digit needs to be doubled, before adding the resulting digits (not the numbers!) all together. That is a complication that is not present in the current challenge. The current challenge is arguably much simpler and therefore different enough I would say. \$\endgroup\$
    – Jitse
    Commented Jun 7 at 7:01
  • 7
    \$\begingroup\$ I think the by-position weighting on this challenge makes the answers fairly different. \$\endgroup\$
    – xnor
    Commented Jun 7 at 7:43
  • 2
    \$\begingroup\$ Related (but not duplicated!) challenges: Luhn algorithm; valid barcode?; ISBN-13 check digit; Dutch BSN eleven-test. \$\endgroup\$ Commented Jun 7 at 7:55
  • 2
    \$\begingroup\$ When a doctor issues a prescription, it's almost always using a PZN pffff, France is really in the Middle Ages, the advanced doctors will print out the prescription, and the less advanced ones rely on the 6 months compulsory course on hieroglyphs in pharmaceutical studies. \$\endgroup\$
    – WoJ
    Commented Jun 7 at 16:04

26 Answers 26

5
\$\begingroup\$

K (ngn/k), 17 bytes

It takes a digit array.

{*|x=11!+/1+&7#x}
             7#x     First 7 digits
          1+&        a b c d ... ->  1 for a times, 2 for b times, ...
        +/           Inner product between first 7 digits and 1 2 3 4 5 6 7
     11!             Mod 11
 *|x=                Same as the last digit?               

Try it online!

\$\endgroup\$
5
\$\begingroup\$

Nekomata + -e, 8 bytes

7R¢ɔ∙11¦

Attempt This Online!

7R¢ɔ∙11¦
7R          [1, 2, 3, 4, 5, 6, 7]
  ¢ɔ        Append 10
    ∙       Dot product with the input
     11¦    Check if the result is divisible by 11
\$\endgroup\$
5
\$\begingroup\$

Python, 42 bytes

lambda d:int(d[6::-1].hex())//.99%11==d[7]

Attempt This Online!

Expects input as a byte string.

How?

This abuses the hex method of bytes objects to implement a poor man's base 100 conversion. The old divide by base-1 trick is then used to get partial sums over the digits (we had to reverse their order for this to come out right). To get the sum over digits we would normally have done mod 99 but as we have to do mod 11 in the end anyway we can save it here.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ TIL. That's clever stuff probably worthy of a post in Python Tips too. \$\endgroup\$ Commented Jun 7 at 17:27
  • \$\begingroup\$ @JonathanAllan thanks, I have my moments, I guess. Any particular bit you were thinking of for the tips? \$\endgroup\$ Commented Jun 8 at 13:10
  • \$\begingroup\$ I guess there are two tips in here, the base 100 conversion was the bit I was thinking of, but the "old divide by base -1" might fit too. \$\endgroup\$ Commented Jun 8 at 13:44
5
\$\begingroup\$

Python 3.8,  50  47 bytes

-1 thanks to l4m2* leading to two more.

Could save at least two bytes by outputting a falsey value for valid and a truthy value for invalid; potentially quite a few using a recursive function.

lambda d,i=0:sum(v*(i:=i+5)for v in d)%11==d[7]

An unnamed function that accepts the list of digits and returns True or False.

Try it online!

How?

Call the weighted sum of the first seven digits \$S\$ and the final digit \$h\$.

A PZN is valid iff \$S \equiv h \mod 11\$

We want an equivalent condition using all eight digits in the sum, i.e. \$S+8h\$, so we can do

for v in d

rather than

for v in d[:7]

So we find \$m\$ such that \$m(S+8h) \equiv h \mod 11\$.

Substituting in \$S \equiv h \mod 11\$ we have \$m(h+8h) \equiv h \mod 11\$, so \$9mh \equiv h \mod 11\$ thus \$9m \equiv 1 \mod 11\$, which is satisfied by \$m=5\$ since \$45 \equiv 1 \mod 11\$.

This gives us

lambda d,i=0:sum(v*(i:=i+1)for v in d)*5%11==d[7]

For 49 bytes, as suggested by l4m2.

Now we can move the multiplication inside our weighted sum to give the code at the top.


My original 50 byte answer

lambda d,i=0:sum(v*(i:=i+1)+3*d[7]for v in d)%11<1

An unnamed function that accepts the list of digits and returns True or False.

Try it online!

Note that \$8\times 3 + 8 = 32 \equiv -1 \mod 11 \$


* arnauld also made a fairly trivial 49er that avoids much real trickery (just make the right multiplier for each digit!):

lambda d,i=1:sum(v*~-((i:=i+1)%9)for v in d)%11<1
\$\endgroup\$
5
4
\$\begingroup\$

Uiua SBCS, 16 bytes

=◿11/+×⇌¯⇡¯7:°⊂⇌

Try it!

\$\endgroup\$
1
  • \$\begingroup\$ 15 bytes \$\endgroup\$
    – Tbw
    Commented Jun 11 at 5:03
4
\$\begingroup\$

JavaScript (ES6), 40 bytes

Expects an array of 8 digits and returns a Boolean value.

a=>!a.reduce(p=>p%11+a[i++]*~(~i%9),i=0)

Try it online!

Commented

a =>           // a[] = input array
!a.reduce(p => // for each value in a[] with accumulator p:
  p % 11 +     //   reduce p modulo 11
  a[i++] *     //   add the next value from a[] multiplied by
  ~(~i % 9),   //   1 .. 7 for the first 7 elements
               //   or -1 for the last one
  i = 0        //   starting with p = 0 and i = 0
)              // end of reduce() -> turned into a Boolean
\$\endgroup\$
3
\$\begingroup\$

05AB1E, 8 bytes

¨ƶO11%Qθ

Input as a list of digits.

Try it online or verify all test cases.

Explanation:

¨         # Remove the last digit of the (implicit) input-list
 ƶ        # Multiply each value by their 1-based index
  O       # Sum
   11%    # Modulo-11
      Q   # Check for each digit in the (implicit) input-list whether it equals this
       θ  # Pop and only leave the last check
          # (which is output implicitly as result)
\$\endgroup\$
2
\$\begingroup\$

APL+WIN, 23 bytes

Prompts for number as a string of digits.

(7↓n)=11|+/(⍳7)×7↑n←⍎¨⎕

Try it online! Thanks to Dyalog Classic

\$\endgroup\$
2
\$\begingroup\$

MATL, 12 11 bytes

0&)7:*s11\=

Try it online! Or verify all test cases.

Explanation

       % Implicit input: numeric vector
0&)    % 2-output indexing (modular, 1-based) with 0: pushes the last element,
       % then a vector with the remaining elements
7:     % Push [1 2 ... 7]
*      % Multiply, element-wise
s      % Sum
11\    % Modulo 11
=      % Equal?
       % Implicit display
\$\endgroup\$
1
\$\begingroup\$

Python 3, 60 bytes

lambda a:sum([x*-~i for i,x in enumerate(a[:-1])])%11==a[-1]

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ As the input is guaranteed to be 8 numbers long, you can replace the -1 with 7 to save two bytes. \$\endgroup\$
    – Julian
    Commented Jun 6 at 21:39
1
\$\begingroup\$

C (gcc), 51 bytes

s,i;f(int*l){for(i=s=0;i<7;)s+=*l++*++i;s=s%11-*l;}

Try it online!

function returning true if invalid, false if valid PZN code.

\$\endgroup\$
1
\$\begingroup\$

PowerShell Core, 43 bytes

$args[0..6]|%{$s+=$_*++$i}
$s%11-eq$args[7]

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Japt -h, 11 bytes

Takes input as a digit array.

ǶUxÈ*ÒYÃuB

Try it

\$\endgroup\$
1
\$\begingroup\$

JavaScript (Node.js), 40 bytes

a=>1>a.reduce((p,v,i)=>p-~i*v,a[7]*2)%11

Try it online!

From Arnauld's. 8th digit * 8 + 8th digit * 2 = 8th digit * (-1), modulo 11

\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 31 bytes

Mod[Most@#.Range@7,11]==Last@#&

Try all test cases online! Unnamed function taking a list of eight digits as input and returning True or False.

\$\endgroup\$
1
\$\begingroup\$

Google Sheets, 58 bytes

=SORT(MOD(A1,10)=MOD(SUM(MID(A1,ROW(1:7),1)*ROW(1:7)),11))
\$\endgroup\$
1
\$\begingroup\$

TI-BASIC, 29 bytes

Input A
ʟA(8)=remainder(Σ(IʟA(I),I,1,7),11

This program takes an array of digits as inputs, and outputs 1 (for true) or 0 (for false). It uses a summation to multiply the digits by their weights and add them together. It then checks if the result modulo eleven is equal to the last digit.

Example input and output:

Example input and output for both true and false results.

\$\endgroup\$
1
\$\begingroup\$

Jelly, 8 bytes

ṖḋJ%11⁼Ṫ

A monadic Link that accepts the list of eight digits and yields 1 (truthy) if a valid PZN or 0 (falsey) otherwise.

Try it online! Or see the test-suite.

How?

Direct implementation of the specification.

ṖḋJ%11⁼Ṫ - Link: list of eight digits, A = [a,b,c,d,e,f,g,h]
Ṗ        - pop {A} -> P = [a,b,c,d,e,f,g]
  J      - indices {A} -> I = [1,2,3,4,5,6,7,8]
 ḋ       - {P} dot product {I} -> S = 1×a+2×b+3×c+4×d+5×e+6×f+7×g+8×0
   %11   - {S} mod 11
       Ṫ - tail {A} -> h
      ⁼  - {S mod 11} equals {h}?
\$\endgroup\$
1
\$\begingroup\$

R, 28 24 bytes

\(d)sum(d*c(1:7,-1))%%11

Attempt This Online!

Takes input as a vector of digits. Outputs swapped truthy (non-zero) / falsy (zero).

\$\endgroup\$
1
\$\begingroup\$

Common Lisp, 92 bytes

Input is a list of numbers

(defun p(n)(=(mod(reduce'+(mapcar(lambda(x y)(* x y))n'(1 2 3 4 5 6 7 0)))11)(car(last n))))
\$\endgroup\$
0
\$\begingroup\$

Arturo, 42 bytes

$->a[0x:∑map a\[0..6]=>[&*<=1+]a\7=x%11]

Try it!

Explanation

$->a[]            ; a function taking a list a
0                 ; push zero, our multiplier, to stack
x:                ; let x be...
∑                 ; sum of...
map a\[0..6]=>[]  ; first seven numbers of input mapped to...
&                 ; current digit...
*                 ; multiplied by...
<=                ; extra copy of...
1+                ; top of stack plus one
a\7=              ; is the last number of input equal to...
x%11              ; x modulo eleven?
\$\endgroup\$
0
\$\begingroup\$

Charcoal, 16 bytes

⁼﹪Σ⭆…θ⁷×ι⊕κ¹¹﹪Nχ

Try it online! Takes input as a string and outputs a Charcoal boolean, i.e. - for a valid PZN, nothing if not. Explanation:

     θ              Input string
    …               Truncated to length
      ⁷             Literal integer `7`
   ⭆                Map over characters and join
        ι           Current character
       ×            Repeated by
          κ         Current index
         ⊕          Incremented
  Σ                 Take the digital sum
 ﹪                  Modulo
           ¹¹       Literal integer `11`
⁼                   Is equal to
              N     Input as a number
             ﹪      Modulo
               χ    Predefined variable `10`
                    Implicitly print

13 bytes by taking input as an array of integers:

⁼⊟θ﹪ΣEθ×ι⊕κ¹¹

Try it online! Link is to verbose version of code. Explanation:

  θ             Input array
 ⊟              Remove last element
⁼               Equals
      θ         Remainder of input array
     E          Map over elements
        ι       Current element
       ×        Multiplied by
          κ     Current index
         ⊕      Incremented
    Σ           Take the sum
   ﹪            Modulo
           ¹¹   Literal integer `11`
                Implicitly print
\$\endgroup\$
0
\$\begingroup\$

J, 15 bytes

{:=11|1#.7$[*#\

Attempt This Online!

{:=11|1#.7$[*#\
             #\   NB. 1,2,3,...,8
            *     NB. multiplication
           [      NB. input digit array
         7$       NB. first 7 digits
      1#.         NB. sum
   11|            NB. mod 11
  =               NB. equals
{:                NB. last digit of input
\$\endgroup\$
0
\$\begingroup\$

AWK, 36 bytes

{for(;++i<NF;)s+=$i*++x}$0=s%11==$NF

Attempt This Online!

{for(;++i<NF;) # each digit
s+=            # sum
$i*++x}        # digit times location
$0=            # set output
s%11==$NF      # sum mod 11 = last digit

Prints 1 if valid or nothing.

\$\endgroup\$
0
\$\begingroup\$

Perl 5 -MList::Util=sum -pl, 34 bytes

$_=(pop@F)==(sum map$_*++$i,@F)%11

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Haskell, 41 bytes

f x=mod(x!!7*2+sum(zipWith(*)[1..]x))11<1

Attempt This Online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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