【问题标题】:Implementation of Luhn Formula卢恩公式的实现
【发布时间】:2014-01-31 11:59:07
【问题描述】:

我试图在 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

等等……

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

卢恩公式

  1. 删除数字的最后一位。最后一位是我们要检查的 颠倒数字

  2. 将奇数位(1、3、5 等)的数字乘以 2,然后将所有大于 9 的结果减去 9

  3. 将所有数字相加

  4. 校验位(卡的最后一个数字)是您需要相加以获得 10 的倍数(模 10)的金额

【问题讨论】:

  • 为了检查奇数位置的数字,您应该使用:range(1, len(reverse_sequence), 2) 而不是 range(0, len(reverse_sequence), 2)
  • @JChris,问题在于您将步骤 2 的操作应用于所有数字。我添加了一个答案,详细解释了如何计算这个检查器。

标签: python math checksum luhn


【解决方案1】:

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

输入参数必须是字符串

def is_card_valid(card_number: str) -> bool:
    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: str) -> bool:
    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

【讨论】:

    【解决方案2】:

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

    我有一些建议给你:

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

    考虑到我的建议,这就是我解决这个问题的方法。请注意,我使用的是 Python 的type annotations。我还将用 cmets 重点介绍 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。

    【讨论】:

      【解决方案3】:

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

      def luhn(value):
          digits = list(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
      

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

      最好的!

      【讨论】:

        【解决方案4】:
        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])
        

        【讨论】:

          【解决方案5】:

          这是我找到的 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】:

            以下内容可能会帮助一些人在 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!')
            

            【讨论】:

              【解决方案7】:

              您的代码中有一些错误:

              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

              【讨论】:

                【解决方案8】:

                我认为算法不正确。

                第二步你需要将产品的数字相加而不是减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
                

                【讨论】:

                • 我的代码检查号码是否有效。不生成验证号!在维基百科中是相反的。
                • 是的,我知道。但是在维基百科中有两个例子:一个用于生成,另一个用于测试。我在此答案中发布的代码是检查验证号的代码。
                【解决方案9】:

                看到这个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 )
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-06-21
                  • 2015-05-26
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多