简介
Luhn算法(Luhn algorithm),也称为“模10”(Mod 10)算法,是一种简单的校验和算法,一般用于验证身份识别码,例如发卡行识别码、国际移动设备辨识码(IMEI),美国国家提供商标识号码,或是加拿大社会保险号码, 银行卡卡号、IMEI号 。该算法由IBM科学家Hans Peter Luhn创造,专利于1954年1月6日申请,1960年8月23日颁证,美国专利号2950048。 成为大家公认的一项标准。
该算法现已属于公有领域并得到了广泛的应用,例如ISO/IEC 7812-1。它不是一种安全的加密哈希函数,设计它的目的只是防止意外出错而不是恶意攻击。 很多银行卡卡号和政府证件号码将该算法作为一种简单的方式用于从键盘错误录入或其他错误号码中分辨有效数字。
校验规则
Luhn算法会通过校验码对一串数字进行验证,校验码通常会被加到这串数字的末尾处,从而得到一个完整的身份识别码。 通过如下规则计算校验码的正确性:
我们以数字“7992739871”为例,计算其校验位:
第一种方法:
- 从校验位开始,从右往左,偶数位乘2(例如,1*2=2),然后将两位数字的个位与十位相加(例如,16:1+6=7,18:1+8=9);
- 把得到的数字加在一起(本例中得到67);
- 将数字的和取模10(本例中得到7),再用10去减(本例中得到3),得到校验位。
原始数字 | 7 | 9 | 9 | 2 | 7 | 3 | 9 | 8 | 7 | 1 | x |
---|---|---|---|---|---|---|---|---|---|---|---|
偶数位乘2 | 7 | 18 | 9 | 4 | 7 | 6 | 9 | 16 | 7 | 2 | x |
将数字相加 | 7 | 9 | 9 | 4 | 7 | 6 | 9 | 7 | 7 | 2 | x |
第二种种方法:
- 从校验位开始,从右往左,偶数位乘2,然后将两位数字的个位与十位相加;
- 计算所有数字的和(67);
- 乘以9(603);
- 取其个位数字(3),得到校验位。
第三种:
- 按照从右往左的顺序,从这串数字的右边开始,包含校验码,将偶数位数字乘以2,如果每次乘二操作的结果大于9(如 8 × 2 = 16),然后计算个位和十位数字的和(如 1 + 6 = 7)或者用这个结果减去9(如 16 - 9 = 7);
- 第一步操作过后会得到新的一串数字,计算所有数字的和(包含校验码);
- 用第二步操作得到的和进行“模10”运算,如果结果位0,表示校验通过,否则失败。
优点和缺点
Luhn算法可以检测出任何单码错误和近乎所有的相邻数字交换产生的错误,但是检测不出两个数字序列09和90的交换错误。它可以检测出十分之七比例的相同两位数交换错误(但22 ↔ 55, 33 ↔ 66 和 44 ↔ 77除外)。 其他更复杂的检查数字算法,如费尔赫夫算法,可以检测出更多的转录错误。模N的Luhn算法是Luhn算法的一个扩展,支持非数字字符串。因为该算法采取了从右向左的方式,而且零位会影响计算的结果。只有当零位造成了数位的移动或是用零来填充一串数字的开头时才不会影响计算结果的生成。因此不论在将1234用零填充为0001234之前或是之后,使用Luhn算法得到的结果都是一样的。
Java实现(第一,二中方法实现的)
package com.example.springbootwebdemo;
import java.util.regex.Pattern;
public class JavaLuhnAlgorithm {
public static void main(String[] args) {
//不满足
boolean resoult = validateCreditCardNumber("C5012711000014");
System.out.println(resoult);
//满足
boolean resoult2 = validateCreditCardNumber("79927398713");
System.out.println(resoult2);
//满足
boolean resoult3 = validateCreditCardNumber("5432123456788881");
System.out.println(resoult3);
//不满足
boolean resoult4 = validateCreditCardNumber("45012711000014");
System.out.println(resoult4);
}
/**
*
* @param code
* @return
*/
private static boolean validateCreditCardNumber(String code) {
//校验码
String checkCode = code.substring(code.length() - 1);
//抽取数字
String regEx = "[^0-9]";
Pattern pattern = Pattern.compile(regEx);
String targetCode = pattern.matcher(code).replaceAll("").trim();
//取数字的数组
int[] digits = new int[targetCode.length()];
for (int i = 0; i < targetCode.length(); i++) {
digits[i] = Integer.parseInt(targetCode.substring(i, i + 1));
}
int sum = 0;
int length = digits.length;
for (int i = 0; i < length; i++) {
//校验码除去
if (i == 0) {
continue;
}
int digit = digits[length - i - 1];
if (i % 2 == 1) {
digit *= 2;
}
sum += digit > 9 ? digit - 9 : digit;
}
String resultCode = 10 - sum % 10 +"";
return resultCode.equals(checkCode);
}
}