Генерация контрольных сумм Luhn - PullRequest
11 голосов
/ 14 сентября 2009

Существует множество реализаций для проверки контрольных сумм 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;
}

Ответы [ 5 ]

9 голосов
/ 14 сентября 2009

Редактировать : Извините, теперь я понимаю, что у вас уже был почти весь мой ответ, вы просто неправильно определили, какой коэффициент использовать для какой цифры.

Весь мой ответ теперь можно суммировать одним предложением:

У вас есть обратный коэффициент, вы умножаете неправильные цифры на 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 - действительным.

Теперь ваш вопрос намекает на то, что вы хотите знать, как создать контрольную сумму, ну, это просто, сделайте следующее:

  1. Прибавьте дополнительный ноль, чтобы ваш номер перешел от ксиоксиокси к ксиокси 0
  2. Рассчитать сумму контрольной суммы Луна для нового номера
  3. Возьмите сумму, модуль 10, чтобы вы получили одну цифру от 0 до 10
  4. Если цифра 0, то поздравляю, ваша контрольная сумма была нулевой
  5. В противном случае рассчитайте 10-значный, чтобы получить то, что вам нужно для последней цифры, вместо этого нуля

Пример: номер 12345

  1. Тэкс на ноль: 123450
  2. Рассчитать контрольную сумму 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
    
  3. Возьмите сумму (15), модуль 10, который дает вам 5

  4. Цифра (5), не ноль
  5. Рассчитайте 10-5, что дает 5, последняя цифра должна быть 5.

Таким образом, результат равен 123455.

3 голосов
/ 04 декабря 2010

ваш php глючит, он ведет в бесконечный цикл. Это рабочая версия, которую я использую, модифицированная из вашего кода

функция Луна ($ число) {

$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) для подтверждения.

2 голосов
/ 13 сентября 2012

BAD

Я буквально не могу поверить, как много грязных реализаций существует.

IDAutomation имеет .NET-сборку с функцией MOD10 () для создания, но, похоже, она не работает. В Reflector код слишком длинный для того, что он должен делать в любом случае.


BAD

Этот беспорядок на странице , которая на самом деле в настоящее время связана с Википедией (!) Для Javascript, имеет несколько реализаций проверки, которые даже не возвращают одно и то же значение при вызове каждой из них.


GOOD

Страница , на которую ссылается лунная страница Википедии , имеет кодировщик 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());​

GOOD

Эта очень полезная страница EE4253 проверяет контрольную цифру, а также показывает полный расчет и объяснение.


GOOD

Мне нужен был код 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;
        }

GOOD

Этот проверочный код в C # , кажется, работает, хотя и немного громоздко. Я только использовал это, чтобы проверить, что выше было правильно.

0 голосов
/ 29 августа 2017

Это функция, которая может вам помочь, она короткая и работает просто отлично.

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;
}
0 голосов
/ 06 февраля 2013

Теперь есть репозиторий github, основанный на оригинальном вопросе / ответе. Смотри

https://github.com/xi-project/xi-algorithm

Это также доступно в Packagist

...