Whiz Tools

Luhn算法计算器

Luhn 算法计算器

介绍

Luhn 算法,也称为“模 10”或“模 10”算法,是一种简单的校验和公式,用于验证各种识别号码,例如信用卡号码、加拿大社会保险号码、IMEI号码和美国国家提供者标识符号码。此计算器允许您使用 Luhn 算法验证号码并生成通过 Luhn 检查的有效号码。

Luhn 算法的工作原理

Luhn 算法的工作原理如下:

  1. 从右侧的数字开始(不包括校验位),向左移动,双倍每个第二位数字的值。
  2. 如果此双倍操作的结果大于 9,则从结果中减去 9。
  3. 将结果序列中的所有数字相加。
  4. 如果总和模 10 等于 0(如果总和以零结尾),则该号码根据 Luhn 公式是有效的;否则,它是无效的。

以下是 Luhn 算法的可视化表示:

1. 双倍每个第二位数字 2. 求和数字(对于双倍 > 9 的数字为 9) 3. 计算总和 4. 检查总和 % 10 == 0

公式

Luhn 算法可以用数学公式表示如下:

did_i 为第 ii 位数字,从右侧数字开始(不包括校验位)并向左移动。然后选择校验位 d0d_0 使得:

(2d2nmod9+d2n1+2d2n2mod9+d2n3++2d2mod9+d1+d0)mod10=0(2d_{2n} \bmod 9 + d_{2n-1} + 2d_{2n-2} \bmod 9 + d_{2n-3} + \cdots + 2d_2 \bmod 9 + d_1 + d_0) \bmod 10 = 0

其中 mod\bmod 是模运算。

用例

Luhn 算法在不同领域有多种应用:

  1. 信用卡验证:大多数信用卡号码使用 Luhn 算法进行验证。
  2. 加拿大社会保险号码:Luhn 算法用于验证这些识别号码的有效性。
  3. IMEI号码:手机 IMEI号码包含一个由 Luhn 算法验证的校验位。
  4. 国家提供者标识符 (NPI)号码:在美国医疗系统中使用,这些号码通过 Luhn 算法进行验证。
  5. ISBN:一些 ISBN-10号码使用 Luhn 算法的变体进行验证。

替代方案

虽然 Luhn 算法被广泛使用,但还有其他校验和算法用于不同的目的:

  1. Damm 算法:另一种校验位算法,可以检测所有单个数字错误和所有相邻交换错误。
  2. Verhoeff 算法:一种更复杂的校验和算法,可以捕获所有单个数字错误和大多数交换错误。
  3. ISBN-13 校验位:使用与 ISBN-10 不同的算法,该算法基于 Luhn 算法。

历史

Luhn 算法由 IBM 计算机科学家汉斯·彼得·吕恩于 1954 年创建。吕恩是信息科学领域的先驱,因多项创新而闻名,包括 KWIC(上下文中的关键字)索引系统。

该算法最初旨在防止意外错误,而非恶意攻击。重要的是要注意,虽然 Luhn 算法可以检测许多常见错误,但它不是一种安全的加密形式,不应依赖于数据安全目的。

尽管年代久远,Luhn 算法仍因其简单性和有效性而广泛使用,能够捕获常见的抄写错误。

实现示例

以下是用不同编程语言实现 Luhn 算法的一些代码示例:

import random

def luhn_validate(number):
    digits = [int(d) for d in str(number)]
    checksum = 0
    for i in range(len(digits) - 1, -1, -1):
        d = digits[i]
        if (len(digits) - i) % 2 == 0:
            d = d * 2
            if d > 9:
                d -= 9
        checksum += d
    return checksum % 10 == 0

def generate_valid_number(length):
    digits = [random.randint(0, 9) for _ in range(length - 1)]
    checksum = sum(digits[::2]) + sum(sum(divmod(d * 2, 10)) for d in digits[-2::-2])
    check_digit = (10 - (checksum % 10)) % 10
    return int(''.join(map(str, digits + [check_digit])))

## 示例用法:
print(luhn_validate(4532015112830366))  # True
print(luhn_validate(4532015112830367))  # False
print(generate_valid_number(16))  # 生成一个有效的 16 位数字
function luhnValidate(number) {
    const digits = number.toString().split('').map(Number);
    let checksum = 0;
    for (let i = digits.length - 1; i >= 0; i--) {
        let d = digits[i];
        if ((digits.length - i) % 2 === 0) {
            d *= 2;
            if (d > 9) d -= 9;
        }
        checksum += d;
    }
    return checksum % 10 === 0;
}

function generateValidNumber(length) {
    const digits = Array.from({length: length - 1}, () => Math.floor(Math.random() * 10));
    const checksum = digits.reduce((sum, digit, index) => {
        if ((length - 1 - index) % 2 === 0) {
            digit *= 2;
            if (digit > 9) digit -= 9;
        }
        return sum + digit;
    }, 0);
    const checkDigit = (10 - (checksum % 10)) % 10;
    return parseInt(digits.join('') + checkDigit);
}

// 示例用法:
console.log(luhnValidate(4532015112830366));  // true
console.log(luhnValidate(4532015112830367));  // false
console.log(generateValidNumber(16));  // 生成一个有效的 16 位数字
import java.util.Random;

public class LuhnValidator {
    public static boolean luhnValidate(long number) {
        String digits = String.valueOf(number);
        int checksum = 0;
        boolean isEven = true;
        for (int i = digits.length() - 1; i >= 0; i--) {
            int digit = Character.getNumericValue(digits.charAt(i));
            if (isEven) {
                digit *= 2;
                if (digit > 9) digit -= 9;
            }
            checksum += digit;
            isEven = !isEven;
        }
        return checksum % 10 == 0;
    }

    public static long generateValidNumber(int length) {
        Random random = new Random();
        long[] digits = new long[length - 1];
        for (int i = 0; i < length - 1; i++) {
            digits[i] = random.nextInt(10);
        }
        long checksum = 0;
        for (int i = digits.length - 1; i >= 0; i--) {
            long digit = digits[i];
            if ((length - 1 - i) % 2 == 0) {
                digit *= 2;
                if (digit > 9) digit -= 9;
            }
            checksum += digit;
        }
        long checkDigit = (10 - (checksum % 10)) % 10;
        long result = 0;
        for (long digit : digits) {
            result = result * 10 + digit;
        }
        return result * 10 + checkDigit;
    }

    public static void main(String[] args) {
        System.out.println(luhnValidate(4532015112830366L));  // true
        System.out.println(luhnValidate(4532015112830367L));  // false
        System.out.println(generateValidNumber(16));  // 生成一个有效的 16 位数字
    }
}

边缘案例和特殊考虑

在实现 Luhn 算法时,请考虑以下边缘案例和特殊考虑:

  1. 输入验证:确保输入是有效的数字字符串。应适当处理非数字字符(可以删除或视为无效输入)。

  2. 前导零:算法应能正确处理具有前导零的数字。

  3. 大数字:准备处理可能超出某些编程语言标准整数类型容量的非常长的数字。

  4. 空输入:定义您的实现应如何处理空字符串或 null 输入。

  5. 非标准字符集:在某些应用中,您可能会遇到以标准 0-9 范围之外的字符表示的数字。定义这些应如何处理。

  6. 性能考虑:对于需要快速验证大量输入的应用,请考虑优化算法实现。

数值示例

  1. 有效的信用卡号码:

    • 号码:4532015112830366
    • Luhn 检查:有效
  2. 无效的信用卡号码:

    • 号码:4532015112830367
    • Luhn 检查:无效
  3. 有效的加拿大社会保险号码:

    • 号码:046 454 286
    • Luhn 检查:有效
  4. 无效的 IMEI号码:

    • 号码:490154203237518
    • Luhn 检查:无效

测试用例

为了验证 Luhn 算法的实现,您可以使用以下测试用例:

def test_luhn_algorithm():
    assert luhn_validate(4532015112830366) == True
    assert luhn_validate(4532015112830367) == False
    assert luhn_validate(79927398713) == True
    assert luhn_validate(79927398714) == False
    
    # 测试生成的数字
    for _ in range(10):
        assert luhn_validate(generate_valid_number(16)) == True
    
    print("所有测试通过!")

test_luhn_algorithm()

参考文献

  1. Luhn, H. P. (1960). "计算机验证号码". 美国专利 2,950,048.
  2. Gallian, Joseph. "识别号码的数学". 《大学数学杂志》,第 22 卷,第 3 期,1991 年,第 194–202 页。JSTOR,www.jstor.org/stable/2686878.
  3. "ISO/IEC 7812-1:2017". 国际标准化组织。检索于 2024 年 8 月 2 日。
  4. Knuth, Donald. "计算机程序设计的艺术,第二卷:半数值算法". Addison-Wesley, 1997.
反馈