Luhn公式的实现

时间:2014-01-12 19:47:25

标签: python math checksum luhn

我试图在Python中实现Luhn Formula,这是我的代码:

import sys


def luhn_check(number):
    if number.isdigit():
        last_digit = int(str(number)[-1])
        reverse_sequence = list(int(d) for d in str(int(number[-2::-1])))

        for i in range(0, len(reverse_sequence), 2):
            reverse_sequence[i] *= 2

        for i in range(len(reverse_sequence)):
            if reverse_sequence[i] > 9:
                reverse_sequence[i] -= 9

        sum_of_digits = 0
        for i in range(len(reverse_sequence)):
            sum_of_digits += reverse_sequence[i]

        result = divmod(sum_of_digits, 10)

        if result == last_digit:
            print("[VALID] %s" % number)
        else:
            print("[INVALID] %s" % number)
        quit()

    print("[ERROR] \" %s \" is not a valid sequence." % number)
    quit()


def main():
    if len(sys.argv) < 2:
        print("Usage: python TLF.py <sequence>")
        quit()

    luhn_check(sys.argv[1])

if __name__ == '__main__':
    main()

但它运作不正常:

[INVALID] 4532015112830366
[INVALID] 6011514433546201
[INVALID] 6771549495586802

依旧......

但代码的逻辑对我来说似乎没问题。我遵循了这个工作流程:

Luhn公式:

  1. 从数字中删除最后一位数字。最后一位是我们要检查的数字 反转数字

  2. 将奇数位(1,3,5等)中的数字乘以2并将9减去任何高于9的结果

  3. 将所有数字加在一起

  4. 校验位(卡片的最后一个数字)是您需要添加的数量才能获得10的倍数(模10)

9 个答案:

答案 0 :(得分:6)

我认为算法不正确。

第二步您需要对产品的数字求和而不是减去9。参考:Wikipedia

在维基百科中你有这个例子:

def luhn_checksum(card_number):
    def digits_of(n):
        return [int(d) for d in str(n)]
    digits = digits_of(card_number)
    odd_digits = digits[-1::-2]
    even_digits = digits[-2::-2]
    checksum = 0
    checksum += sum(odd_digits)
    for d in even_digits:
        checksum += sum(digits_of(d*2))
    return checksum % 10

def is_luhn_valid(card_number):
    return luhn_checksum(card_number) == 0


result = is_luhn_valid(4532015112830366)
print 'Correct:' + str(result)
result = is_luhn_valid(6011514433546201)
print 'Correct:' + str(result)
result = is_luhn_valid(6771549495586802)
print 'Correct:' + str(result)

结果:

>>>Correct:True
>>>Correct:True
>>>Correct:True

答案 1 :(得分:2)

您的代码中存在一些错误:

result = divmod(sum_of_digits, 10)

返回一个元组,你只需要模数,即使用

result = sum_of_digits % 10

其次,为了检查有效性,您不会省略最后一位数(即校验和),而是将其包含在计算中。使用

reverse_sequence = list(int(d) for d in str(int(number[::-1]))) 

检查结果为零:

if not result:
    print("[VALID] %s" % number)

或者如果你坚持保持这种不需要的复杂性,检查校验和模10的最后一位是:保持

reverse_sequence = list(int(d) for d in str(int(number[-2::-1])))

但请使用

if (result + last_digit) % 10 == 0:
    print("[VALID] %s" % number)

对于更简单和更短的代码,我可以为您提供my older answer的参考。

答案 2 :(得分:1)

请参阅此Python recipe

def cardLuhnChecksumIsValid(card_number):
    """ checks to make sure that the card passes a luhn mod-10 checksum """

    sum = 0
    num_digits = len(card_number)
    oddeven = num_digits & 1

    for count in range(0, num_digits):
        digit = int(card_number[count])

        if not (( count & 1 ) ^ oddeven ):
            digit = digit * 2
        if digit > 9:
            digit = digit - 9

        sum = sum + digit

    return ( (sum % 10) == 0 )

答案 3 :(得分:1)

以下内容可能会帮助一些人在python中使用Luhn算法。

num = list(input("Please enter the number to test (no space, no symbols, only \
numbers): "))

num = list(map(int, num))[::-1] #let's transform string into int and reverse it

for index in range(1,len(num),2):
    if num[index]<5:
        num[index] = num[index] *2
    else: #doubling number>=5 will give a 2 digit number
        num[index] = ((num[index]*2)//10) + ((num[index]*2)%10)

checksum=sum(num)

print("checksum= {}".format(checksum))

if checksum%10 !=0:
    print('the number is not valid')
else:
    print('the number is valid!')

答案 4 :(得分:1)

我会保持简单易读,如下所示:

def luhn(value):
    digits = map(int, str(value))
    oddSum = sum(digits[-1::-2])
    evnSum = sum([sum(divmod(2 * d, 10)) for d in digits[-2::-2]])
    return (oddSum + evnSum) % 10 == 0

但是有很多方法可以做同样的事情。显然,您必须以不同方式查看实际输出,这只是总计总数以确定该值是否有效。

最佳!

答案 5 :(得分:1)

这是我发现的Luhn测试最简洁的python公式:

def luhn(n):
    r = [int(ch) for ch in str(n)][::-1]
    return (sum(r[0::2]) + sum(sum(divmod(d*2,10)) for d in r[1::2])) % 10 == 0

https://www.rosettacode.org/wiki/Luhn_test_of_credit_card_numbers中提供了上述函数和其他Luhn实现(使用不同的编程语言)。

答案 6 :(得分:0)

check_numbers = ['49927398716', '4847352989263095', '79927398713', '5543352315777720']

def Luhn_Check(number):
    """
        this function checks a number by using the Luhn algorithm.

        Notes (aka - How to Luhn) :
            Luhn algorithm works in a 1 2 1 2 ... order.
            Therefore, in computer speak, 1 0 1 0 ... order

            step 1:
                -> reverse the # so we are not working from right to left (unless you want too)
                -> double every second number

            step 2:
                -> if the doubled number is greater then 9, add the individual digits of the number to get a single digit number
                    (eg. 12, 1 + 2 = 3)

            step 3:
               -> sum all the digits, if the total modulo is equal to 0 (or ends in zero) then the number is valid...
    """

    reverse_numbers = [int(x) for x in number[::-1]] # convert args to int, reverse the numbers, put into a list
    dbl_digits = list() # create empty list
    digits = list(enumerate(reverse_numbers, start=1)) # enumerate numbers starting with an index of 1

    for index, digit in digits:
        if index % 2 == 0:  # double every second (other) digit.

            doub_digit = digit * 2
            dbl_digits.append(doub_digit - 9) if doub_digit > 9 else dbl_digits.append(doub_digit)

        else:
            # if not '0' append to list (this would be the 1 in Luhn algo sequence (1 2 1 2 ...)
            dbl_digits.append(digit)

    return sum(dbl_digits) % 10


if (__name__ == "__main__"):
    print("Valid Numbers: %s " % [x for x in check_numbers if Luhn_Check(x) == 0])

答案 7 :(得分:0)

您的逻辑的主要问题是您正在应用一些仅适用于奇数位至偶数位的计算。

我为您提供一些建议:

  1. 接收一个字符串,而不是接收一个int作为输入,因此您可以处理带有尾随零的数字。
  2. 在处理列表时尝试应用list comprehensions或使用map
  3. 您可以定义一些辅助函数来提高代码的可重复性。

考虑我的建议,这就是解决该问题的方法。请注意,我正在使用Python的type annotations。我还将在注释中重点介绍Luhn算法的步骤。

def compute_sum_of_remainig_digits(remaining_digits: List[int]) -> int:
    # use a list comprehension to extract digits in even positions
    even_positions = remaining_digits[-2::-2]
    # use a list comprehension to extract digits in odd positions
    odd_positions = remaining_digits[-1::-2]
    # 2. Multiply the digits in odd positions (1, 3, 5, etc.) by 2
    # and subtract 9 to all any result higher than 9
    computed_odd = [2 * d - 9 if 2 * d > 9 else 2 * d for d in odd_positions]
    # 3. Add all the numbers together
    return sum(even_positions + computed_odd)


def compute_checksum(number: str) -> int:
    # the following line convert a string of digits into list of digits using map
    # this way you do not need to explicitly declare a for loop
    digits = list(map(int, number))
    # 1. Drop the last digit from the number...
    last_digit = digits[-1]
    total = compute_sum_of_remainig_digits(digits[:-1 ])

    # 4. The check digit (the last number of the card) is
    # the amount that you would need to add...
    return (total + last_digit)


def check_valid_number(number: str) -> bool:
    # 4. ...to get a multiple of 10 (Modulo 10)
    return (compute_checksum(number) * 9) % 10 == 0

现在让我们验证前面的代码:

>>> valid_number = "4929355681587216"
>>> invalid_number = "5183814022326227"
>>> check_valid_number(valid_number)
True
>>> check_valid_number(invalid_number)
False

我希望这个答案可以帮助您或帮助其他有麻烦的人了解如何计算Luhn's Checker。

答案 8 :(得分:0)

一行代码实现Luhn公式(如果没有换行)

输入参数必须是字符串

def is_card_valid(card_number):
    return (sum(
        (element + (index % 2 == 0) * (element - 9 * (element > 4))
         for index, element in enumerate(map(int, card_number[:-1])))
    ) + int(card_number[-1])) % 10 == 0

带地图功能

def is_card_valid(card_number):
    return (sum(
        map(lambda n: n[1] + (n[0] % 2 == 0) * (n[1] - 9 * (n[1] > 4)),
            enumerate(map(int, card_number[:-1])))
    ) + int(card_number[-1])) % 10 == 0