Генерация контрольных сумм Luhn
Существует множество реализаций для проверки контрольных сумм Luhn, но очень мало для их генерации. Я сталкивался с этим, однако в моих тестах он показал, что он содержит ошибки, и я не понимаю логику дельта-переменной.
Я сделал эту функцию, которая предположительно должна генерировать контрольные суммы Luhn, но по какой-то причине я еще не понял, что созданные контрольные суммы недопустимы в половине случаев.
function Luhn($number, $iterations = 1)
{
while ($iterations-- >= 1)
{
$stack = 0;
$parity = strlen($number) % 2;
$number = str_split($number, 1);
foreach ($number as $key => $value)
{
if ($key % 2 == $parity)
{
$value *= 2;
if ($value > 9)
{
$value -= 9;
}
}
$stack += $value;
}
$stack = 10 - $stack % 10;
if ($stack == 10)
{
$stack = 0;
}
$number[] = $stack;
}
return implode('', $number);
}
Некоторые примеры:
Luhn(3); // 37, invalid
Luhn(37); // 372, valid
Luhn(372); // 3728, invalid
Luhn(3728); // 37283, valid
Luhn(37283); // 372837, invalid
Luhn(372837); // 3728375, valid
Я проверяю сгенерированные контрольные суммы на этой странице, что я здесь не так делаю?
Для дальнейшего использования, вот рабочая функция.
function Luhn($number, $iterations = 1)
{
while ($iterations-- >= 1)
{
$stack = 0;
$number = str_split(strrev($number), 1);
foreach ($number as $key => $value)
{
if ($key % 2 == 0)
{
$value = array_sum(str_split($value * 2, 1));
}
$stack += $value;
}
$stack %= 10;
if ($stack != 0)
{
$stack -= 10;
}
$number = implode('', array_reverse($number)) . abs($stack);
}
return $number;
}
Я удалил переменную $ parity, так как она нам не нужна, и для проверки:
function Luhn_Verify($number, $iterations = 1)
{
$result = substr($number, 0, - $iterations);
if (Luhn($result, $iterations) == $number)
{
return $result;
}
return false;
}
8 ответов
Изменить: Извините, теперь я понимаю, что у вас уже был почти весь мой ответ, вы просто неправильно определили, какой фактор использовать для какой цифры.
Весь мой ответ теперь можно подытожить одним предложением:
Вы поменяли множитель, вы умножаете неправильные цифры на 2 в зависимости от длины номера.
Взгляните на статью в Википедии об алгоритме Луна.
Причина, по которой ваша контрольная сумма недопустима в половине случаев, состоит в том, что в случае ваших чеков половина вашего номера имеет нечетное число цифр, а затем вы удваиваете неправильную цифру.
Для 37283 при подсчете справа вы получите следующую последовательность чисел:
3 * 1 = 3 3
8 * 2 = 16 --> 1 + 6 = 7
2 * 1 = 2 2
7 * 2 = 14 --> 1 + 4 = 5
+ 3 * 1 = 3 3
= 20
Алгоритм требует суммирования отдельных цифр от исходного числа и отдельных цифр произведения этих "каждых двух цифр справа".
Итак, справа вы получаете сумму 3 + (1 + 6) + 2 + (1 + 4) + 3, что дает вам 20.
Если число, которое вы заканчиваете, заканчивается нулем, а 20 - действительным.
Теперь ваш вопрос намекает на то, что вы хотите знать, как сгенерировать контрольную сумму, ну, это просто, сделайте следующее:
- Прибавьте лишний ноль, чтобы ваш номер перешел от ксиоксикси к ксиоксиокси0
- Рассчитать сумму контрольной суммы Луна для нового числа
- Возьмите сумму, модуль 10, чтобы вы получили одну цифру от 0 до 10
- Если цифра 0, то поздравляю, ваша контрольная сумма была нулевой
- В противном случае, рассчитайте 10-значный, чтобы получить то, что вам нужно для последней цифры, вместо этого нуля
Пример: номер 12345
- Тэкс на ноль: 123450
Рассчитайте контрольную сумму Луна для 123450, в результате чего
0 5 4 3 2 1 1 2 1 2 1 2 <-- factor 0 10 4 6 2 2 <-- product 0 1 0 4 6 2 2 <-- sum these to: 0+1+0+4+6+2+2=15
Возьмите сумму (15), модуль 10, который дает вам 5
- Цифра (5), не ноль
- Вычислите 10-5, что дает 5, последняя цифра должна быть 5.
Таким образом, результат 123455.
ПЛОХОЙ
Я в буквальном смысле не могу поверить, как много грязных реализаций существует.
IDAutomation имеет сборку.NET с функцией MOD10() для создания, но она просто не работает. В Reflector код слишком длинный для того, что он должен делать в любом случае.
ПЛОХОЙ
Этот беспорядок на странице, на которую в настоящий момент ссылаются из Википедии (!) Для Javascript, имеет несколько реализаций проверки, которые даже не возвращают одно и то же значение, когда я вызываю каждую из них.
ХОРОШО
Страница, на которую ссылается страница Луна в Википедии, имеет кодировщик Javascript, который, кажется, работает:
// Javascript
String.prototype.luhnGet = function()
{
var luhnArr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]], sum = 0;
this.replace(/\D+/g,"").replace(/[\d]/g, function(c, p, o){
sum += luhnArr[ (o.length-p)&1 ][ parseInt(c,10) ]
});
return this + ((10 - sum%10)%10);
};
alert("54511187504546384725".luhnGet());
ХОРОШО
Эта очень полезная страница EE4253 проверяет контрольную цифру, а также показывает полный расчет и объяснение.
ХОРОШО
Мне нужен был код C#, и в итоге я использовал этот код проекта:
// C#
public static int GetMod10Digit(string data)
{
int sum = 0;
bool odd = true;
for (int i = data.Length - 1; i >= 0; i--)
{
if (odd == true)
{
int tSum = Convert.ToInt32(data[i].ToString()) * 2;
if (tSum >= 10)
{
string tData = tSum.ToString();
tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
}
sum += tSum;
}
else
sum += Convert.ToInt32(data[i].ToString());
odd = !odd;
}
int result = (((sum / 10) + 1) * 10) - sum;
return result % 10;
}
ХОРОШО
Этот код проверки в C#, кажется, работает, если немного громоздко. Я просто использовал это, чтобы проверить, что выше было правильно.
Ваш PHP глючит, он ведет в бесконечный цикл. Это рабочая версия, которую я использую, модифицированная из вашего кода
function Luhn ($ number) {
$stack = 0; $number = str_split(strrev($number)); foreach ($number as $key => $value) { if ($key % 2 == 0) { $value = array_sum(str_split($value * 2)); } $stack += $value; } $stack %= 10; if ($stack != 0) { $stack -= 10; $stack = abs($stack); } $number = implode('', array_reverse($number)); $number = $number . strval($stack); return $number;
}
Создайте php и запустите на своем локальном хосте Luhn(xxxxxxxx) для подтверждения.
Это функция, которая может вам помочь, она короткая и работает просто отлично.
function isLuhnValid($number)
{
if (empty($number))
return false;
$_j = 0;
$_base = str_split($number);
$_sum = array_pop($_base);
while (($_actual = array_pop($_base)) !== null) {
if ($_j % 2 == 0) {
$_actual *= 2;
if ($_actual > 9)
$_actual -= 9;
}
$_j++;
$_sum += $_actual;
}
return $_sum % 10 === 0;
}
Поскольку другие ответы, отображаемые или связанные с C#, не работали, я добавил протестированную и более пояснительную версию C#:
/// <summary>
/// Calculates Luhn Check Digit based on
/// https://en.wikipedia.org/wiki/Luhn_algorithm
/// </summary>
/// <param name="digits">The digits EXCLUDING the check digit on the end.
/// The check digit should be compared against the result of this method.
/// </param>
/// <returns>The correct checkDigit</returns>
public static int CalculateLuhnCheckDigit(int[] digits)
{
int sum = 0;
bool isMultiplyByTwo = false;
//Start the summing going right to left
for (int index = digits.Length-1; index >= 0; --index)
{
int digit = digits[index];
//Every other digit should be multipled by two.
if (isMultiplyByTwo)
digit *= 2;
//When the digit becomes 2 digits (due to digit*2),
//we add the two digits together.
if (digit > 9)
digit = digit.ToString()
.Sum(character => (int)char.GetNumericValue(character));
sum += digit;
isMultiplyByTwo = !isMultiplyByTwo;
}
int remainder = sum % 10;
//If theres no remainder, the checkDigit is 0.
int checkDigit = 0;
//Otherwise, the checkDigit is the number that gets to the next 10
if (remainder != 0)
checkDigit = 10 - (sum % 10);
return checkDigit;
}
Пример его использования:
public static bool IsValid(string userValue)
{
//Get the check digit from the end of the value
int checkDigit = (int)char.GetNumericValue(userValue[userValue.Length - 1]);
//Remove the checkDigit for the luhn calculation
userValue = userValue.Substring(0, userValue.Length - 1);
int[] userValueDigits = userValue.Select(ch => (int)char.GetNumericValue(ch))
.ToArray();
int originalLuhnDigit = CalculateLuhnCheckDigit(userValueDigits);
//If the user entered check digit matches the calcuated one,
//the number is valid.
return checkDigit == originalLuhnDigit;
}
Теперь есть репозиторий github, основанный на оригинальном вопросе / ответе. Увидеть
https://github.com/xi-project/xi-algorithm
Это также доступно в Packagist
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
int *LONT, n, TARF;
int SEGVT = 0;
int SEGVT2 = 0;
string TARJETA;
double VA;
cout << "cuantos digitos tiene la tarjeta: " << endl;
cin >> n;
LONT = new int[n];
do {
cout << "ingrese el # de la tarjeta: " << endl;
cin >> TARJETA;
VA = stod(TARJETA);
} while (VA < 0);
for (int POS = 0; POS < TARJETA.size(); POS++) {
LONT[POS] = TARJETA[POS] - '0';
}
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
LONT[i] = TARJETA[i] - '0';
LONT[i] = LONT[i] * 2;
if (LONT[i] >= 10) {
LONT[i] = LONT[i] - 9;
}
SEGVT2 = SEGVT2 + LONT[i];
}
else
{
LONT[i] = TARJETA[i] - '0';
SEGVT = SEGVT + LONT[i];
}
}
TARF = SEGVT + SEGVT2;
if (TARF % 10 == 0) {
cout << SEGVT2 << SEGVT;
cout << "El numero de tarjeta " << TARJETA << "; Es de una tarjeta valida (YA QUE SU MOD10 ES " << TARF << endl;
}
else
{
cout << SEGVT2 << SEGVT;
cout << "El numero de tarjeta" << TARJETA << "; No es de una tarjeta valida (YA QUE SU MOD10 ES " << TARF << endl;
}
delete[] LONT;
}
Проверка четности должна начинаться справа.
Попробуй это:
<?php
function Luhn($digits) {
$sum = 0;
foreach (str_split(strrev($digits)) as $i => $digit) {
$sum += ($i % 2 == 0) ? array_sum(str_split($digit * 2)) : $digit;
}
return $digits . (10 - ($sum % 10)) % 10;
}
Добавить контрольную сумму Луна в $input
$digits = Luhn($input);
Проверьте число с контрольной суммой Луна в нем:
if ($digits == Luhn(substr($digits, 0, -1))) {
// ...
}
Получить номер контрольной суммы:
$luhn_digit = substr(Luhn($digits), -1);