The Luhn algorithm for verifying credit card numbers, etc

54

7

Challenge

Write the shortest program or function to calculate the Luhn Algorithm for verifying (credit card) numbers.

Luhn algorithm explained

From RosettaCode, this algorithm for the purposes of this challenge is specified as such, with the example input of 49927398716:

Reverse the digits, make an array:
    6, 1, 7, 8, 9, 3, 7, 2, 9, 9, 4
Double the numbers in odd indexes:
    6, 2, 7, 16, 9, 6, 7, 4, 9, 18, 4
Sum the digits in each number:
    6, 2, 7, 7, 9, 6, 7, 4, 9, 9, 4
Sum all of the numbers:
    6 + 2 + 7 + 7 + 9 + 6 + 7 + 4 + 9 + 9 + 4 = 70
If the sum modulo 10 is 0, then the number is valid:
    70 % 10 = 0 => valid

IO Rules

Input: A string or number (your choice), in your language's input/output format of choice

Output: A truthy or falsy value, respectively, indicating whether or not the input is valid according to the test above.

Notes / Tips

  • Try not to accidentally post your own credit card or account numbers, if you use them to test :)

  • If the input is invalid and impossible to process with the specified algorithm (i.e, too short to work with), you can do whatever you want, including blow up my computer.

  • However, the previous bullet does not mean that your language can do whatever it wants with Numbers that are too large for it to handle. If your language isn't capable of handling a test case, then consider taking a string as input.

Examples

The following examples were validated with this Python script; if you think one is wrong or have a question, just ping @cat.

49927398716      True
49927398717      False
1234567812345670 True    
1234567812345678 False
79927398710      False
79927398711      False
79927398712      False
79927398713      True
79927398714      False
79927398715      False
79927398716      False
79927398717      False
79927398718      False
79927398719      False
374652346956782346957823694857692364857368475368 True
374652346956782346957823694857692364857387456834 False
8 False **
0 True  **

** according to the Python implementation, but you may do anything because these are too short to be eligible by a strict adherence to the specification.


If any of the above invalidates existing answers (though I believe that should not be possible), then those answers are stil valid. However, new answers, in order to be valid, should follow the specification above.

Leaderboard

var QUESTION_ID=22,OVERRIDE_USER=73772;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

Chris Laplante

Posted 2011-01-27T21:43:32.773

Reputation: 652

Answers

21

Golfscript - 24 chars

-1%{2+0!:0)*109%+}*10%8=

Explanation:

  1. -1% reverses the string
  2. { begins a block (which we use as a loop). Each character in the strings is pushed as it's ascii value.
    1. 2+ adds 2. (the ascii value of a digit is 48+n, so we have 50+n now and the last digit is n)
    2. 0!:0 inverts the value of 0 and stores it (everything is a variable), so we have 1 on the first iteration, 0 on the second, etc.
    3. )* adds one to this value and multiplies it, so we multiply by 2, then 1, then 2, etc.
    4. 109% is remainder modulo 109. This affects only values 5-9 which have been doubled and reduces them to the correct value.
    5. + adds this value to the running sum
  3. }* ends the block and does a 'fold' operation. First, the first character is pushed (since we have reversed, this is the check digit). Then, alternate pushing and executing the block. Thus, we are using the first character's ascii value as the starting value for the running sum.
  4. 10% takes the remainder modulo 10.
  5. 8= will return 1 if the value is 8. We use this because we did not normalize the first pushed character (the check digit).

One might think that we could use 8- instead of 2+ to save a character by changing 109% to 89%, except then we would need to add a space so the - is subtraction (instead of -0).

Nabb

Posted 2011-01-27T21:43:32.773

Reputation: 2 540

11

GolfScript, 44 chars

-1%{16%}%2/1,\+{(\.{0=2*.9>9*-+}{;}if+}*10%!

Selected commentary

Interestingly, the first two items below demonstrate three completely different uses of the % operator: array selection, map, and mod. Most GolfScript operators are "context-sensitive", giving them hugely divergent behaviours depending on what types the arguments are.

  1. -1% reverses the string. This is important as the digit pairs are counted from the right.
  2. {16%}% converts all the ASCII digits into numbers, by modding them with 16.
  3. 2/ splits the array into groups of 2.
  4. 1, is a cheap way to do [0].
  5. \+ effectively prepends the 0 to the digits array. It does this by swapping then concatenating.

The 0 is prepended in preparation for the fold that comes in next. Rather than taking an explicit initial value, GolfScript's fold uses the first item in the array as the initial value.

Now, let's look at the actual fold function. This function takes two arguments: the folded value, and the current item on the array (which in this case will be an array of 2 or (uncommonly) 1, because of the 2/ earlier). Let's assume the arguments are 1 [2 3].

  1. (\. splits out the leftmost array element, moves the remaining array to the front, then copies it. Stack now looks like: 1 2 [3] [3].
  2. The if checks if the array is empty (which is the case for the last group when dealing with an odd-sized account number). If so, then no special processing happens (just pop off the empty array).
  3. For an even group:
    1. 0= grabs the first (only, in this case) element of the array. 1 2 3
    2. 2* doubles the number. 1 2 6
    3. .9>9*- subtracts 9 from the number if it's greater than 9. Implemented as: copy the number, compare with 9, multiply the result (which is either 0 or 1) with 9, then subtract. 1 2 6
    4. + finally adds that to the first number. 1 8
  4. + (after the if) adds the result of the if to the original value, resulting in the new folded value.

After the folding completes, we simply mod with 10 (10%), and negate the result (!), so that we return 1 iff the sum is a multiple of 10.

Chris Jester-Young

Posted 2011-01-27T21:43:32.773

Reputation: 4 464

This seems to return 0 for the example number on wikipedia (49927398716) – gnibbler – 2011-02-01T10:51:16.507

nm. I forgot to use echo -n – gnibbler – 2011-02-01T11:49:47.390

1@gnibbler: Haha, fail. :-P (Seriously, I got stung by that one too, in my initial testing.) – Chris Jester-Young – 2011-02-01T13:06:37.310

1A few places to save a few easy characters here. -1% 2/ can be combined into -2/. 1, can be replaced with 0 (0 is coerced to an array, then + is concatenate). 9>9*- can be replaced with 9>+ (since we're only concerned with the last digit). Also, the checking for odd lengths is a bit long, using .,2%,\+ is shorter. After doing this, we can also change {16%}% and (\0= into {16}/ (inside the loop). Once you've done all that, it'll look something like this: .,2%,\+-2/0\+{{16%}/2*.9>+++}*10%!. – Nabb – 2011-02-02T17:15:36.033

@Nabb: Thanks! I'll incorporate those into my solution, although it looks like you already have one that's kicking serious arse. :-) – Chris Jester-Young – 2011-02-02T17:40:59.273

Yep, Nabb kicked ass on this one – gnibbler – 2011-02-03T22:17:53.260

11

Python, 73 69 characters

def P(x):D=map(int,x);return sum(D+[d-d/5*9for d in D[-2::-2]])%10==0

Keith Randall

Posted 2011-01-27T21:43:32.773

Reputation: 19 865

4You can save two more chars by not looping backwards : D[-2::-2] -> D[1::2] as the order of a sum isn't important :) – ThinkChaos – 2014-02-01T23:14:05.597

==0 can be shortened to <1 – Black Owl Kai – 2019-03-23T10:54:43.507

10

Python 3, 77 bytes

c=lambda a:sum(sum(divmod(int(a[-e-1])<<e%2,10))for e in range(len(a)))%10==0

Alexandru

Posted 2011-01-27T21:43:32.773

Reputation: 5 485

9

C# 119 characters:

bool l(string n){return(String.Join("",n.Reverse().Select((x,i)=>(x-48)*(i%2<1?1:2)+"").ToArray()).Sum(x=>x-48))%10<1;}

Not too bad for a code golf n00b in a statically typed language, I hope.

This can be reduced to 100:

bool l(string n){return String.Join("",n.Reverse().Select((x,i)=>(x-48)*(i%2+1))).Sum(x=>x+2)%10<1;}

mootinator

Posted 2011-01-27T21:43:32.773

Reputation: 1 171

It's a good idea, and an interesting approach, but it doesn't appear to work. At least not with my few tests. It looks like the "i" in your first lambda is supposed to be the index of the character in the string. Does that work as it should? If so, why do you reverse the string only to then modify it based on index position? Seems a bit redundant, no? – Nellius – 2011-02-01T23:48:44.777

I only tested one of my credit cards and a few off by one errors from it TBH. (Using the VS 2008 debugger) The algorithm is supposed to double every second digit starting with the LAST digit. If I didn't reverse the string, it would be incorrect for strings with odd lengths. – mootinator – 2011-02-02T01:37:09.640

Turns out I did have the result of i%2<1?1:2 backwards. Thanks. – mootinator – 2011-02-02T01:59:12.100

8

Golfscript - 34 chars

{15&}%.-2%\);-2%{.+(9%)}%+{+}*10%!

Example number from wikipedia page 4992739871

{15&}%  does a bitwise and of each ascii digit with 00001111
        now I have a list of digits 
        [4 9 9 2 7 3 9 8 7 1 6]
.       makes a copy of the list, now I have two identical lists
        [4 9 9 2 7 3 9 8 7 1 6] [4 9 9 2 7 3 9 8 7 1 6]
-2%     like [::-2] in python takes every second element in reverse
        [4 9 9 2 7 3 9 8 7 1 6] [6 7 9 7 9 4]
\       swap the two lists around
        [6 7 9 7 9 4] [4 9 9 2 7 3 9 8 7 1 6]
);      drop the last digit off the list
        [6 7 9 7 9 4] [4 9 9 2 7 3 9 8 7 1]
-2%     same as before
        [6 7 9 7 9 4] [1 8 3 2 9]
{       for each item in the list ...
.+      ... double it ...
(       ... subtract 1 ...
9%      ... mod 9 ...
)}%     ... add 1 ...
        [6 7 9 7 9 4] [2 7 6 4 9]
+       join the two lists
        [6 7 9 7 9 4 2 7 6 4 9]
{+}*    add the elements up
        70
10%     mod 10
        0
!       invert the result
        1

gnibbler

Posted 2011-01-27T21:43:32.773

Reputation: 14 170

The .+(9%) is very innovative (for me, anyway). I like! +1 – Chris Jester-Young – 2011-02-01T13:09:40.443

Srsly though, GolfScript needs a partitioning operator, so you don't need to do this "drop end item off and repeat" nonsense. :-) – Chris Jester-Young – 2011-02-01T13:14:51.157

1@Chris, I learned about that many years ago called "casting out nines". It is a neat way to double check longhand additions and multiplications – gnibbler – 2011-02-01T13:22:03.007

3This won't work for a value of 0 being doubled (0(9%) is 9 and not 0). – Nabb – 2011-02-02T16:59:56.657

8

PHP, 108 bytes

<?function v($s,$t=0){for($i=strlen($s);$i>=0;$i--,$c=$s[$i])$t+=$c+$i%2*(($c>4)*-4+$c%5);return!($t % 10);}

Juan

Posted 2011-01-27T21:43:32.773

Reputation: 2 738

7

Haskell, 96 bytes

There must be a better/shorter way, but here's my Haskell solution in 96 characters:

l=(==0).(`mod`10).sum.zipWith($)(cycle[id,\x->x`mod`5*2+x`div`5]).reverse.map((+(-48)).fromEnum)

Sadly the digitToInt function can only be used if you import Data.Char first. Otherwise I could get down to 88 characters by replacing ((+(-48)).fromEnum) with digitToInt.

sepp2k

Posted 2011-01-27T21:43:32.773

Reputation: 1 679

7

Ruby - 85 characters

def f s
l=s.size
s.chars.map{|k|(i=k.to_i*((l-=1)%2+1))%10+i/10}.inject(:+)%10==0
end

Nemo157

Posted 2011-01-27T21:43:32.773

Reputation: 1 891

You probably know about this, but you could do .sum instead of .inject(:+) to save 7 bytes – Håvard Nygård – 2018-02-01T18:58:22.743

6

Windows PowerShell, 82

filter f{!((''+($_[($_.length)..0]|%{+"$_"*($i++%2+1)})-replace'.','+$&'|iex)%10)}

History:

  • 2011-02-13 03:08 (84) First attempt.
  • 2011-02-13 12:13 (82) I don't need to join, as spaces do not hurt. +1 + +3 can still be evaluated.

Joey

Posted 2011-01-27T21:43:32.773

Reputation: 12 260

5

APL, 28 bytes

{0=10|+/⍎¨∊⍕¨v×⌽2-2|⍳⍴v←⍎¨⍵}

Exploded view

{                     v←⍎¨⍵}  ⍝ turn the string into a numeric vector of its digits, v
                2-2|⍳⍴v       ⍝ make a vector of the same length, with 2 in every 2nd place
             v×⌽              ⍝ multiply it with v, starting from the right
          ∊⍕¨                 ⍝ turn each component into a string and collect all the digits
      +/⍎¨                    ⍝ turn each digit again into a number and sum them
 0=10|                        ⍝ check whether the sum is a multiple of 10

Examples

      {0=10|+/⍎¨∊⍕¨v×⌽2-2|⍳⍴v←⍎¨⍵} '79927398713'
1
      {0=10|+/⍎¨∊⍕¨v×⌽2-2|⍳⍴v←⍎¨⍵} '123456789'
0

Tobia

Posted 2011-01-27T21:43:32.773

Reputation: 5 455

1-2: {0=10|+/⍎¨∊⍕¨⍵×⌽2-2|⍳⍴⍵}⍎¨ – Adám – 2017-03-30T12:47:53.577

5

D, 144 bytes

bool f(S)(S s){int t(C)(C c){return to!int(c)-'0';}int n,v;foreach(i,c;array(retro(s))){v=i&1?t(c)*2:t(c);n+=v>=10?v%10+v/10:v;}return n%10==0;}

More legibly:

bool f(S)(S s)
{
    int t(C)(C c)
    {
        return to!int(c) - '0';
    }

    int n, v;

    foreach(i, c; array(retro(s)))
    {
        v = i & 1 ? t(c) * 2 : t(c);

        n += v >= 10 ? v % 10 + v / 10 : v;
    }

    return n % 10 == 0;
}

Jonathan M Davis

Posted 2011-01-27T21:43:32.773

Reputation: 705

5

Q, 63

{0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}

usage

q){0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}"79927398711"
0b
q){0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}"79927398712"
0b
q){0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}"79927398713"
1b

tmartin

Posted 2011-01-27T21:43:32.773

Reputation: 3 917

47 bytes with {0=mod[sum"J"$raze($)($)x*#:[x]#1 2]10}"I"$'(|) a different way to double the odd indices. – streetster – 2017-10-21T23:21:29.203

4

Perl, 46 42 41 bytes

Includes +1 for -p

Give input on STDIN:

luhn.pl <<< 79927398713

luhn.pl:

#!/usr/bin/perl -p
s%.%$=-=-$&-$&*1.2*/\G(..)+$/%eg;$_=/0$/

Ton Hospel

Posted 2011-01-27T21:43:32.773

Reputation: 14 114

Can you please explain how this works? You seem to be decrementing by the match and then also by the match times 1.2 but only in the correct positions. Why 1.2? Shouldn't it be $=-=-$&-$&*/\G(..)+$/? – msh210 – 2016-04-22T21:33:21.077

3@msh210: It encodes the effect of the multiplication by 2. 0..4 * 2 gives 0, 2, 4, 6, 8 but 5..9 gives 10,12,14,16,18 which sum to 1 3 5 7 9 which have the same last digits as 11 13 15 17 19 which are the same values as 0..9 * 2.2 if you truncate to integer. The first $& already contributes a factor 1, so a correction by 1.2 is still needed. $= can only hold integers and starts with a value that ends on 0 so takes care of the truncating. The negative values are needed since the /\G/ regex changes any $& still on the evaluation stack so they need to be changed – Ton Hospel – 2016-04-22T21:58:43.837

Oh. Brilliant! And thanks for the explanation. – msh210 – 2016-04-22T22:57:26.360

4

PowerShell 123

filter L($x){$l=$x.Length-1;$l..0|%{$d=$x[$_]-48;if($_%2-eq$l%2){$s+=$d}elseif($d-le4){$s+=$d*2}else{$s+=$d*2-9}};!($s%10)}

Ty Auvil

Posted 2011-01-27T21:43:32.773

Reputation: 473

3

05AB1E, 12 10 bytes

RSāÈ>*SOTÖ

Try it online! or as a Test Suite

Explanation

R             # reverse input
 S            # split to list of digits
  ā           # push range[1 ... len(input)]
   È          # map isEven on each
    >         # increment
     *        # multiply doubling every other item
      SO      # sum digits
        TÖ    # mod 10 == 0

Emigna

Posted 2011-01-27T21:43:32.773

Reputation: 50 798

3

Jelly, 12 11 bytes

ṚḤJḤ$¦DFS⁵ḍ

Try it online! (with all test cases)

How it works

ṚḤJḤ$¦DFSḍ⁵  - Main link. Argument: n (integer) e.g. 49927398716
Ṛ            - Reverse. Casts a number to digits     [6, 1, 7, 8, 9, 3, 7, 2, 9, 9, 4]
     ¦       - Sparse application. Apply the next command to the given indicies
 Ḥ           -   Command: Double
    $        -   Indicies:
  J          -     range(length)...                  [1, 2 , 3, 4, 5, 6, 7, 8, 9, 10, 11]
   Ḥ         -     doubled.                          [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
             - Doubles elements at odd indicies      [6, 2, 7, 16, 9, 6, 7, 4, 9, 18, 4]
      D      - Split each into digits                [6, 2, 7, [1, 6], 9, 6, 7, 4, 9, [1, 8], 4]
       F     - Flatten                               [6, 2, 7, 1, 6, 9, 6, 7, 4, 9, 1, 8, 4]
        S    - Sum                                   70
          ḍ  - Divisible by... 
         ⁵   -   10?                                 1

Alternatively, for 12 bytes:

ṚḤJḤ$¦DFSḍ@⁵

caird coinheringaahing

Posted 2011-01-27T21:43:32.773

Reputation: 13 702

3

x86-16 ASM, IBM PC DOS, 23 bytes

03 F1       ADD  SI, CX         ; start at end of input string 
FD          STD                 ; set LODSB direction to decrement 
    DIGIT_LOOP:
AC          LODSB               ; load next digit into AL, decrement SI
2C 30       SUB  AL, '0'        ; convert ASCII char to binary value 
F7 DA       NEG  DX             ; flip DX to alternate odd/even index
78 06       JS   EVEN           ; if even index, do not double and sum digits 
D0 E0       SHL  AL, 1          ; double the value 
D4 0A       AAM                 ; BCD convert to split digits (ex: 18 = 12H --> 0108H) 
02 DC       ADD  BL, AH         ; add tens digit to running sum 
    EVEN:
02 D8       ADD  BL, AL         ; add ones digit to running sum 
E2 ED       LOOP DIGIT_LOOP 
93          XCHG BX, AX         ; sum is in BL, move to AL for conversion
D4 0A       AAM                 ; BCD convert AL, set ZF=1 if low digit is 0

Uses (abuses) the x86's BCD-to-binary instruction AAM to handle the individual digit splitting and modulo 10 check.

Input card num string pointer in SI, length in CX. Output: ZF if valid.

Example test program output:

enter image description here

Download LUHN.COM IBM PC DOS test program.

640KB

Posted 2011-01-27T21:43:32.773

Reputation: 7 149

3

JavaScript (ES6), 61 bytes

Non-competing, as JavaScript was very different in 2011.

Sum of digits of 2*n is 2*n if n in 0..4, 2*n-9 if n in 5..9. That said, all the sum can be computed in one step.

s=>!([...s].reduceRight((t,d)=>t-d-i++%2*(d>4?d-9:d),i=0)%10)

edc65

Posted 2011-01-27T21:43:32.773

Reputation: 31 086

2

JavaScript 1.8: 106 characters

This is an original solution I came up with before I found this post:

function(n){return!(n.split('').reverse().reduce(function(p,c,i){return(+c&&((c*(1+i%2)%9)||9))+p},0)%10)}

Readable form:

function luhnCheck(ccNum) {
    return !(                                  // True if the result is zero.
             ccNum.split('').
               reverse().                      // Iterate over the string from rtl.
               reduce(function(prev, cur, idx) {
                 return prev +                 // Sum the results of each character.
                        (+cur &&               // If the current digit is 0, move on.
                         ((cur * (1 + idx % 2) // Double cur at even indices.
                           % 9) || 9));        // Sum the digits of the result.
               }, 0)
            % 10);                             // Is the sum evenly divisible by 10?
}

kojiro

Posted 2011-01-27T21:43:32.773

Reputation: 717

2

Powershell, 74 bytes

param($s)$s[$s.Length..0]|%{(1+$i++%2)*"$_"}|%{$r+=$_-9*($_-gt9)}
!($r%10)

Explanation

  1. for each char of an argument string, in reverse order
  2. get a digit of double value of a digit
  3. a double value of a digit can not be greater then 18. Therefore, we accumulate a value minus 9 if value > 9
  4. return true if the remainder of the division by 10 is 0

Test script

$f = {

param($s)$s[$s.Length..0]|%{(1+$i++%2)*"$_"}|%{$r+=$_-9*($_-gt9)}
!($r%10)

}

@(
    ,("49927398716"      , $True)
    ,("49927398717"      , $False)
    ,("1234567812345670" , $True)
    ,("1234567812345678" , $False)
    ,("79927398710"      , $False)
    ,("79927398711"      , $False)
    ,("79927398712"      , $False)
    ,("79927398713"      , $True)
    ,("79927398714"      , $False)
    ,("79927398715"      , $False)
    ,("79927398716"      , $False)
    ,("79927398717"      , $False)
    ,("79927398718"      , $False)
    ,("79927398719"      , $False)
    ,("374652346956782346957823694857692364857368475368" , $True)
    ,("374652346956782346957823694857692364857387456834" , $False)
    ,("8" , $False)
    ,("0" , $True)
) | % {
    $s, $expected = $_
    $result = &$f $s
    "$($result-eq$expected): $result : $s"
}

Output

True: True : 49927398716
True: False : 49927398717
True: True : 1234567812345670
True: False : 1234567812345678
True: False : 79927398710
True: False : 79927398711
True: False : 79927398712
True: True : 79927398713
True: False : 79927398714
True: False : 79927398715
True: False : 79927398716
True: False : 79927398717
True: False : 79927398718
True: False : 79927398719
True: True : 374652346956782346957823694857692364857368475368
True: False : 374652346956782346957823694857692364857387456834
True: False : 8
True: True : 0

mazzy

Posted 2011-01-27T21:43:32.773

Reputation: 4 832

2

K4, 35 bytes

{~.*|$+/.:',/$x*1+1{y;~x}\|x:|.:'x}

Aaron Davies

Posted 2011-01-27T21:43:32.773

Reputation: 881

2

Scala: 132

def q(x:Int)=x%10+x/10
def c(i:String)={val s=i.reverse
(s(0)-48)==10-(s.tail.sliding(2,2).map(n=>(q((n(0)-48)*2)+n(1)-48)).sum%10)}

invocation:

c("79927398713")
  • reverse ("79927398713") = 31789372997
  • s(0), s.tail: (3)(1789372997)
  • sliding (2,2) = (17 89 37 29 97)
  • map (q((n(0)-48*2 + n(1)-48)) => q(('1'-'0')*2)+ '7'-'0')=1*2+7

user unknown

Posted 2011-01-27T21:43:32.773

Reputation: 4 210

2

Retina, 43 42 bytes

Retina is (much) newer than this challenge.


;
r`(.);.
$1$&
\d
$*
1+
$.&
.
$*
$
$._
0$

The leading empty line is significant.

Prints 0 for falsy and 1 for truthy results.

Try it online! (Slightly modified to run all test cases at once.)

Explanation


;

Insert ; in every position to separate digits.

r`(.);.
$1$&

From the right, we repeatedly match two digits and double the left one. This way we avoid an expensive reversing of the list.

\d
$*

We match each digit and convert it to that many 1s (that is, we convert each digit to unary).

1+
$.&

This matches each unary number and converts it back to decimal by replacing it with its length. Together with the previous stage, this adds the doubled digits.

.
$*

Again, we match every character and turn it into that many 1s. That is we convert each digit individually back to unary. This also matches the ; separators, which are treated as zeros in the conversion, which means they're simply removed. Since all the unary numbers are now squashed together we've automatically added the unary representations of the all digits together.

$
$._

At the end, we insert the length of the entire string, i.e. the decimal representation of the unary checksum.

0$

Finally we count the number of matches of this regex, i.e. we check whether the decimal representation ends in 0, printing 0 or 1 accordingly.

Martin Ender

Posted 2011-01-27T21:43:32.773

Reputation: 184 808

1

Haskell: 97

For some reason this isn't working for me, so here is my version

l=(\x->(==0)$(`mod`10).sum$zipWith($)(cycle[id,sum.map(read.(:"")).show.(*2)])(map(read.(:""))x))

user701072

Posted 2011-01-27T21:43:32.773

Reputation: 21

1

MATL, 23 20 bytes (non competing)

P!Utn:2X\!*t9>+s10\~

Try it online!

Outputs 1 for a valid number, 0 otherwise.

Saved three bytes thanks to Luis Mendo's suggestions.

Explanation

P       % flip the order of elements
!       % transpose into column vector
U       % convert char matrix to numeric
t       % duplicate the vector
n       % find the length
:       % create a new vector length n (1, 2, 3, ... n)
2       % number literal
X\      % take it mod 2, to make the new vector (1, 2, 1, ..., (n-1) mod 2 +1)
!       % transpose
*       % element-wise product
t       % duplicate
9       % push 9
>       % 1 if it is greater than 9
+       % add the vectors, this makes the last digit of each the same as the sum of the digits
s       % add them
10      % number literal
\       % mod 10
~       % logical 'not' (element-wise)
        % (implicit) convert to string and display

B. Mehta

Posted 2011-01-27T21:43:32.773

Reputation: 763

1

Jelly, 14 bytes

DUḤJḤ$¦DS$€S⁵ḍ

Try it online!

Explanation:

D              get digits
 U             reverse array
   JḤ$         for every other index,
  Ḥ   ¦        double the value
          €    for each value,
       D $     get the digits
        S$     and sum them
           S   sum the list
            ⁵ḍ check if it's divisible by 10

ellie

Posted 2011-01-27T21:43:32.773

Reputation: 131

Why is this non-competing? – mudkip201 – 2018-01-25T01:32:10.523

@mudkip201 Correct me if I'm wrong, but this version of Jelly didn't exist when the question was asked, so it isn't valid for the question. – ellie – 2018-01-25T01:41:59.050

3Pretty sure there was a meta consensus that said that languages that were made after the challenge aren't 'non-competing' any more – mudkip201 – 2018-01-25T01:43:10.897

1

Japt, 14 13 bytes

Takes input as an array of digits.

ÔxÈ*ÒYu)ìxÃvA

Try it

ÔxÈ*ÒYu)ìxÃvA     :Implicit input of array
Ô                 :Reverse
 x                :Reduce by addition
  È               :After passing each element at 0-based index Y through the following function
   *              :   Multiply by
    Ò             :     Bitwise increment
     Yu           :     Y modulo 2
       )          :   End multiplication
        ì         :   Convert to digit array
         x        :   Reduce by addition
          Ã       :End function
           v      :Test for divisibility by
            A     :  10

Shaggy

Posted 2011-01-27T21:43:32.773

Reputation: 24 623

This doesn't reverse the array, so inputs like 24 return the wrong result – Embodiment of Ignorance – 2019-03-27T04:32:58.123

Thanks, @EmbodimentofIgnorance, looks like I made a faulty assumption somewhere along the way. Fixed and saved a byte in the process. – Shaggy – 2019-03-27T11:28:46.013

1

Dart, 120 bytes

f(s,{i=0})=>s.split('').reversed.map(int.parse).map((n)=>i++%2>0?n*2:n).map((n)=>n>9?n-10+1:n).fold(0,(p,e)=>p+e)%10==0;

Try it online!

Elcan

Posted 2011-01-27T21:43:32.773

Reputation: 913

1

Perl 6, 37 bytes

(*.flip.comb >>*>>[1,2]).comb.sum%%10

Try it online!

A Whatever lambda that takes a number and returns a boolean.

Explanation:

 *.flip                  # Reverse the number
       .comb             # Split to list of digits
             >>*>>       # Multiply each element by:
                  [1,2]  # The list 1,2,1,2,1,2...
(                      ).comb           # Split the list to a list of digits
                             .sum       # Get the digit sum
                                 %%10   # Return if it is divisible by 10

Jo King

Posted 2011-01-27T21:43:32.773

Reputation: 38 234

1

PHP, 83 82 bytes

foreach(str_split(strrev($argn))as$x=>$d)$m+=$d+($d%10+$d/5|0)*$x&=1;echo!($m%10);

As standalone program, call with php -F. Input is STDIN, output is to STDOUT as bool (1 or empty).

$ echo 49927398716|php -F luhn.php
1

$ echo 49927398717|php -F luhn.php

$ echo 1234567812345670|php -F luhn.php
1

Verify all test cases

640KB

Posted 2011-01-27T21:43:32.773

Reputation: 7 149

1

Japt v2.0a0, 14 bytes

¬ÔxÈ*ÒYu)ìxÃvA

The first byte can be removed if I took input as array of chars.

Try it

¬                   //Split (implicit) input into list of chars
 Ô                  //Reverse
  xÈ                //Get the sum of every char after passing through the following function
    *               //  Multiply the element
     ÒYu)           //  By the index modulus 2 plus 1 (Odd -> 2, Even -> 2)
         ìx         //  And take the sum of the digits
  Ã                 //Close function
vA                  //And check if result is divisible by 10

Embodiment of Ignorance

Posted 2011-01-27T21:43:32.773

Reputation: 7 014

Oh, didn't see you'd posted this before I fixed mine. Don't forget, you only have 'til Tuesday to post 3 more solutions and qualify for the additional 300 rep bounty. – Shaggy – 2019-03-28T17:23:13.113

1

APL, 38 bytes

d←10∘⊥⍣¯1⋄{0=10|+/+/d x×1+~2|⍳⍴x←⌽d ⍵}

expects the number as a number, not a string, but that's only because tryAPL (understandably) doesn't implement

further reducible, i'm sure…

Aaron Davies

Posted 2011-01-27T21:43:32.773

Reputation: 881

1

GNU sed, 140 bytes

(including +1 for the -r flag)

s/^(..)*.$/0&/
s/(.)./\1x&/g
s/x[5-9]/1&/g
s/[0x]//g
s/[789]/&6/g
s/[456]/&3/g
s/[369]/&11/g
s/[258]/&1/g
s/.{10}//g
s/.+/false/
s/^$/true/

Sed is almost never the most natural language for arithmetic, but here we go:

#!/bin/sed -rf

# zero-pad to even length
s/^(..)*.$/0&/
# double every other digit
s/(.)./\1x&/g
# add carry (converts mod-9 to mod-10)
s/x[5-9]/1&/g
# convert sum to unary
s/[0x]//g
s/[789]/&6/g
s/[456]/&3/g
s/[369]/&11/g
s/[258]/&1/g
# remove whole tens
s/.{10}//g
# output 'true' or false
s/.+/false/
s/^$/true/

Toby Speight

Posted 2011-01-27T21:43:32.773

Reputation: 5 058

1

PHP - 136 characters

function t($c){foreach($a=str_split(strrev($c)) as $k=>&$v){$v=array_sum(str_split(($k % 2)!==0?2*$v:$v));}return !(array_sum($a)% 10);}

Flame

Posted 2011-01-27T21:43:32.773

Reputation: 131

0

Tcl, 85 bytes

proc L n {expr ([regsub -all . [string rev $n] {+([incr i]%2?&:&*2%10+(&>4))}])%10<1}

Try it online!

Tcl, 86 bytes

proc L n {expr !(([regsub -all . [string rev $n] {+([incr i]%2?&:&*2%10+(&>4))}])%10)}

Try it online!

Tcl, 119 bytes

proc L n {expr !(([join [lmap a [lreverse [split $n ""]] {expr [incr i]%2?$a:[join [split [expr 2*$a] ""] +]}] +])%10)}

Try it online!

sergiol

Posted 2011-01-27T21:43:32.773

Reputation: 3 055

0

K (oK), 28 bytes

Solution:

~10!+/48!,/$(1+2!!#x)*48!|x:

Try it online!

Examples:

~10!+/48!,/$(1+2!!#x)*48!|x:"1234567812345670"
1
~10!+/48!,/$(1+2!!#x)*48!|x:"1234567812345678"
0

Explanation:

~10!+/48!,/$(1+2!!#x)*48!|x: / the solution
                          x: / save input as variable x
                         |   / reverse it
                      48!    / mod with 48 (converts ascii -> int)
            (       )*       / multiply with stuff in brackets
                  #x         / length of x
                 !           / til, the range 0..x-1
               2!            / mod with 2 (0 1 2 3 -> 0 1 0 1)
             1+              / add 1 (0 1 0 1 -> 1 2 1 2)
           $                 / convert to string
         ,/                  / flatten string
      48!                    / convert this into integers
    +/                       / sum over the list
 10!                         / modulo 10
~                            / negate (so 1 for truthy, 0 for falsy)

Extra:

A couple of 30-byte solutions in K4, not quite as short...

~.*|$+/,/10\:'((#x)#1 2)*.:'x:
~.*|$+/.:'',/$((#x)#1 2)*.:'x:

streetster

Posted 2011-01-27T21:43:32.773

Reputation: 3 635

0

Pyt, 20 bytes

ĐḶ⌊⁺⇹ą↔⇹ř⁻2%⁺*ŚƩ1ᴇ%¬

Explanation:

                        Implicit input
Đ                       Duplicate input
 Ḷ⌊⁺                    Get length of integer in base-10 (floor(log_10(input))+1)
    ⇹                   Swap top two on stack
     ą↔                 Convert input to an array of digits, and reverse
       ⇹                Swap top two on stack
        ř⁻              Push [0,1,2,...,floor(log_10(input))]
          2%            Take previous array element-wise mod 2
            ⁺           Increment each element of the array
             *          Elementwise multiplication of the arrays
              Ś         Elementwise sum of digits
               Ʃ        Sum up array
                1ᴇ%     Mod 10
                   ¬    Negate (this returns false if not 0, and true otherwise)

Try it online!

mudkip201

Posted 2011-01-27T21:43:32.773

Reputation: 833

0

Kotlin, 77 bytes

mapIndexed{i,c->(""+(2-(length%2 xor i%2))*(c-'0')).sumBy{it-'0'}}.sum()%10<1

Beautified

mapIndexed { i, c ->
    ("" + (2 - (length % 2 xor i % 2)) * (c - '0')).sumBy { it - '0' }
}.sum() % 10 < 1

Test

data class Test(val input: String, val output: Boolean)

val tests = listOf(
        Test("49927398716", true),
        Test("49927398717", false),
        Test("1234567812345670", true),
        Test("1234567812345678", false),
        Test("79927398710", false),
        Test("79927398711", false),
        Test("79927398712", false),
        Test("79927398713", true),
        Test("79927398714", false),
        Test("79927398715", false),
        Test("79927398716", false),
        Test("79927398717", false),
        Test("79927398718", false),
        Test("79927398719", false),
        Test("374652346956782346957823694857692364857368475368", true),
        Test("374652346956782346957823694857692364857387456834", false),
        Test("8", false),
        Test("0", true)
)

fun String.f() =
mapIndexed{i,c->(""+(2-(length%2 xor i%2))*(c-'0')).sumBy{it-'0'}}.sum()%10<1
fun main(args: Array<String>) {
    for ((input, expectedOut) in tests) {
        val actualOut = input.f()
        if (actualOut != expectedOut) {
            System.err.println("$input $expectedOut $actualOut")
        }
    }
}

TIO

TryItOnline

jrtapsell

Posted 2011-01-27T21:43:32.773

Reputation: 915

0

Julia, 67 bytes

x=digits(n)
sum(x[1:2:end])+sum(reduce(vcat,digits.(x[2:2:end]*2)))

xiaodai

Posted 2011-01-27T21:43:32.773

Reputation: 119

0

MathGolf, 11 bytes

▒xôï¥)*Σ+♂÷

Try it online!

At least I tied with Jelly, I can't really see a way to remove a byte from this.

Explanation

▒             split to list of chars/digits
 x            reverse int/array/string
  ô           start block of length 6 (for-each)
   ï          index of current loop
    ¥         modulo 2
     )        increment
      *       pop a, b : push(a*b)
       Σ      digit sum
        +     add to the counter (implicitly calculates 0+x when stack only has one element)
              Block ends here
         ♂    push 10
          ÷   is divisible

maxb

Posted 2011-01-27T21:43:32.773

Reputation: 5 754

0

Haskell, 73 bytes

i#0=0
i#n|m<-mod n 10=i*(m+sum[-9|m>4])+m+(1-i)#div n 10
f n=mod(0#n)10<1

Try it online!

Laikoni

Posted 2011-01-27T21:43:32.773

Reputation: 23 676

0

Bash (bash 4.4.5)+, 109 106 bytes

Output of 0 means True. Anything else means False. Try it Online!

  1. -3 chars: echo $n|rev ==> rev<<<$n

  2. -3 chars: for i in `seq ${#r}`; ==> for((;${#r}>i++;))

  3. y obtained by x * 2.2 and taking the last int digit. Equivalent to multiplying by 2 and summing digits, but quicker

r=`rev<<<$n`
for((;${#r}>i++;)){
x=${r:i-1:1}
y=`bc<<<"($x*2.2)%10/1"`
s=$((i%2>0?s+x:s+y));}
echo $[s%10]

Explanation:

r=`echo $1|rev`                #read input ($1) and reverse it (r)
for((;${#r}>i++;)){            #loop over digits of r ; do (i)
  x=${r:i-1:1};                #  x is the (i-1)'th digit of r
  y=`bc<<<"($x*2.2)%10/1"`     #  y is sum of doubled digits of x 
  s=$((i%2>0?s+x:s+y))         #  s is running total of digit-sums,
  ;}                           #    alternately adding x and y
echo $[s%10]                   #print s modulo 10

roblogic

Posted 2011-01-27T21:43:32.773

Reputation: 554

0

APL(NARS), 37 chars, 74 bytes

{0=10∣+/{+/10 10⊤⍵}¨v+v×0=2∣⍳≢v←⌽⍎¨⍵}

test:

  f←{0=10∣+/{+/10 10⊤⍵}¨v+v×0=2∣⍳≢v←⌽⍎¨⍵}
  f '1234567812345670'
1
  f '49927398717'
0
  f '1234567812345678'
0
  f '79927398710'
0
  f '79927398719'
0
  f '374652346956782346957823694857692364857368475368'
1
  f '374652346956782346957823694857692364857387456834'
0
  f ,'8'
0
  f ,'0'
1

comment:

{0=10∣+/{+/10 10⊤⍵}¨v+v×0=2∣⍳≢v←⌽⍎¨⍵}
                                ⍎¨⍵ for each element of ⍵ (that would be char digits) convert in int
                             v←⌽    reverse the array result of above, and assign it to variable v
                           ⍳≢        produce all index of above v that are 1..≢v
                        0=2∣         produce one array of boolean where 0 means even index, 1 odd
                    v+v×            multipy that array for v itself (means get only odd ones the other 0)
                                    and sum it to v (so we have v with elements index odd duplicate)
       {+/10 10⊤⍵}¨                convert each element of the array of 2 digits base 10 than sum
   10∣+/                            sum all element mod 10
 0=                                if result is 0 return 1 (true), else return 0 (false)

RosLuP

Posted 2011-01-27T21:43:32.773

Reputation: 3 036

0

Burlesque, 33 bytes

riXX<-J2ENj2en2?***{XX++}ms10.%z?

Try it online!

ri         # Read input as int
XX         # Explode into digits
<-         # Reverse
J2EN       # Duplicate and take every 2nd starting from 1
j2en       # Take the other half
2?*        # Multiply each digit by 2
**         # Remerge arrays
{XX++}ms   # Sum the digits of multidigit and then sum result
10.%z?     # If result `mod` 10 is 0

DeathIncarnate

Posted 2011-01-27T21:43:32.773

Reputation: 916

0

Pyth, 19 bytes (non-competing)

Non-competing since Pyth is newer than this challenge.

!%s.esj*b@S2kT_jQTT

A program that takes input of a string on STDIN and prints True or False as appropriate.

Try it online or verify all test cases

How it works

!%s.esj*b@S2kT_jQTT  Program. Input: Q
               jQT   Yield the base-10 representation of Q as a list, giving the digits
              _      Reverse
   .e                Enumerated map, with elements as b and indices as k:
          S2           Yield [1, 2]
         @  k          Modular indexing with k, yielding 1 for even indices and 2 for odd
                       indices
       *b              Multiply by b
      j      T         Yield the digits
     s                 Sum
  s                  Sum the resulting list
 %                T  Modulo 10
!                    Logical not, yielding True for 0 and False otherwise
                     Implicitly print

TheBikingViking

Posted 2011-01-27T21:43:32.773

Reputation: 3 674