Talk:Luhn algorithm: Difference between revisions

Content deleted Content added
ENTi (talk | contribs)
mNo edit summary
Undid revision 1289018748 by 148.252.146.68 (talk)
 
(43 intermediate revisions by 29 users not shown)
Line 1:
{{WikiProject banner shell|class=C|
{{maths rating|frequentlyviewed=yes|class=C|priority=Low|field=discrete}}
{{WikiProject ComputingMathematics|priority=Low}}
{{WikiProject Computing|auto=inherit}}
 
}}
 
== Worked Example ==
Line 12 ⟶ 13:
 
With numbers that result in 10 or greater are added together, not subtract 9. However when you are write computer code minus 9 is normally used.
 
ممكن اعرف انت في حياتي [[User:Maommad|Maommad]] ([[User talk:Maommad|talk]]) 11:51, 6 January 2021 (UTC)
 
== No longer valid? ==
Line 28 ⟶ 31:
::The reason to do this is that it keeps the ___domain of the set the same. Converting 16 to 7 keeps a 1 to 1 ratio for each possible digit for the checkvalue. [[User:Mckaysalisbury|McKay]] ([[User talk:Mckaysalisbury|talk]]) 22:35, 30 September 2010 (UTC)
: Technically you don't have to do that intermediate step, thanks to the associative and commutative properties of addition. You can just do the doubling and then add up all the individual digits for all the numbers in whatever order you want, and you can do mod 10 as often as you like along the way, as long as you do it once more after you add the last digit [so if the number is 6789 you could do: (1 + (2+7) + ((1+6+9) mod 10)) mod 10 and you get the same answer]. I think the author felt describing it with the values for each digit added up independently made it easiest for the reader to trace back the work to the original digits in the credit card number. [[User:Xcbsmith|Xcbsmith]] ([[User talk:Xcbsmith|talk]]) 21:48, 15 November 2011 (UTC)
 
:: The rationale behind converting 16 to 7 is to catch single-digit errors.
:: Perhaps this should be made more [[WP: obvious]] in the article.
:: The [[check digit]] article claims that "Systems with weights ... with the weights on neighboring numbers being different, are widely used [to detect] transposition errors. 1, 3, 7, and 9 are used because they are coprime to 10, so changing any digit changes the check digit; using a coefficient that is divisible by 2 or 5 would lose information (because 5×0 + 5×2 + 5×4 + 5×6 + 5×8 + 0 modulo 10) and thus not catch some single-digit errors."
:: This seems to imply that the Luhn algorithm (because it uses weights of 1 and 2) would not catch some single-digit errors. However, the Luhn algorithm actually does catch all single-digit errors, because it has that extra step -- *after* it weights the digits, it converts 16 to 7 etc.
:: If hypothetically someone were to incorrectly implement the Luhn algorithm after hastily reading a description of it and forgot to implement that step, he would have
:: (let's call this the "dumb algorithm"):
::# From the rightmost digit, which is the check digit, moving left, double the value of every second digit. (For example, 8187 --> 16 + 1 + 16 + 7 is mis-written as 3187 --> 6 + 1 + 16 + 7)
::# Take the sum of all the resulting values. (16 + 1 + 16 + 7 = 40, while 6 + 1 + 16 + 7 = 30)
::# If the total [[modular arithmetic|modulo]] 10 is equal to 0 (if the total ends in zero) then the number is valid according to the dumb formula; else it is not valid. (We want the check algorithm to detect and reject single-digit errors like this one, but in this case it fails to detect the error).
:: For the reasons that [[User:Mckaysalisbury|McKay]] and [[User:Xcbsmith|Xcbsmith]] stated, with this dumb algorithm, a transcription error -- in a position that will be doubled -- that changes "5" to "0", or "6" to "1", or "7" to "2", or "8" to "3", or "9" to "4", or vice-versa, will be undetected.
:: The extra step in the Luhn algorithm -- "if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the product" -- allows the Luhn algorithm to detect *any* single-digit transcription error.
:: How can we make this more obvious to people reading this article, without getting bogged down in unnecessary detail? --[[User:DavidCary|DavidCary]] ([[User talk:DavidCary|talk]]) 15:58, 22 December 2015 (UTC)
::: How about we just say that after the doubling step you add up all the digits modulo 10? Or perhaps say you add up, discarding all but the last digit along the way? [[User:Xcbsmith|Xcbsmith]] ([[User talk:Xcbsmith|talk]]) 02:05, 6 October 2016 (UTC)
 
=== Strengths/Weaknesses ===
Line 45 ⟶ 62:
:::You are correct. According to Vital (Now TSYS) the wording is correct and the pseudo-code is wrong. I have recommended that it be changed. Have to be careful though in how you word the for loop cause not all languages implement for the same. While loops may be safer and more language independant. --[[User:dmprantz|dmprantz]] 17 October 2006 (UTC)
 
Interesting, the pseudocode matches the description of the algorithm now, but the example given is wrong (it doubles the OTHER digits, starting with the first from the end of the payload, and not with the second, while the description clearly says "every second" - it should say "every second counting the whole number, not only the payload"). More interesting, running the given number on 6 different online calculators, 4 of them gave the result as in the example (i.e. 4) but the other 2 gave the result as in the description and pseudocode (i.e. 3) :D My cards numbers all match the example, and not the description/pseudocode. Something is fishy in this article :P ... [[User:LaurV|LaurV]] ([[User talk:LaurV|talk]]) 04:30, 8 March 2025 (UTC)
 
=== Computing the check digit ===
Line 144 ⟶ 162:
Both the C# implementation and the Scala implementation seems wrong to me. The algorithm states "Counting from the check digit, which is the rightmost, and moving left, double the value of every second digit". Then, shouldn't the example read:
 
<sourcesyntaxhighlight lang="csharp">
bool isLuhnValid(string s)
{
return s.Reverse().SelectMany((c, i) => ((c - '0') << (i & 1)).ToString()).Sum(c => (c - '0')) % 10 == 0;
}
</syntaxhighlight>
</source>
 
Similarly for Scala, shouldn't it be:
 
<sourcesyntaxhighlight lang="scala">
def isLuhnValid(s : String) =
s.reverse.zipWithIndex.map({ case (c, i) => ((c - '0') << (i & 1))}).mkString.map(_ - '0').sum % 10 == 0
</syntaxhighlight>
</source>
 
[[User:Rehno Lindeque|Rehno Lindeque]] ([[User talk:Rehno Lindeque|talk]]) 09:33, 1 June 2011 (UTC)
Line 175 ⟶ 193:
 
: Agree completely. A simple pseudocode algorithm should be crafted and all other examples removed. – [[User:Acdx|Acdx]] <small>(<i>[[User_talk:Acdx|talk]]</i>)</small> 13:56, 28 July 2011 (UTC)
 
Everyone seems to agree, so I've done this. [[Special:Contributions/98.182.22.49|98.182.22.49]] ([[User talk:98.182.22.49|talk]]) 23:23, 1 June 2019 (UTC)
 
: For the record, I don't agree at all. I consider this another example of a Wikipedia article going from something useful to something meaningless. [[User:Mlewan|Mlewan]] ([[User talk:Mlewan|talk]]) 07:18, 2 June 2019 (UTC)
 
:: Mlewan, I write software for a living, and I assure you it's still plenty useful. Anyone who wants to check digits in their code can just translate the pseudocode to their language of choice, which is trivial for any developer (or they'll just use a library that implements it).
 
:: I'm not just implementing policy for policy's sake. In fact, I almost never edit pages at all. I only bothered to change it because I ran into it at work and thought "Wow, that's excessive and pointless. I'm going to check the talk page to see what's going on here." This brings it in line with other algorithm pages, such as Quicksort or A*. [[Special:Contributions/98.182.22.49|98.182.22.49]] ([[User talk:98.182.22.49|talk]]) 22:40, 2 June 2019 (UTC)
 
== Python Example ==
 
If anyone is interested, I've cleaned up the python code a bit to make it easier to understand for newer programmers. I used a less "clever" implementation, and tried to name variables more clearly. I also consolidated the checksum code, so it is implemented once, and used in two ways to check validity, and to find the last digit.
Line 180 ⟶ 208:
I've also put up a gist on github with the code and tests showing correctness. https://gist.github.com/1773914
 
<sourcesyntaxhighlight lang="python">
def luhn_checksum(card_number):
def digits_of(n):
Line 198 ⟶ 226:
def calculate_luhn(partial_card_number):
return 10 - luhn_checksum(int(partial_card_number) * 10)
</syntaxhighlight>
</source>
[[Special:Contributions/206.169.213.106|206.169.213.106]] ([[User talk:206.169.213.106|talk]]) 21:08, 8 February 2012 (UTC)
:It looks good. I've put this version up in the article. – [[User:Acdx|Acdx]] <small>(<i>[[User_talk:Acdx|talk]]</i>)</small> 11:55, 18 February 2012 (UTC)
Line 206 ⟶ 234:
 
::: The Python example doesn't clarify anything. I have done minor programming with BuildBot; which I believe uses Python. I have used Perl, lisp, Basic, C, Java, C++, 8 different assembler languages, JavaScript, Pascal and Fortran. Any programmer who can not look at the table at the start and not translate it to the language they are using should not be programming. This is not a statement against Phyton. It is for the title of this section; remove all programming languages. I agree with this. [[Special:Contributions/71.19.161.130|71.19.161.130]] ([[User talk:71.19.161.130|talk]]) 20:51, 5 November 2014 (UTC)
 
:::: Agreed. American Express cards, in particular, use zero padding to adjust the position of the numbers. More concerning to me, however, is that this example is uncited and looks more like original research until you read this discussion, which no one should have to do. Then again, the code example was changed without a source, and thus... [[User:Nulbyte|Nulbyte]] ([[User talk:Nulbyte|talk]]) 21:00, 8 July 2015 (UTC)
 
== Ruby Solution ==
Line 211 ⟶ 241:
Here is a basic Ruby solution to luhn10 validation. I decided to place it here instead of the article.
 
<sourcesyntaxhighlight lang=ruby>
def luhn10?(number)
checksum = 0
Line 230 ⟶ 260:
split_integer(number).inject(0){ |sum,digit| sum += digit.to_i}
end
</syntaxhighlight>
</source>
 
<small><span class="autosigned">—&nbsp;Preceding [[Wikipedia:Signatures|unsigned]] comment added by [[User:{{{1}}}|{{{1}}}]] ([[User talk:{{{1}}}|talk]] • [[Special:Contributions/{{{1}}}|contribs]]) </span></small><!-- Template:Unsigned -->
Line 247 ⟶ 277:
I'm not going into details, but there's simply too much wrong with the C implementation. The link needs to be removed or the author needs to fix this.
--[[User:ENTi|eNTi]] ([[User talk:ENTi|talk]]) 20:57, 13 May 2015 (UTC)
 
== APL Implementation ==
<pre>
Luhnc←{10|-+/,0 10⊤⍵×⌽(≢⍵)⍴2 1} ⍝ calculate the check digit
Luhnv←{(⊃⌽⍵)≡Luhnc ¯1↓⍵} ⍝ verify the check digit
 
x← 7 9 9 2 7 3 9 8 7 1
Luhnc x
3
Luhnv x,3
1
</pre>
 
A team effort of the "Array Oriented Functional Programming with Dyalog" workshop at [http://functionalconf.com/ FunctionalConf '16]. &nbsp; [[User:Roger Hui|Roger Hui]] ([[User talk:Roger Hui|talk]]) 10:41, 16 October 2016 (UTC)
 
== Confusion about check digit ==
 
I am very confused. Why do some of the implementations completely ignore the check digit and others don't? And why would the digit be ignored? <!-- Template:Unsigned --><small class="autosigned">—&nbsp;Preceding [[Wikipedia:Signatures|unsigned]] comment added by [[User:Sollyucko|Sollyucko]] ([[User talk:Sollyucko#top|talk]] • [[Special:Contributions/Sollyucko|contribs]]) 18:29, 4 April 2019 (UTC)</small> <!--Autosigned by SineBot-->
 
== "third row": what&where is it? ==
 
Twice, the "Description" mentions a "third row". I have no idea where that third row is and what it contains. What are rows one&two? <!-- Template:Unsigned IP --><small class="autosigned">—&nbsp;Preceding [[Wikipedia:Signatures|unsigned]] comment added by [[Special:Contributions/146.60.210.112|146.60.210.112]] ([[User talk:146.60.210.112#top|talk]]) 14:40, 21 July 2019 (UTC)</small> <!--Autosigned by SineBot-->
 
 
I noticed the row mystery. Overall, this article is the worst description of anything I ever saw. Absolutely useless.
 
== Pseudocode ==
 
I’ve no idea what this is supposed to be doing. It looks incomplete as well as incorrect. [[User:Nick Levine|Nick Levine]] ([[User talk:Nick Levine|talk]]) 08:29, 11 May 2020 (UTC)
 
== Formula error ==
 
The sentence "The check digit is calculated by 10 - (s mod 10)." is not correct. If 's' is a multiple of 10 then the formula result is 10, which is not a digit. To correct this, the sentence should be updated to say "The check digit is calculated by (10 - (s mod 10)) mod 10.". <!-- Template:Unsigned IP --><small class="autosigned">—&nbsp;Preceding [[Wikipedia:Signatures|unsigned]] comment added by [[Special:Contributions/64.231.116.243|64.231.116.243]] ([[User talk:64.231.116.243#top|talk]]) 02:13, 20 June 2022 (UTC)</small> <!--Autosigned by SineBot-->
 
== pseudocode ==
 
Three comments on the pseudocode:
int parity := (nDigits-2)modulus 2
First, there is no such operation as "modulus". The operation is called "modulo" or "mod" for short. The "modulus" is the second operand in a mod operation (in this case, 2) not the operation or the result of the operation. Second, unless card numbers can have no digits other than a check digit, "(nDigits-2) mod 2" is equal to "nDigits mod 2". Third, type declarations and casts are undesirable in pseudocode unless the meaning of the code is unclear without them. [[User:Zero0000|Zero]]<sup><small>[[User_talk:Zero0000|talk]]</small></sup> 05:58, 7 October 2022 (UTC)
 
== One alternative formula is wrong ==
 
The formula 10[s/10] -s is wrong as written. The use of integer math is implied, for if fractional values are allowed, the formula is zero for all s. With internet arithmetic and given example where’s=67, the formula gives -7, not the correct answer of 3.
I would suggest it be r)e-written as
10 + { INT(10[s/10])- s}
Or perhaps better
10-{ s - INT( 10[s/10])}
This Alternative formula gives 3, but I have not carefully checked that it agrees with Luhn’s original formula.
Alternatively, just delete that line in the article. [[User:GWT45|GWT45]] ([[User talk:GWT45|talk]]) 04:10, 6 February 2023 (UTC)
 
:I think you've misread the formula: it's using the "ceiling" operator rather than square brackets. If s=67, then <math>\lceil s/10\rceil = \lceil 6.7\rceil = 7</math>. So the full equation evaluates to 3.
:The first equation <math>10 - (s\operatorname{mod} 10)</math> does look wrong though, since it'd result in 10 rather than 0 if s is a multiple of 10. [[User:JamesHenstridge|James]] ([[User talk:JamesHenstridge|talk]]) 10:54, 4 July 2023 (UTC)
 
== Unnecessarily complex implementations ==
 
Why do all these implementations do the unnecessary steps of separating the check digit from the payload, subtracting the weighted payload sum from 10 and comparing it with the check digit?
 
All you have to do is leave the check digit as part of the payload, weight that position as 1, mod the the whole sum by 10 and compare it to 0. [[User:Tzadikv|Tzadik]] ([[User talk:Tzadikv|talk]]) 12:13, 4 January 2024 (UTC)
 
== the C# example ==
 
The code is not correct, nor does it bring a correct result. [[User:לומד|לומד]] ([[User talk:לומד|talk]]) 08:26, 21 February 2024 (UTC)
 
:How is it wrong? Can you explain what the problem is, or give an example of an input that causes it to have the wrong output?
:[[User:Sollyucko|Solomon Ucko]] ([[User talk:Sollyucko|talk]]) 14:44, 21 February 2024 (UTC)
::- `in int[1] digits` doesn't compile because of the `1` (unless this is an upcoming feature of the language, but even so we probably don't want an array with only one element)
::- whether it "doubles" a digit is based on its index from the start, not the end, of the input
::- if the check digit needs to be 0, the code instead checks that it is 10
::There used to be a working example, but all the examples were deleted (see "Remove all implementation examples" above) and someone has since added this one instead. [[User:Rawling|Rawling]] ([[User talk:Rawling|talk]]) 10:09, 9 March 2024 (UTC)
 
== Is the final step in calculating the checksum actually correct? ==
 
According to the article, the last step is "The check digit is calculated by <math>(10 - (s \bmod 10))</math>". Unfortunately, following this logic, it would be IMPOSSIBLE for the check digit to ever equal 0. The value returned by <math>(s \bmod 10)</math> can only range between 0 and 9. So when you subtract that number from 10, you get a value that ranges between 10 and 1. This set of numbers is completely missing a 0, and it also includes a 10 which is too large for a single digit. The article here doesn't mention how this is to be fixed. I would assume if the output is 10 then it would then just be converted to 0 in an additional final step, but that's not mentioned anywhere in the article, so I'm not sure how that should actually be handled. [[Special:Contributions/73.169.149.34|73.169.149.34]] ([[User talk:73.169.149.34|talk]]) 20:16, 27 September 2024 (UTC)