Алгоритм Luhn mod N - Luhn mod N algorithm
Алгоритм Лун мод N является расширением к алгоритму Luhn (также известный как моды 10 алгоритма) , что позволяет ему работать с последовательностями значений в любой базе . Это может быть полезно, когда контрольная цифра требуется для подтверждения строки идентификации, состоящей из букв, комбинации букв и цифр или любого произвольного набора из N символов.
Неформальное объяснение
Алгоритм Luhn mod N генерирует контрольную цифру (точнее, контрольный символ) в том же диапазоне допустимых символов, что и входная строка. Например, если алгоритм применяется к строке строчных букв (от a до z ), контрольный символ также будет строчной буквой. Помимо этого различия, он очень похож на исходный алгоритм.
Основная идея расширения состоит в том, что полный набор допустимых входных символов отображается в список кодовых точек (т. Е. Последовательные целые числа, начинающиеся с нуля). Алгоритм обрабатывает входную строку, преобразовывая каждый символ в связанную с ним кодовую точку, а затем выполняет вычисления по модулю N (где N - количество допустимых входных символов). Наконец, результирующая кодовая точка проверки отображается обратно для получения соответствующего проверочного символа.
Отображение символов на кодовые точки
Первоначально необходимо создать соответствие между допустимыми входными символами и кодовыми точками. Например, предположим , что допустимые символы - это строчные буквы от a до f . Следовательно, подходящим отображением будет:
| символ | а | б | c | d | е | ж |
|---|---|---|---|---|---|---|
| Кодовая точка | 0 | 1 | 2 | 3 | 4 | 5 |
Обратите внимание, что порядок символов совершенно не имеет значения. Это другое сопоставление также будет приемлемым (хотя, возможно, более громоздким для реализации):
| символ | c | е | а | ж | б | d |
|---|---|---|---|---|---|---|
| Кодовая точка | 0 | 1 | 2 | 3 | 4 | 5 |
Также возможно смешивать буквы и цифры (и, возможно, даже другие символы). Например, это сопоставление подходит для шестнадцатеричных цифр в нижнем регистре:
| символ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | а | б | c | d | е | ж |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Кодовая точка | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Алгоритм на C #
Предполагая, что определены следующие функции:
int CodePointFromCharacter(char character) {...}
char CharacterFromCodePoint(int codePoint) {...}
int NumberOfValidInputCharacters() {...}
Функция для генерации контрольного символа:
char GenerateCheckCharacter(string input)
{
int factor = 2;
int sum = 0;
int n = NumberOfValidInputCharacters();
// Starting from the right and working leftwards is easier since
// the initial "factor" will always be "2".
for (int i = input.Length - 1; i >= 0; i--)
{
int codePoint = CodePointFromCharacter(input[i]);
int addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = IntegerValue(addend / n) + (addend % n);
sum += addend;
}
// Calculate the number that must be added to the "sum"
// to make it divisible by "n".
int remainder = sum % n;
int checkCodePoint = (n - remainder) % n;
return CharacterFromCodePoint(checkCodePoint);
}
И функция для проверки строки (с контрольным символом в качестве последнего символа):
bool ValidateCheckCharacter(string input)
{
int factor = 1;
int sum = 0;
int n = NumberOfValidInputCharacters();
// Starting from the right, work leftwards
// Now, the initial "factor" will always be "1"
// since the last character is the check character.
for (int i = input.Length - 1; i >= 0; i--)
{
int codePoint = CodePointFromCharacter(input[i]);
int addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = IntegerValue(addend / n) + (addend % n);
sum += addend;
}
int remainder = sum % n;
return (remainder == 0);
}
Алгоритм на Java
Предполагая, что определены следующие функции:
int codePointFromCharacter(char character) {...}
char characterFromCodePoint(int codePoint) {...}
int numberOfValidInputCharacters() {...}
Функция для генерации контрольного символа:
char generateCheckCharacter(String input) {
int factor = 2;
int sum = 0;
int n = numberOfValidInputCharacters();
// Starting from the right and working leftwards is easier since
// the initial "factor" will always be "2".
for (int i = input.length() - 1; i >= 0; i--) {
int codePoint = codePointFromCharacter(input.charAt(i));
int addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = (addend / n) + (addend % n);
sum += addend;
}
// Calculate the number that must be added to the "sum"
// to make it divisible by "n".
int remainder = sum % n;
int checkCodePoint = (n - remainder) % n;
return characterFromCodePoint(checkCodePoint);
}
И функция для проверки строки (с контрольным символом в качестве последнего символа):
boolean validateCheckCharacter(String input) {
int factor = 1;
int sum = 0;
int n = numberOfValidInputCharacters();
// Starting from the right, work leftwards
// Now, the initial "factor" will always be "1"
// since the last character is the check character.
for (int i = input.length() - 1; i >= 0; i--) {
int codePoint = codePointFromCharacter(input.charAt(i));
int addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = (addend / n) + (addend % n);
sum += addend;
}
int remainder = sum % n;
return (remainder == 0);
}
Алгоритм в JavaScript
Предполагая, что определены следующие функции:
const codePoints = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
//This can be any string of permitted characters
function numberOfValidInputCharacters() {
return codePoints.length;
}
function codePointFromCharacter(character) {
return codePoints.indexOf(character);
}
function characterFromCodePoint(codePoint) {
return codePoints.charAt(codePoint);
}
Функция для генерации контрольного символа:
function generateCheckCharacter(input) {
let factor = 2;
let sum = 0;
let n = numberOfValidInputCharacters();
// Starting from the right and working leftwards is easier since
// the initial "factor" will always be "2".
for (let i = input.length - 1; i >= 0; i--) {
let codePoint = codePointFromCharacter(input.charAt(i));
let addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = (Math.floor(addend / n)) + (addend % n);
sum += addend;
}
// Calculate the number that must be added to the "sum"
// to make it divisible by "n".
let remainder = sum % n;
let checkCodePoint = (n - remainder) % n;
return characterFromCodePoint(checkCodePoint);
}
И функция для проверки строки (с контрольным символом в качестве последнего символа):
function validateCheckCharacter(input) {
let factor = 1;
let sum = 0;
let n = numberOfValidInputCharacters();
// Starting from the right, work leftwards
// Now, the initial "factor" will always be "1"
// since the last character is the check character.
for (let i = input.length - 1; i >= 0; i--) {
let codePoint = codePointFromCharacter(input.charAt(i));
let addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = (Math.floor(addend / n)) + (addend % n);
sum += addend;
}
let remainder = sum % n;
return (remainder == 0);
}
пример
Поколение
Рассмотрим приведенный выше набор допустимых входных символов и пример входной строки abcdef . Чтобы сгенерировать контрольный символ, начните с последнего символа в строке и двигайтесь влево, удваивая все остальные кодовые точки. Затем следует суммировать "цифры" кодовых точек, записанные в базе 6 (поскольку имеется 6 допустимых входных символов):
| символ | а | б | c | d | е | ж |
|---|---|---|---|---|---|---|
| Кодовая точка | 0 | 1 | 2 | 3 | 4 | 5 |
| Двойной | 2 | 6 (основание 10) 10 (основание 6) |
10 (основание 10) 14 (основание 6) |
|||
| Уменьшить | 0 | 2 | 2 | 1 + 0 | 4 | 1 + 4 |
| Сумма цифр | 0 | 2 | 2 | 1 | 4 | 5 |
Общая сумма цифр 14 (0 + 2 + 2 + 1 + 4 + 5). Чтобы получить следующее кратное 6 (в данном случае 18 ) число, нужно сложить 4 . Это результирующий код проверки. Соответствующий контрольный символ - e .
Проверка
Результирующую строку abcdefe можно затем проверить, используя аналогичную процедуру:
| символ | а | б | c | d | е | ж | е |
|---|---|---|---|---|---|---|---|
| Кодовая точка | 0 | 1 | 2 | 3 | 4 | 5 | 4 |
| Двойной | 2 | 6 (основание 10) 10 (основание 6) |
10 (основание 10) 14 (основание 6) |
||||
| Уменьшить | 0 | 2 | 2 | 1 + 0 | 4 | 1 + 4 | 4 |
| Сумма цифр | 0 | 2 | 2 | 1 | 4 | 5 | 4 |
Общая сумма цифр 18 . Так как он делится на 6, проверочный символ действителен .
Реализация
Сопоставление символов с кодовыми точками и обратно может быть реализовано несколькими способами. Самый простой подход (похожий на исходный алгоритм Луна) - использовать арифметику кода ASCII. Например, для входного набора от 0 до 9 кодовая точка может быть вычислена путем вычитания кода ASCII для «0» из кода ASCII желаемого символа. Обратная операция обеспечит обратное отображение. С дополнительными диапазонами символов можно обращаться с помощью условных операторов.
Непоследовательные наборы можно сопоставить в обоих направлениях с помощью жестко запрограммированного оператора switch / case . Более гибкий подход - использовать что-то похожее на ассоциативный массив . Для этого требуется пара массивов для обеспечения двустороннего сопоставления.
Дополнительная возможность заключается в использовании массива символов, где индексы массива являются кодовыми точками, связанными с каждым символом. Преобразование символа в кодовую точку затем может выполняться с помощью линейного или двоичного поиска. В этом случае обратное отображение - это просто поиск в массиве.
Слабость
Это расширение имеет ту же слабость, что и исходный алгоритм, а именно, оно не может обнаружить транспонирование последовательности <first-valid-character> <last-valid-character> в <last-valid-character> <first-valid-character> (или наоборот). Это эквивалентно преобразованию 09 в 90 (при условии набора допустимых входных символов от 0 до 9 в порядке). Положительным моментом является то, что чем больше набор допустимых входных символов, тем меньше влияние слабости.