如何实现Luhn算法?

时间:2014-10-15 13:32:08

标签: java algorithm luhn

我正在尝试创建一个程序来验证基于luhn算法的10到12位长数字序列,但是我的程序一直告诉我每个数字都是无效的,即使它们不是。

此数字应该有效,但我的代码不这么认为:8112189876

这个号码不应该是我的程序同意的,因为它认为每个号码都是无效的:8112189875

这是我的代码:

static void luhn(){
    System.out.print("Enter number to validate:\n");
    String pnr = input.nextLine();
    int length = pnr.length();
    int sum = 0;
    for (int i = 1, pos = length - 1; i < 10; i++, pos--){
        char tmp = pnr.charAt(pos);
        int num = tmp - 0
        int product;
        if (i % 2 != 0){
            product = num * 1;
        }
        else{
            product = num * 2;
        }
        if (product > 9)
            product -= 9;
        sum+= product;              
        boolean valid = (sum % 10 == 0);
        if (valid){
            System.out.print("Valid!\r");
        }
        else{
            System.out.print("Invalid!");
        }
    }
}

8 个答案:

答案 0 :(得分:14)

使用org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit.LUHN_CHECK_DIGIT.isValid(number)

Maven依赖:

<dependency>
    <groupId>commons-validator</groupId>
    <artifactId>commons-validator</artifactId>
    <version>1.5.1</version>
</dependency>

答案 1 :(得分:7)

我看到的第一件事就是:

int num = tmp - 0

你应该改为:

int num = tmp - '0';

其次,您应该验证for循环的之和,因为您只关心处理完所有数字后的总和。

第三,您从数字的末尾开始,并且您不包括字符串的第一个数字。为什么不将i用于这两项任务?

结果(工作)方法:

static void luhn(){
  System.out.print("Enter number to validate:\n");
  String pnr = input.nextLine();
  // this only works if you are certain all input will be at least 10 characters
  int extraChars = pnr.length() - 10;
  if (extraChars < 0) {
    throw new IllegalArgumentException("Number length must be at least 10 characters!");
  }
  pnr = pnr.substring(extraChars, 10 + extraChars);
  int sum = 0;
  // #3: removed pos
  for (int i = 0; i < pnr.length(); i++){
    char tmp = pnr.charAt(i);
    // #1: fixed the '0' problem
    int num = tmp - '0';
    int product;
    if (i % 2 != 0){
      product = num * 1;
    }
    else{
      product = num * 2;
    }
    if (product > 9)
      product -= 9;
    sum+= product;              
  }
  // #2: moved check outside for loop
  boolean valid = (sum % 10 == 0);
  if (valid){
    System.out.print("Valid!\r");
  }
  else{
    System.out.print("Invalid!");
  }
}

文体,如果代替方法签名

,此方法会更有用
static void luhn() {

它取而代之的是方法签名

static boolean luhn(String input) {

这很容易让您的代码从任何来源(文件,硬编码等)获取String并对结果执行任何操作(打印消息,或者执行其他操作)。显然,您会在此方法之外移动System.out.printinput.nextLine()if(valid)位代码。

完全重构的程序:

import java.util.Scanner;

public class Luhn {
  private static Scanner input;

  public static void main(String... args) {
    input = new Scanner(System.in);
    System.out.print("Enter number to validate:\n");
    String pnr = input.nextLine();
    boolean result = luhn(pnr);
    printMessage(result);
    input.close();
  }

  static boolean luhn(String pnr){
    // this only works if you are certain all input will be at least 10 characters
    int extraChars = pnr.length() - 10;
    if (extraChars < 0) {
      throw new IllegalArgumentException("Number length must be at least 10 characters!");
    }
    pnr = pnr.substring(extraChars, 10 + extraChars);
    int sum = 0;
    for (int i = 0; i < pnr.length(); i++){
      char tmp = pnr.charAt(i);
      int num = tmp - '0';
      int product;
      if (i % 2 != 0){
        product = num * 1;
      }
      else{
        product = num * 2;
      }
      if (product > 9)
        product -= 9;
      sum+= product;              
    }
    return (sum % 10 == 0);
  }

  private static void printMessage(boolean valid) {
    if (valid){
      System.out.print("Valid!\r");
    }
    else{
      System.out.print("Invalid!");
    }
  }
}

答案 2 :(得分:2)

我在应用中使用此功能来检查卡号有效性:

public static boolean Check(String ccNumber)
    {
            int sum = 0;
            boolean alternate = false;
            for (int i = ccNumber.length() - 1; i >= 0; i--)
            {
                    int n = Integer.parseInt(ccNumber.substring(i, i + 1));
                    if (alternate)
                    {
                            n *= 2;
                            if (n > 9)
                            {
                                    n = (n % 10) + 1;
                            }
                    }
                    sum += n;
                    alternate = !alternate;
            }
            return (sum % 10 == 0);
    }

希望它有所帮助,

答案 3 :(得分:0)

你应该从tmp减去'0'而不是0.减去0会返回你不想要的ASCII值。

答案 4 :(得分:0)

这里我写了一些函数,既可以计算给定数字的校验位,也可以验证给定数字序列并从中提取数字。

要计算给定号码的校验位:

/**
 * Generates the check digit for a number using Luhn's algorithm described in detail at the following link:
 * https://en.wikipedia.org/wiki/Luhn_algorithm
 *
 * In short the digit is calculated like so:
 * 1. From the rightmost digit moving left, double the value of every second digit. If that value is greater than 9,
 *    subtract 9 from it.
 * 2. Sum all of the digits together
 * 3. Multiply the sum by 9 and the check digit will be that value modulo 10.
 *
 * @param number the number to get the Luhn's check digit for
 * @return the check digit for the given number
 */
public static int calculateLuhnsCheckDigit(final long number) {
    int     sum       = 0;
    boolean alternate = false;
    String  digits    = Long.toString(number);

    for (int i = digits.length() - 1; i >= 0; --i) {
        int digit = Character.getNumericValue(digits.charAt(i)); // get the digit at the given index
        digit = (alternate = !alternate) ? (digit * 2) : digit;  // double every other digit
        digit = (digit > 9)              ? (digit - 9) : digit;  // subtract 9 if the value is greater than 9
        sum += digit;                                            // add the digit to the sum
    }

    return (sum * 9) % 10;
}

要使用Luhn算法验证数字序列并提取数字:

/**
 * Verifies that a given number string is valid according to Luhn's algorithm, which is described in detail here:
 * https://en.wikipedia.org/wiki/Luhn_algorithm
 *
 * In short, validity of the number is determined like so:
 * 1. From the rightmost digit (the check digit) moving left, double the value of every second digit. The check
 *    digit is not doubled; the first digit doubled is the one immediately to the left of the check digit. If that
 *    value is greater than 9, subtract 9 from it.
 * 2. Sum all of the digits together
 * 3. If the sum modulo 10 is equal to 0, then the number is valid according to Luhn's algorithm
 *
 * @param luhnsNumber the number string to verify and extract the number from
 * @return an empty Optional if the given string was not valid according to Luhn's algorithm
 *         an Optional containing the number verified by Luhn's algorithm if the given string passed the check
 */
public static Optional<Long> extractLuhnsNumber(final String luhnsNumber) {
    int     sum       = 0;
    boolean alternate = true;
    Long    number    = Long.parseLong(luhnsNumber.substring(0, luhnsNumber.length() - 1));

    for (int i = luhnsNumber.length() - 1; i >= 0; --i) {
        int digit = Character.getNumericValue(luhnsNumber.charAt(i)); // get the digit at the given index
        digit = (alternate = !alternate) ? (digit * 2) : digit;       // double every other digit
        digit = (digit > 9)              ? (digit - 9) : digit;       // subtract 9 if the value is greater than 9
        sum += digit;                                                 // add the digit to the sum
    }

    return (sum % 10 == 0) ? Optional.of(number) : Optional.empty();
}

答案 5 :(得分:0)

此帖子/问题的新手可以检查相应的Wikipedia page以获得解决方案。下面是从那里复制粘贴的Java代码。

public class Luhn
{
        public static boolean check(String ccNumber)
        {
                int sum = 0;
                boolean alternate = false;
                for (int i = ccNumber.length() - 1; i >= 0; i--)
                {
                        int n = Integer.parseInt(ccNumber.substring(i, i + 1));
                        if (alternate)
                        {
                                n *= 2;
                                if (n > 9)
                                {
                                        n = (n % 10) + 1;
                                }
                        }
                        sum += n;
                        alternate = !alternate;
                }
                return (sum % 10 == 0);
        }
}

答案 6 :(得分:0)

如果您使用Java 10或更高版本,则可以使用以下代码:

public static boolean luhn(String s) {
    IntUnaryOperator sumDigits = n -> n / 10 + n % 10;
    var digits = s.chars()
                  .map(Character::getNumericValue)
                  .toArray();
    return IntStream.rangeClosed(1, digits.length)
                    .map(i -> digits.length - i)
                    .map(i -> i % 2 == 0 ? digits[i] : sumDigits.applyAsInt(digits[i] * 2))
                    .sum() % 10 == 0;
}

这是该算法的功能方法。

答案 7 :(得分:0)

package randomNumGen;

public class JavaLuhnAlgorithm {

    public static void main(String[] args) {
        // TODO Auto-generated method stub




        validateCreditCardNumber("8112189876");
        String imei = "012850003580200";
        validateCreditCardNumber(imei);
    }

    private static void validateCreditCardNumber(String str) {

        int[] ints = new int[str.length()];
        for (int i = 0; i < str.length(); i++) {
            ints[i] = Integer.parseInt(str.substring(i, i + 1));
        }
        for (int i = ints.length - 2; i >= 0; i = i - 2) {
            int j = ints[i];
            j = j * 2;
            if (j > 9) {
                j = j % 10 + 1;
            }
            ints[i] = j;
        }
        int sum = 0;
        for (int i = 0; i < ints.length; i++) {
            sum += ints[i];
        }
        if (sum % 10 == 0) {
            System.out.println(str + " is a valid credit card number");
        } else {
            System.out.println(str + " is an invalid credit card number");
        }
    }

}