66
\$\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\$
0

68 Answers 68

1 2
3
1
\$\begingroup\$

><>, 39 37 bytes

00@:}2%1+$c%*:a%$9)++{1+$1l3=.
n;a%0=

Try it online

Explanation

00                                 # init sum and counter to 0
  @:}2%1+                          # set counter to 1 if even and 2 if odd
         $c%*                      # mod current number by 12 and multiply by counter
               :a%$9)++            # sum digits and add to total sum
                       {1+$        # increment counter
                           1l3=.   # jump to next row if done, else skip init on 1st row
n;a%0=                             # print sum mod 10 == 0
\$\endgroup\$
1
\$\begingroup\$

Zsh, 52 bytes

for j (${(Oas::)1})((s+=++i%2?j:j*2.2%10))
<<<$s[-1]

Try it Online..

Parse input into array and reverse it: ${(Oas::)1}. Then iterate over the array elements.
0 is truthy, non-zero is falsy.

From my work, the Visa test number (4111 1111 1111 1111) is embedded in my brain.

Previous efforts: 55 bytes 61 bytes 72 bytes 77 bytes

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

Thunno 2 t!, 8 bytes

drŻɗ⁺×ʂS

Attempt This Online!

Or 10 bytes flagless:

drŻɗ⁺×ʂSt~

Attempt This Online!

Explanation

drŻɗ⁺×ʂSt~  # Implicit input
dr          # Cast to digits; reverse the list
  Ż         # Without popping, push [0..length)
   ɗ⁺       # Mod 2 of each; increment each
     ×      # Multiply elementwise
      ʂ     # Sum the digits of each number
       S    # Sum the resulting list
        t   # Pop and push the last digit
         ~  # And check if this equals 0
            # Implicit output
\$\endgroup\$
1
\$\begingroup\$

Rust, 195 181 bytes

|s:&str|s.chars().map(|c|c.to_digit(10).unwrap()).rev().enumerate().map(|(i,n)|n<<i%2).map(|n|format!("{n}").chars().map(|c|c.to_digit(10).unwrap()).sum::<u32>()).sum::<u32>()%10<1;

Attempt This Online!

I'm pretty unfamiliar with Rust golf, so feel free to suggest savings.

  • -14 by @ceilingcat

Explanation

|s:&str|                           // input is a &str
s                                  // original string
.chars()                           // get the characters
.map(|c|c.to_digit(10).unwrap())   // convert each character into a digit
.rev()                             // reversed
.enumerate()                       // into pairs of (index, digit)
.map(|(i,n)|n<<i%2)                // into n or 2*n if the index is odd
.map(                              // getting the digit sum...
  |n|format!("{n}")                // to string
  .chars()                         // get the chars chars
  .map(|c|c.to_digit(10).unwrap()) // convert to digits
  .sum::<u32>())                   // sum them up
.sum::<u32>()                      // sum all the numbers up
 %10<1;                            // check if divisible by 10
\$\endgroup\$
0
1
\$\begingroup\$

Forth (gforth), 82 bytes

: f >r i + 1- 0 r> 0 do over i - c@ '0 - i 2 mod 1+ * 10 /mod + + loop 10 mod 0= ;

Try it online!

Explanation

Takes input as a string, loops over the digits backwards, doubling if odd, then adds the digits of each number to the total.

Code Explanation

: f           \ start new word definition
  >r          \ stick the string length on the return stack temporarily
  i + 1-      \ add the string length to the addr -1 to get the addr of the last char
  0           \ initialize the sum as 0
  r> 0        \ set up the loop variables to run from 0 to string length
  do          \ start the counted loop
    over i -  \ get the address of the current char
    c@ '0 -   \ get the value of the current char, then subtract ascii 0 to get it as a num
    i 2 mod   \ get whether the current index is odd or even
    1+ *      \ add 1 and multiply by the current digit (will be 2* if odd, and 1* if even)
    10 /mod + \ get the digits of the new number and add them together
    +         \ add this value to the sum
  loop        \ end the counted loop
  10 mod 0=   \ check if divisible by 10
;             \ end word definition
\$\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
0
\$\begingroup\$

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

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.