Алгоритм Луна на языке Go
Алгоритм Луна, или проверка mod 10, - это простой алгоритм контрольной суммы. Его используют для предварительной проверки идентификационных номеров: банковских карт, IMEI и других числовых кодов.
Важно понимать ограничение: алгоритм Луна не доказывает, что карта существует, что на ней есть деньги или что пользователь имеет право ей пользоваться. Он только отвечает на вопрос: "похоже ли это число на корректно составленный номер по правилу контрольной суммы?"
Это похоже на проверку орфографии в слове. Если слово написано с ошибкой, проверка может это заметить. Но если слово написано правильно, это еще не значит, что предложение правдивое.
Зачем нужна контрольная сумма
Пользователь легко может ошибиться при вводе длинного номера: пропустить цифру, перепутать две цифры, нажать соседнюю клавишу. Контрольная сумма помогает быстро отсеять часть таких ошибок до отправки запроса в платежный провайдер или другую систему.
Алгоритм Луна хорош для учебных задач, потому что в нем есть:
- обход строки справа налево;
- работа с цифрами;
- условное преобразование;
- сумма;
- проверка остатка от деления.
Правило алгоритма
Для проверки числа:
- Идем по цифрам справа налево.
- Самую правую цифру оставляем как есть.
- Каждую вторую цифру справа умножаем на 2.
- Если после умножения получилось число больше 9, вычитаем 9.
- Складываем все полученные цифры.
- Если сумма делится на 10 без остатка, число проходит проверку.
Почему "вычитаем 9"? Если цифра 8 умножилась на 2, получилось 16. Сумма цифр 1 + 6 = 7. То же самое даст 16 - 9 = 7. Для результатов от 10 до 18 это удобная короткая форма.
Пошаговый пример
Ввод:
4532015112830366
Идем справа налево:
4 5 3 2 0 1 5 1 1 2 8 3 0 3 6 6
Преобразуем каждую вторую цифру справа:
8 5 6 2 0 1 1 1 2 2 7 3 0 3 3 6
Сумма:
8 + 5 + 6 + 2 + 0 + 1 + 1 + 1 + 2 + 2 + 7 + 3 + 0 + 3 + 3 + 6 = 50
50 % 10 == 0, значит номер проходит проверку Луна.
Реализация в Go
func ValidLuhn(input string) bool { if input == "" { return false } sum := 0 double := false for i := len(input) - 1; i >= 0; i-- { ch := input[i] if ch < '0' || ch > '9' { return false } digit := int(ch - '0') if double { digit *= 2 if digit > 9 { digit -= 9 } } sum += digit double = !double } return sum%10 == 0 }
Здесь ch - '0' превращает ASCII-символ цифры в число. Например, '7' - '0' дает 7.
Флаг double говорит, нужно ли удвоить текущую цифру. Мы начинаем справа, поэтому для самой правой цифры double равен false.
Пробелы и дефисы
Пользователь может ввести номер так:
4532 0151 1283 0366
или так:
4532-0151-1283-0366
Можно заранее нормализовать строку:
func normalizeNumber(input string) string { var builder strings.Builder for _, r := range input { if r == ' ' || r == '-' { continue } builder.WriteRune(r) } return builder.String() }
После нормализации передайте результат в ValidLuhn.
Проверка длины
Алгоритм Луна сам по себе не знает, какая длина допустима для конкретного типа номера. Поэтому часто добавляют отдельную проверку:
number := normalizeNumber(input) if len(number) < 12 || len(number) > 19 { return false } return ValidLuhn(number)
Для разных предметных областей правила длины могут отличаться. Не смешивайте их с самим алгоритмом, если хотите оставить функцию универсальной.
Тесты
func TestValidLuhn(t *testing.T) { tests := []struct { name string input string want bool }{ {name: "valid", input: "4532015112830366", want: true}, {name: "invalid checksum", input: "4532015112830367", want: false}, {name: "letters", input: "4532abc", want: false}, {name: "empty", input: "", want: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidLuhn(tt.input) if got != tt.want { t.Fatalf("got %v, want %v", got, tt.want) } }) } }
Обратите внимание на пустую строку: без отдельной проверки сумма была бы 0, и 0 % 10 == 0. Для пользовательского ввода это нежелательно, поэтому функция сразу возвращает false.
Частые ошибки
- Идти слева направо и удваивать неправильные позиции.
- Забыть вычесть 9 после удвоения.
- Не проверять, что символ является цифрой.
- Смешать алгоритм Луна с правилами конкретной платежной системы.
- Считать, что прохождение Луна означает валидную карту.
Чеклист
- Номер нормализуется, если разрешены пробелы или дефисы.
- Все символы проверяются как цифры.
- Обход идет справа налево.
- Каждая вторая цифра справа удваивается.
- При результате больше 9 вычитается 9.
- Сумма проверяется через
sum % 10 == 0. - Длина проверяется отдельным правилом, если это нужно для формы.