49
\$\begingroup\$

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>

\$\endgroup\$

45 Answers 45

1
\$\begingroup\$

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!

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

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
\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Shaggy Mar 28 at 17:23
1
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ This doesn't reverse the array, so inputs like 24 return the wrong result \$\endgroup\$ – Embodiment of Ignorance Mar 27 at 4:32
  • \$\begingroup\$ Thanks, @EmbodimentofIgnorance, looks like I made a faulty assumption somewhere along the way. Fixed and saved a byte in the process. \$\endgroup\$ – Shaggy Mar 27 at 11:28
1
\$\begingroup\$

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

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

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
\$\endgroup\$
0
\$\begingroup\$

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!

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

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:
\$\endgroup\$
0
\$\begingroup\$

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!

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

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

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

Julia, 67 bytes

x=digits(n)
sum(x[1:2:end])+sum(reduce(vcat,digits.(x[2:2:end]*2)))
\$\endgroup\$
0
\$\begingroup\$

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
\$\endgroup\$
0
\$\begingroup\$

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!

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

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)
\$\endgroup\$
0
\$\begingroup\$

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
\$\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.