Javascript Luhn算法的实现
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12310837/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Implementation of Luhn algorithm
提问by Mithril
I am trying to implement simple validation of credit card numbers. I read about the Luhn algorithm on Wikipedia:
我正在尝试实现信用卡号的简单验证。我在维基百科上阅读了 Luhn 算法:
- Counting from the check digit, which is the rightmost, and moving left, double the value of every second digit.
- Sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5) together with the undoubled digits from the original number.
- If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
- 从最右边的校验位开始向左移动,每第二位的值加倍。
- 将乘积的数字(例如,10: 1 + 0 = 1, 14: 1 + 4 = 5)与原始数字的非双位数相加。
- 如果总模 10 等于 0(如果总和以零结尾),则根据 Luhn 公式,该数字有效;否则无效。
On Wikipedia, the description of the Luhn algorithm is very easily understood. However, I have also seen other implementations of the Luhn algorithm on Rosetta Codeand elsewhere.
在维基百科上,Luhn 算法的描述很容易理解。但是,我还在Rosetta Code和其他地方看到了 Luhn 算法的其他实现。
Those implementations work very well, but I am confused about why they can use an array to do the work. The array they use seems to have no relation with Luhn algorithm, and I can't see how they achieve the steps described on Wikipedia.
这些实现工作得很好,但我很困惑为什么他们可以使用数组来完成这项工作。他们使用的数组似乎与 Luhn 算法没有关系,我看不出他们是如何实现维基百科上描述的步骤的。
Why are they using arrays? What is the significance of them, and how are they used to implement the algorithm as described by Wikipedia?
他们为什么使用数组?它们的意义是什么,它们如何用于实现维基百科描述的算法?
采纳答案by yngccc
the array [0,1,2,3,4,-4,-3,-2,-1,0]
is used as a look up array for finding the difference between a number in 0-9 and the digit sum of 2 times its value. For example, for number 8, the difference between 8 and (2*8) = 16 -> 1+6 = 7 is 7-8 = -1.
该数组[0,1,2,3,4,-4,-3,-2,-1,0]
用作查找数组,用于查找 0-9 中的数字与其值的 2 倍数字总和之间的差值。例如,对于数字 8,8 和 (2*8) = 16 -> 1+6 = 7 之间的差为 7-8 = -1。
Here is graphical presentation, where {n} stand for sum of digit of n
这是图形表示,其中 {n} 代表 n 的数字之和
[{0*2}-0, {1*2}-1, {2*2}-2, {3*2}-3, {4*2}-4, {5*2}-5, {6*2}-6, {7*2}-7....]
| | | | | | | |
[ 0 , 1 , 2 , 3 , 4 , -4 , -3 , -2 ....]
The algorithm you listed just sum over all the digit and for each even spot digit, look up the the difference using the array, and apply it to the total sum.
您列出的算法只是对所有数字和每个偶数点数字求和,使用数组查找差异,并将其应用于总和。
回答by Ron
Unfortunately none of the codes above worked for me. But I found on GitHuba working solution
不幸的是,上面的代码都不适合我。但我在GitHub上找到了一个可行的解决方案
// takes the form field value and returns true on valid number
function valid_credit_card(value) {
// accept only digits, dashes or spaces
if (/[^0-9-\s]+/.test(value)) return false;
// The Luhn Algorithm. It's so pretty.
var nCheck = 0, nDigit = 0, bEven = false;
value = value.replace(/\D/g, "");
for (var n = value.length - 1; n >= 0; n--) {
var cDigit = value.charAt(n),
nDigit = parseInt(cDigit, 10);
if (bEven) {
if ((nDigit *= 2) > 9) nDigit -= 9;
}
nCheck += nDigit;
bEven = !bEven;
}
return (nCheck % 10) == 0;
}
回答by kolypto
Compact Luhn validator:
紧凑型 Luhn 验证器:
var luhn_validate = function(imei){
return !/^\d+$/.test(imei) || (imei.split('').reduce(function(sum, d, n){
return n===(imei.length-1)
? 0
: sum + parseInt((n%2)? d: [0,2,4,6,8,1,3,5,7,9][d]);
}, 0)) % 10 == 0;
};
Works fine for both CC and IMEI numbers. Fiddle: http://jsfiddle.net/8VqpN/
适用于 CC 和 IMEI 号码。小提琴:http: //jsfiddle.net/8VqpN/
回答by muet
Lookup tables or arrays can simplify algorithm implementations - save many lines of code - and with that increase performance... if the calculation of the lookup index is simple - or simpler - and the array's memory footprint is affordable.
查找表或数组可以简化算法实现 - 节省许多代码行 - 并提高性能......如果查找索引的计算很简单 - 或更简单 - 并且数组的内存占用是可以承受的。
On the other hand, understanding how the particular lookup array or data structure came to be can at times be quite difficult, because the related algorithm implementation may look - at first sight - quite different from the original algorithm specification or description.
另一方面,理解特定的查找数组或数据结构是如何形成的有时会非常困难,因为相关的算法实现可能看起来 - 乍一看 - 与原始算法规范或描述完全不同。
Indication to use lookup tables are number oriented algorithms with simple arithmetics, simple comparisons, and equally structured repetition patterns - and of course - of quite finite value sets.
使用查找表的指示是面向数字的算法,具有简单的算术、简单的比较和结构相同的重复模式 - 当然 - 非常有限的值集。
The many answers in this thread go for different lookup tables and with that for different algorithms to implement the very same Luhn algorithm. Most implementations use the lookup array to avoid the cumbersome figuring out of the value for doubled digits:
该线程中的许多答案针对不同的查找表,并针对不同的算法来实现相同的 Luhn 算法。大多数实现使用查找数组来避免计算双位数值的麻烦:
var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];
//
// ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
// | | | | | | | | | |
//
// - d-igit=index: 0 1 2 3 4 5 6 7 8 9
// - 1st
// calculation: 2*0 2*2 2*2 2*3 2*4 2*5 2*6 2*7 2*8 2*9
// - intermeduate
// value: = 0 = 2 = 4 = 6 = 8 =10 =12 =14 =16 =18
// - 2nd
// calculation: 1+0 1+2 1+4 1+6 1+8
//
// - final value: 0 2 4 6 8 =1 =3 =5 =7 =9
//
var luhnFinalValue = luhnArray[d]; // d is numeric value of digit to double
An equal implementation for getting the luhnFinalValue looks like this:
获取 luhnFinalValue 的等效实现如下所示:
var luhnIntermediateValue = d * 2; // d is numeric value of digit to double
var luhnFinalValue = (luhnIntermediateValue < 10)
? luhnIntermediateValue // (d ) * 2;
: luhnIntermediateValue - 10 + 1; // (d - 5) * 2 + 1;
Which - with the comments in above true and false terms - is of course simplified:
其中 - 带有上述真假术语的评论 - 当然是简化的:
var luhnFinalValue = (d < 5) ? d : (d - 5) * 2 + 1;
Now I'm not sure if I 'saved' anything at all... ;-) especially thanks the value-formed or short form of if-then-else. Without it, the code may look like this - with 'orderly' blocks and embedded in the next higher context layer of the algorithm and therefore luhnValue:
现在我不确定我是否“保存”了任何东西...... ;-) 特别感谢 if-then-else 的值形式或简短形式。没有它,代码可能看起来像这样 - 带有“有序”块并嵌入到算法的下一个更高的上下文层中,因此 luhnValue:
var luhnValue; // card number is valid when luhn values for each digit modulo 10 is 0
if (even) { // even as n-th digit from the the end of the string of digits
luhnValue = d;
} else { // doubled digits
if (d < 5) {
luhnValue = d * 2;
} else {
lunnValue = (d - 5) * 2 + 1;
}
}
Or:
或者:
var luhnValue = (even) ? d : (d < 5) ? d * 2 : (d - 5) * 2 + 1;
Btw, with modern, optimizing interpreters and (just in time) compilers, the difference is only in the source code and matters only for readability.
顺便说一句,使用现代的优化解释器和(及时)编译器,区别仅在于源代码,仅对可读性有影响。
Having come that far with explanation - and 'justification' - of the use of lookup tables and comparison to straight forward coding, the lookup table looks now a bit overkill to me. The algorithm without is now quite easy to finish - and it looks pretty compact too:
在解释 - 和“理由” - 使用查找表并与直接编码进行比较之后,查找表现在对我来说看起来有点矫枉过正。没有的算法现在很容易完成 - 它看起来也很紧凑:
function luhnValid(cardNo) { // cardNo as a string w/ digits only
var sum = 0, even = false;
cardNo.split("").reverse().forEach(function(dstr){ d = parseInt(dstr);
sum += ((even = !even) ? d : (d < 5) ? d * 2 : (d - 5) * 2 + 1);
});
return (sum % 10 == 0);
}
What strikes me after going through the explanation exercise is that the initially most enticing implementation - the one using reduce() from @kalypto - just lost totally its luster for me... not only because it is faulty on several levels, but more so because it shows that bells and whistles may not always 'ring the victory bell'. But thank you, @kalypto, it made me actually use - and understand - reduce():
在完成解释练习后,让我印象深刻的是,最初最吸引人的实现——使用来自@kalypto 的 reduce() 的实现——对我来说完全失去了光彩......不仅因为它在几个层面上都有缺陷,而且更是如此因为它表明花里胡哨可能并不总是“敲响胜利的钟声”。但谢谢你,@kalypto,它让我真正使用 - 并理解 - reduce():
function luhnValid2(cardNo) { // cardNo as a string w/ digits only
var d = 0, e = false; // e = even = n-th digit counted from the end
return ( cardNo.split("").reverse().reduce(
function(s,dstr){ d = parseInt(dstr); // reduce arg-0 - callback fnc
return (s + ((e = !e) ? d : [0,2,4,6,8,1,3,5,7,9][d]));
} // /end of callback fnc
,0 // reduce arg-1 - prev value for first iteration (sum)
) % 10 == 0
);
}
To be true to this thread, some more lookup table options have to be mentioned:
为了忠实于这个线程,必须提到一些更多的查找表选项:
- how about just adjust varues for doubled digits - as posted by @yngum
- how about just everything with lookup tables- as posted by @Simon_Weaver - where also the values for the non-doubled digits are taken from a look up table.
- how about just everything with just ONE lookup table- as inspired by the use of an offset as done in the extensively discussed luhnValid() function.
- 如何只调整双位数的 varues - 正如@yngum 发布的那样
- 仅带有查找表的所有内容如何-如@Simon_Weaver 发布的那样-其中非双位数的值也取自查找表。
- 仅使用一个查找表的所有内容如何- 受到广泛讨论的 luhnValid() 函数中使用偏移量的启发。
The code for the latter - using reduce - may look like this:
后者的代码 - 使用 reduce - 可能如下所示:
function luhnValid3(cardNo) { // cardNo as a string w/ digits only
var d = 0, e = false; // e = even = n-th digit counted from the end
return ( cardNo.split("").reverse().reduce(
function(s,dstr){ d = parseInt(dstr);
return (s + [0,1,2,3,4,5,6,7,8,9,0,2,4,6,8,1,3,5,7,9][d+((e=!e)?0:10)]);
}
,0
) % 10 == 0
);
}
And for closing lunValid4() - very compact - and using just 'old fashioned' (compatible) JavaScript - with one single lookup table:
并且为了关闭 lunValid4() - 非常紧凑 - 并且只使用“老式”(兼容)JavaScript - 使用一个查找表:
function luhnValid4(cardNo) { // cardNo as a string w/ digits only
var s = 0, e = false, p = cardNo.length; while (p > 0) { p--;
s += "01234567890246813579".charAt(cardNo.charAt(p)*1 + ((e=!e)?0:10)) * 1; }
return (s % 10 == 0);
}
Corollar: Stringscan be looked at as lookup tables of characters... ;-)
推论:字符串可以看作是字符查找表...... ;-)
A perfect example of a nice lookup table application is the counting of set bits in bits lists - bits set in a a (very) long 8-bit byte string in (an interpreted) high-level language (where any bit operations are quite expensive). The lookup table has 256 entries. Each entry contains the number of bits set in an unsigned 8-bit integer equal to the index of the entry. Iterating through the string and taking the unsigned 8-bit byte equal value to access the number of bits for that byte from the lookup table. Even for low-level language - such as assembler / machine code - the lookup table is the way to go... especially in an environment, where the microcode (instruction) can handle multiple bytes up to 256 or more in an (single CISC) instruction.
一个很好的查找表应用程序的完美示例是位列表中设置位的计数 - 在(解释的)高级语言中的(非常)长的 8 位字节字符串中设置的位(其中任何位操作都非常昂贵) . 查找表有 256 个条目。每个条目包含在等于条目索引的无符号 8 位整数中设置的位数。遍历字符串并采用无符号的 8 位字节等值来访问查找表中该字节的位数。即使对于低级语言 - 例如汇编程序/机器代码 - 查找表也是可行的方法......尤其是在微代码(指令)可以在(单个 CISC ) 操作说明。
Some notes:
一些注意事项:
- numberString * 1 and parseInt(numberStr) do about the same.
- there are some superfluous indentations, parenthesis,etc... supporting my brain in getting the semantics quicker... but some that I wanted to leave out, are actually required... when it comes to arithmetic operations with short-form, value-if-then-else expressions as terms.
- some formatting may look new to you; for examples, I use the continuation comma with the continuation on the same line as the continuation, and I 'close' things - half a tab - indented to the 'opening' item.
- All formatting is all done for the human, not the computer... 'it' does care less.
- numberString * 1 和 parseInt(numberStr) 做的差不多。
- 有一些多余的缩进,括号等......支持我的大脑更快地获得语义......但我想省略的一些实际上是必需的......当涉及到带有简短形式,值的算术运算时-if-then-else 表达式作为术语。
- 某些格式对您来说可能很新;例如,我使用延续逗号和延续在与延续相同的行上,并且我“关闭”东西——半个制表符——缩进到“开始”项目。
- 所有格式化都是为人类完成的,而不是计算机......“它”确实不在乎。
algorithmdatastructureluhnlookuptablecreditcardvalidationbitlist
回答by leodido
A very fastand elegant implementationof the Luhn algorithmfollowing:
一个非常快速和优雅的实现了的卢恩算法如下:
const isLuhnValid = function luhn(array) {
return function (number) {
let len = number ? number.length : 0,
bit = 1,
sum = 0;
while (len--) {
sum += !(bit ^= 1) ? parseInt(number[len], 10) : array[number[len]];
}
return sum % 10 === 0 && sum > 0;
};
}([0, 2, 4, 6, 8, 1, 3, 5, 7, 9]);
console.log(isLuhnValid("4112344112344113".split(""))); // true
console.log(isLuhnValid("4112344112344114".split(""))); // false
On my dedicated git repositoryyou can grab it and retrieve more info (like benchmarks link and full unit tests for ~50 browsers and some node.js versions).
在我的专用 git存储库中,您可以获取它并检索更多信息(例如基准测试链接和大约 50 个浏览器和一些 node.js 版本的完整单元测试)。
Or you can simply install it via boweror npm. It works both on browsers and/or node.
或者您可以简单地通过bower或npm安装它。它适用于浏览器和/或节点。
bower install luhn-alg
npm install luhn-alg
回答by Simon_Weaver
If you want to calculatethe checksum, this code from this pageis very concise and in my random tests seems to work.
如果你想计算校验和,这个页面的代码非常简洁,在我的随机测试中似乎有效。
NOTE:the verification algorithmns on this page do NOT all work.
注意:此页面上的验证算法并非全部有效。
// Javascript
String.prototype.luhnGet = function()
{
var luhnArr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]], sum = 0;
this.replace(/\D+/g,"").replace(/[\d]/g, function(c, p, o){
sum += luhnArr[ (o.length-p)&1 ][ parseInt(c,10) ]
});
return this + ((10 - sum%10)%10);
};
alert("54511187504546384725".luhnGet());?
Here's my findings for C#
这是我对 C# 的发现
回答by Hafthor
function luhnCheck(value) {
return 0 === (value.replace(/\D/g, '').split('').reverse().map(function(d, i) {
return +['0123456789','0246813579'][i % 2][+d];
}).reduce(function(p, n) {
return p + n;
}) % 10);
}
Update: Here's a smaller version w/o string constants:
更新:这是一个没有字符串常量的较小版本:
function luhnCheck(value) {
return !(value.replace(/\D/g, '').split('').reverse().reduce(function(a, d, i) {
return a + d * (i % 2 ? 2.2 : 1) | 0;
}, 0) % 10);
}
回答by Majid Laissi
Code is the following:
代码如下:
var LuhnCheck = (function()
{
var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];
return function(str)
{
var counter = 0;
var incNum;
var odd = false;
var temp = String(str).replace(/[^\d]/g, "");
if ( temp.length == 0)
return false;
for (var i = temp.length-1; i >= 0; --i)
{
incNum = parseInt(temp.charAt(i), 10);
counter += (odd = !odd)? incNum : luhnArr[incNum];
}
return (counter%10 == 0);
}
})();
The variable counter
is the sum of all the digit in odd positions, plus the double of the digits in even positions, when the double exceeds 10 we add the two numbers that make it (ex: 6 * 2 -> 12 -> 1 + 2 = 3)
变量counter
是奇数位置所有数字的总和,加上偶数位置数字的双倍,当双倍数超过 10 时,我们将两个数字相加(例如:6 * 2 -> 12 -> 1 + 2 = 3)
The Array you are asking about is the result of all the possible doubles
你问的数组是所有可能的双打的结果
var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];
var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];
- 0 * 2 = 0 --> 0
- 1 * 2 = 2 --> 2
- 2 * 2 = 4 --> 4
- 3 * 2 = 6 --> 6
- 4 * 2 = 8 --> 8
- 5 * 2 = 10 --> 1+0 --> 1
- 6 * 2 = 12 --> 1+2 --> 3
- 7 * 2 = 14 --> 1+4 --> 5
- 8 * 2 = 16 --> 1+6 --> 7
- 9 * 2 = 18 --> 1+8 --> 9
- 0 * 2 = 0 --> 0
- 1 * 2 = 2 --> 2
- 2 * 2 = 4 --> 4
- 3 * 2 = 6 --> 6
- 4 * 2 = 8 --> 8
- 5 * 2 = 10 --> 1+0 --> 1
- 6 * 2 = 12 --> 1+2 --> 3
- 7 * 2 = 14 --> 1+4 --> 5
- 8 * 2 = 16 --> 1+6 --> 7
- 9 * 2 = 18 --> 1+8 --> 9
So for example
所以例如
luhnArr[3] --> 6 (6 is in 3rd position of the array, and also 3 * 2 = 6)
luhnArr[7] --> 5 (5 is in 7th position of the array, and also 7 * 2 = 14 -> 5 )
回答by garryp
Another alternative:
另一种选择:
function luhn(digits) {
return /^\d+$/.test(digits) && !(digits.split("").reverse().map(function(checkDigit, i) {
checkDigit = parseInt(checkDigit, 10);
return i % 2 == 0
? checkDigit
: (checkDigit *= 2) > 9 ? checkDigit - 9 : checkDigit;
}).reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}) % 10);
}
回答by Pardeep Jain
Alternative ;) Simple and Best
替代 ;) 简单和最好
<script>
// takes the form field value and returns true on valid number
function valid_credit_card(value) {
// accept only digits, dashes or spaces
if (/[^0-9-\s]+/.test(value)) return false;
// The Luhn Algorithm. It's so pretty.
var nCheck = 0, nDigit = 0, bEven = false;
value = value.replace(/\D/g, "");
for (var n = value.length - 1; n >= 0; n--) {
var cDigit = value.charAt(n),
nDigit = parseInt(cDigit, 10);
if (bEven) {
if ((nDigit *= 2) > 9) nDigit -= 9;
}
nCheck += nDigit;
bEven = !bEven;
}
return (nCheck % 10) == 0;
}
console.log(valid_credit_card("5610591081018250"),"valid_credit_card Validation");
</script>
Best Solution here
最佳解决方案在这里
with all test cases passed according to
根据所有测试用例通过
and the credit goes to
功劳归于