Реализация формулы Луна
Я пытался реализовать формулу Luhn в Python, вот мой код:
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, 3, 5 и т. Д.) На 2 и вычтите 9 для всех результатов, превышающих 9
Добавить все числа вместе
Контрольная цифра (последний номер карты) - это сумма, которую нужно добавить, чтобы получить кратное 10 (по модулю 10).
10 ответов
Я думаю, что алгоритм не является правильным.
На втором шаге нужно сложить цифры продуктов вместо вычитки 9. Ссылка: Википедия.
В Википедии у вас есть этот пример:
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
Это самая краткая формула 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
Вышеупомянутая функция и другие реализации Luhn (на разных языках программирования) доступны по https://www.rosettacode.org/wiki/Luhn_test_of_credit_card_numbers.
Я хотел бы сделать это простым и легким для чтения, что-то вроде этого:
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
Но есть множество способов сделать то же самое. Очевидно, что вам придется сделать это по-другому, чтобы увидеть фактический результат, это просто суммирует итоговое значение, чтобы определить, является ли значение допустимым.
Лучший!
Следующее может помочь некоторым людям начать с алгоритма Луна в python.
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!')
В вашем коде есть несколько ошибок:
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: keep
reverse_sequence = list(int(d) for d in str(int(number[-2::-1])))
но использовать
if (result + last_digit) % 10 == 0:
print("[VALID] %s" % number)
Для более простого и короткого кода я могу дать вам ссылку на мой старый ответ.
Увидеть этот рецепт Python
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 )
Реализации формулы Луна в одной строке кода (без разрывов строк)
входной аргумент должен быть строкой
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
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])
Учитывая строку цифр, мы можем определить значение каждой цифры путем преобразования в целое число. Это довольно очевидное утверждение. Однако при преобразовании в int базовый код должен выполнить многочисленные проверки, прежде чем выдать результат.
Если мы можем гарантировать , что мы всегда обрабатываем только символы, представляющие числа от 0 до 9, то поиск по словарю окажется быстрее.
Это приводит к чрезвычайно эффективной реализации алгоритма Луна, не требующей какого-либо целочисленного преобразования.
Используя timeit, я определил, что могу вызвать функцию isvalid() с 16-значной строкой миллион раз за ~1,1 с.
from itertools import cycle
LMAP = {
'0': 0,
'1': 2,
'2': 4,
'3': 6,
'4': 8,
'5': 1,
'6': 3,
'7': 5,
'8': 7,
'9': 9
}
NMAP = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9
}
def isvalid(ccn: str) -> bool:
c = cycle((NMAP, LMAP))
_sum = 0
for d in ccn[::-1]:
_sum += next(c)[d]
return _sum % 10 == 0
def check_digit(ccn: str) -> str:
c = cycle((LMAP, NMAP))
_sum = 0
for d in ccn[::-1]:
_sum += next(c)[d]
return 10 - r if (r := _sum % 10) != 0 else 0
for ccn in ['4532015112830341', '4532015112830342']:
print(isvalid(ccn))
print(check_digit('453201511283034'))
Выход:
True
False
1
Основная проблема с вашей логикой заключается в том, что вы применяете некоторые вычисления, которые относятся только к нечетным цифрам и к четным цифрам.
У меня есть несколько предложений для вас:
- Вместо получения int в качестве входных данных получите строку, чтобы можно было обрабатывать числа с конечными нулями.
- Попробуйте применить понимание списка или используйте
map
при обработке списка - Вы можете определить несколько вспомогательных функций, чтобы улучшить редактируемость кода.
Учитывая то, что я предложил, вот как я решил бы эту проблему. Обратите внимание, что я использую аннотации типов Python . Я также выделю шаги алгоритма Луна с комментариями.
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
Я надеюсь, что этот ответ поможет вам или поможет другим людям, попавшим в затруднительное положение, понять, как вычислить чекер Луна.