Создание контрольных сумм Луна
Существует множество реализаций для проверки контрольных сумм Луна, но очень мало для их создания. Я встретил этот, однако в моих тестах он обнаружил, что он глючит, и я не понимаю логики дельта-переменной.
Я сделал эту функцию, которая предположительно должна была генерировать контрольные суммы 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;
}
Ответы
Ответ 1
Изменить: Извините, теперь я понимаю, что у вас уже был почти весь мой ответ, вы просто неправильно определили, какой фактор использовать для какой цифры.
Мой весь ответ теперь можно суммировать с этим единственным предложением:
У вас есть коэффициент обратный, вы умножаете неправильные цифры на 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, число действительно.
Теперь ваш вопрос подсказывает, что вы хотите знать, как сгенерировать контрольную сумму, ну, что легко, сделайте следующее:
- Откажитесь от лишнего нуля, поэтому ваш номер идет от xyxyxyxy до xyxyxyxy0
- Рассчитать сумму контрольной суммы luhn для нового номера
- Возьмите сумму, модуль 10, так что вы получите одну цифру от 0 до 10
- Если цифра равна 0, то поздравления, цифра контрольной суммы равна нулю
- В противном случае подсчитайте 10 цифр, чтобы получить то, что вам нужно для последней цифры, вместо нулевого
Пример: номер 12345
- Tack on the zero: 123450
-
Вычислить контрольную сумму luhn для 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.
Ответ 2
ваш php глючит, он ведет в бесконечный цикл.
Это рабочая версия, которую я использую, измененная из вашего кода
функция 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) для подтверждения.
Ответ 3
BAD
Я буквально не могу поверить, сколько там крахмальных реализаций.
IDAutomation имеет . NET-сборку с функцией MOD10() для создания, но она просто не работает. В Reflector код слишком длинный для того, что он должен делать в любом случае.
BAD
Этот беспорядок страницы, который на самом деле в настоящее время связан с Википедией (!) для Javascript, имеет несколько реализаций проверки, которые даже не возвращаются то же самое значение, когда я называю каждый.
ХОРОШО
Страница связанная с страницей Wikipedia Luhn, содержит кодировщик 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#
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;
}
ХОРОШО
Этот код проверки на С#, похоже, работает, если он немного громоздкий. Я просто использовал его, чтобы проверить, что это было правильно.
Ответ 4
Теперь существует репозиторий github, основанный на исходном вопросе/ответе. См.
https://github.com/xi-project/xi-algorithm
Он также доступен в пакете
Ответ 5
Это функция, которая может помочь вам, она короткая, и она работает нормально.
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;
}
Ответ 6
Поскольку другие ответы, отображаемые или связанные с С#, не работали, я добавил проверенную и более понятную версию С#:
/// <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;
}