如何解决Luhn 算法逻辑
我目前正在学习 Codecademy 的全栈工程师课程,到目前为止我一直很擅长,发现新事物,自己解决问题,但这对我的进步来说是一个严重的障碍,因为我可以' t 似乎确定了这个逻辑的问题。我并不是要质疑 Luhn 的算法,但我真的需要对此进行一些澄清......
所以我的问题是,算法将我的所有数组返回为有效,我的代码如下(codecademy 提供的数组):
// All valid credit card numbers
const valid1 = [4,5,3,9,6,7,8,1,8];
const valid2 = [5,4,9];
const valid3 = [3,2,6];
const valid4 = [6,5];
const valid5 = [4,6];
// All invalid credit card numbers
const invalid1 = [4,5];
const invalid2 = [5,3];
const invalid3 = [3,4];
const invalid4 = [6,5];
const invalid5 = [5,4];
// Can be either valid or invalid
const mystery1 = [3,4];
const mystery2 = [5,9];
const mystery3 = [6,3];
const mystery4 = [4,3];
const mystery5 = [4,3];
// An array of all the arrays above
const batch = [valid1,valid2,valid3,valid4,valid5,invalid1,invalid2,invalid3,invalid4,invalid5,mystery1,mystery2,mystery3,mystery4,mystery5];
以及我实现算法的函数:
const validateCred = arr => {
let checkSum = 0;
let ifEvendouble = 0;
arr.push(checkSum);
//Iterate through array,double what is needed
for(let i = arr.length - 2; i >= 0; i--){
console.log(ifEvendouble);
//If ifEvendouble is even,we are at the 'other' cell
if((ifEvendouble % 2) === 0){
let doubled = arr[i] * 2;
//If doubled digit is greater than 9,store sum of individual digits
//Convert the doubled number to a string then extract each member and convert back to number for calculation,add to checkSum and skip to next iteration,otherwise,add arr[i]
let newDigit = 0;
if(doubled > 9){
newDigit = Number(doubled.toString()[0]) + Number(doubled.toString()[1]);
//Add doubled & split digit to total and continue the loop
checkSum += newDigit;
ifEvendouble++;
continue;
}
//Add doubled digit less than 9 to total and continue the loop
checkSum += doubled;
ifEvendouble++;
continue;
}
//Add current array member to total
checkSum += arr[i];
ifEvendouble++;
}//End for loop
console.log(checkSum);
const checkDigit = (checkSum * 9) % 10;
const totalSum = checkDigit + checkSum;
if(totalSum % 10 === 0){
console.log('Valid');
return true;
} else {
console.log('Invalid');
return false;
}
};
validateCred(invalid1); // -> Output: Valid
根据我的理解,我的 totalSum 总是 将是 10 的倍数,如果我从 10 中减去我的个位数,将其添加到我的 checkSum 中是总是 给我 10 的倍数。我错了吗?
编辑:我一直在尝试调试这个,但我做的越多,离我偏离的核心算法越远。
编辑(2):所以感谢下面的人,我认为我的问题是生成我自己的校验码而不是使用已经提供的校验码?我的困惑是,从阅读维基百科页面来看,它说:
'计算校验位的例子: 假设一个帐号“7992739871”将添加一个校验位,使其格式为 7992739871x'
然后他们开始用除 x 之外的数字进行所有计算,我认为这是现在的主要困惑。
解决方法
您的算法过于复杂。 Wikipedia 描述简洁,只需执行 3 个步骤
- 从最右边的数字(不包括校验位)开始向左移动,每第二个数字的值加倍。校验位既不加倍也不包括在此计算中;翻倍的第一个数字是位于校验位左侧的数字。如果此加倍运算的结果大于 9(例如,8 × 2 = 16),则将结果的位数相加(例如,16: 1 + 6 = 7,18: 1 + 8 = 9)或等效地,从结果中减去 9(例如,16: 16 − 9 = 7,18: 18 − 9 = 9)。
- 求所有数字的总和(包括校验位)。
- 如果总模 10 等于 0(如果总和以零结尾),则根据 Luhn 公式,该数字有效;否则无效。
我还认为您误解了校验位是什么。您似乎将它作为 0
附加到数组中,并尝试在最后计算它。它已经存在于数字中 - 这是最后一位。
const validateCred = arr => {
let doubleIt = true;
let sum = 0;
// From the rightmost digit excluding check digit...
for(let i = arr.length - 2; i >= 0; i--){
if(doubleIt){
let doubled = arr[i] * 2;
if(doubled > 9){
doubled -= 9
}
sum += doubled
}
else {
sum += arr[i]
}
doubleIt = !doubleIt;
}
// Add the check digit to the sum
sum += arr[arr.length-1];
// If sum is divisible by 10 it is valid
if(sum % 10 === 0){
console.log('Valid');
return true;
} else {
console.log('Invalid');
return false;
}
};
const invalid1 = [4,5,3,2,7,8,1,9,5];
const valid1 = [4,6,8];
validateCred(invalid1);
validateCred(valid1);
你出错的地方主要是校验位的使用。您似乎正在计算它,而它已经作为数组中的最后一个元素存在。下面的代码片段更接近您的原始代码,只是没有计算校验位。
const validateCred = arr => {
let ifEvenDouble = 0;
let checkSum=0
//Iterate through array,double what is needed
for(let i = arr.length - 2; i >= 0; i--){
//If ifEvenDouble is even,we are at the 'other' cell
if((ifEvenDouble % 2) === 0){
let doubled = arr[i] * 2;
//If doubled digit is greater than 9,store sum of individual digits
//Convert the doubled number to a string then extract each member and convert back to number for calculation,add to checkSum and skip to next iteration,otherwise,add arr[i]
let newDigit = 0;
if(doubled > 9){
newDigit = Number(doubled.toString()[0]) + Number(doubled.toString()[1]);
//Add doubled & split digit to total and continue the loop
checkSum += newDigit;
ifEvenDouble++;
continue;
}
//Add doubled digit less than 9 to total and continue the loop
checkSum += doubled;
ifEvenDouble++;
continue;
}
//Add current array member to total
checkSum += arr[i];
ifEvenDouble++;
}//End for loop
const checkDigit = arr[arr.length-1]
const totalSum = checkDigit + checkSum;
if(totalSum % 10 === 0){
console.log('Valid');
return true;
} else {
console.log('Invalid');
return false;
}
};
const invalid1 = [4,8];
validateCred(invalid1);
validateCred(valid1);
我知道问题更多的是关于你哪里出错了,而不是更好的解决方案; Jamiec 的回答已经很好地涵盖了这一点。
但是,通过大量数组方法,我们应该能够写出一个更简单的答案。
// utility functions
const sum = (ns) => ns .reduce ((a,b) => a + b,0)
const last = (xs) => xs [xs .length - 1]
// helper function
const doubleDig = (d) => 2 * d > 9 ? 2 * d - 9 : 2 * d
// main function
const luhn = (ds) =>
(sum ([... ds] .map (Number) .reverse () .slice(1) .map (
(d,i) => i % 2 == 0 ? doubleDig (d) : d
)) + Number (last (ds))) % 10 == 0
// sample data
const batch = [
/* Valid */ [4,8],[5,4,9],[3,6],[6,5],[4,/* Invalid */ [4,3],4],/* Mystery */ [3,]
// demo
console.log (batch .map (luhn))
console .log (
luhn ('4539677908016808'),luhn ('4532778771091795')
)
.as-console-wrapper {max-height: 100% !important; top: 0}
此函数适用于所提供的一位数数组,但也适用于一串数字,因此 luhn ('4539677908016808')
等价于 luhn ([4,8])
。
首先,我们确保我们使用的是一组数字,使用 [... ds] .map (Number)
。然后我们 .reverse
数组,以便更轻松地跟踪偶数和奇数位置,而无需摆弄数组长度。我们 .slice
关闭了 now-first 元素,稍后我们只需要它作为校验位。现在我们映射结果,将偶数加倍并根据需要剔除 9(使用辅助函数 doubleDig
),但保持奇数不变。我们使用辅助函数 sum
对结果求和,并使用辅助函数 last
找到最后一位数字,将其转换为数字并将其添加到总数中。我们以模数为基数 10 结束,并报告该值是否为 0。
那些辅助函数很有用,我几乎总是喜欢使用这样的函数,但是每个函数只在我们的主函数中的一个地方被调用,这使得我们可以轻松地将它们内联,并且我们可以编写一个支架- 这样做的单独版本:
const luhn = (ds) =>
([...ds] .map (Number) .reverse () .slice(1) .map (
(d,i) => i % 2 == 0 ? (2 * d > 9 ? 2 * d - 9 : 2 * d) : d
) .reduce ((a,0) + Number (ds [ds .length - 1])) % 10 == 0
我不认为这是一种改进。虽然原始版本很密集,但也不难理解。这个,尤其是我们将 doubleDig
的条件表达式(三元)内联到另一个三元中时,看起来非常难看。也许更宽敞的布局会有所帮助。但是 sum (...)
绝对比 (...) .reduce ((a,0)
更干净,而 last
比它的替代品更干净。总的来说,这并不鼓舞人心。但最好将其识别为上述辅助函数分解的替代方案。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。