查找表或数组可以简化算法实现 - 节省许多代码行 - 并以此提高性能......如果查找索引的计算简单 - 或更简单 - 并且数组的内存占用是可以承受的。
另一方面,有时很难理解特定的查找数组或数据结构是如何形成的,因为相关的算法实现可能看起来 - 乍一看 - 与原始算法规范或描述完全不同。
使用查找表的指示是面向数字的算法,具有简单的算术、简单的比较和同样结构化的重复模式 - 当然 - 具有相当有限的值集。
该线程中的许多答案适用于不同的查找表,并适用于不同的算法来实现相同的 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
获取 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;
其中 - 与上述真假术语中的评论 - 当然是简化的:
var luhnFinalValue = (d < 5) ? d : (d - 5) * 2 + 1;
现在我不确定我是否“保存”了任何东西...... ;-) 特别感谢 if-then-else 的 value-formed 或short form。没有它,代码可能看起来像这样——带有“有序”块并嵌入到算法的下一个更高的上下文层中,因此是 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;
}
}
或者:
var luhnValue = (even) ? d : (d < 5) ? d * 2 : (d - 5) * 2 + 1;
顺便说一句,使用现代的优化解释器和(及时)编译器,区别仅在于源代码,仅对可读性很重要。
已经对使用查找表和与直接编码进行比较的解释和“理由”进行了这么多,现在查找表对我来说看起来有点矫枉过正。没有的算法现在很容易完成 - 而且它看起来也很紧凑:
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);
}
经过解释练习后,让我印象深刻的是,最初最诱人的实现 - 使用来自 @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
);
}
要忠实于该线程,必须提及更多查找表选项:
- 只需调整双位数的 varues 怎么样 - 正如@yngum 所发布的那样
- 正如@Simon_Weaver 所发布的那样,使用查找表的所有内容怎么样 - 其中非双位数的值也是从查找表中获取的。
- 仅使用一个查找表的所有内容怎么样- 受到广泛讨论的 luhnValid() 函数中使用偏移量的启发。
后者的代码 - 使用 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
);
}
并且对于关闭 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);
}
花冠:可以将字符串视为字符的查找表... ;-)
一个很好的查找表应用程序的完美示例是位列表中设置位的计数 - 在(解释的)高级语言(任何位操作都非常昂贵)中的一个(非常)长的 8 位字节字符串中设置的位. 查找表有 256 个条目。每个条目包含在等于条目索引的无符号 8 位整数中设置的位数。遍历字符串并获取无符号的 8 位字节相等值以从查找表中访问该字节的位数。即使对于低级语言 - 例如汇编程序/机器代码 - 查找表也是要走的路......尤其是在微码(指令)可以在一个(单个 CISC)中处理多达 256 个或更多字节的环境中) 操作说明。
一些注意事项:
- numberString * 1 和 parseInt(numberStr) 做的差不多。
- 有一些多余的缩进,括号等......支持我的大脑更快地获得语义......但是我想省略的一些实际上是必需的......当涉及到短格式的算术运算时,值-if-then-else 表达式作为术语。
- 某些格式对您来说可能看起来很新;例如,我使用延续逗号和延续与延续在同一行,并且我“关闭”事物 - 半个制表符 - 缩进到“开始”项目。
- 所有格式都是为人类完成的,而不是计算机......“它”确实不在乎。
算法数据 结构 luhn 查找表 信用卡 验证 位表