Pergunta

Existem muitas implementações para validar checksums Luhn mas muito poucos para gerá-los. Eu me deparei esta no entanto em meus testes ele revelou ser buggy e eu não compreender a lógica por trás da variável delta.

Eu fiz essa função que supostamente deve gerado checksums Luhn mas por alguma razão que eu ainda não entenderam as somas de verificação geradas são metade inválido do tempo.

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);
}

Alguns exemplos:

Luhn(3); // 37, invalid
Luhn(37); // 372, valid
Luhn(372); // 3728, invalid
Luhn(3728); // 37283, valid
Luhn(37283); // 372837, invalid
Luhn(372837); // 3728375, valid

Estou validar as somas de verificação geradas contra esta página , o que estou fazendo de errado aqui?


Para referência futura, aqui é a função de trabalho.

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;
}

Eu deixei cair a variável $ paridade já que não precisa dele para este fim, e para verificar:

function Luhn_Verify($number, $iterations = 1)
{
    $result = substr($number, 0, - $iterations);

    if (Luhn($result, $iterations) == $number)
    {
        return $result;
    }

    return false;
}
Foi útil?

Solução

Editar :. Desculpe, eu percebo agora que você tinha quase a minha resposta inteira, já que você havia determinado apenas incorretamente que fator a utilização para a qual dígito

Toda a minha resposta agora pode ser resumida com esta única frase:

Você tem o fator invertido, você está multiplicando os dígitos errados por 2 dependendo do comprimento do número.


Dê uma olhada no href="http://en.wikipedia.org/wiki/Luhn_algorithm" rel="noreferrer"> artigo .

A razão a sua soma de verificação é metade inválido o tempo é que, com os seus cheques, metade do tempo o seu número tem um número ímpar de dígitos, e então você dobrar o dígito errado.

Para 37283, quando a contagem da direita, você receber essa seqüência de números:

  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

O algoritmo requer que você somar os dígitos individuais do número original, e os dígitos individuais do produto desses "a cada dois dígitos da direita".

Assim, a partir da direita, você soma 3 + (1 + 6) + 2 + (1 + 4) + 3, que lhe dá 20.

Se o número que você acabar com extremidades com um zero, o que faz 20, o número é válido.

Agora, sua pergunta insinua você querendo saber como gerar o checksum, bem, isso é fácil, faça o seguinte:

  1. Tack em um zero a mais, para que o seu número vai de xyxyxyxy para xyxyxyxy0
  2. Calcule a soma luhn soma de verificação para o novo número
  3. Tomai a soma, módulo 10, de modo a obter um único dígito de 0 a 10
  4. Se o dígito é 0, então parabéns, seu dígito verificador era um
  5. de zero
  6. Caso contrário, calcule de 10 dígitos para conseguir o que você precisa para o último dígito, em vez de que o zero

Exemplo: Número é 12345

  1. Tack em um zero:
  2. 123450
  3. Calcular a soma de verificação luhn para 123450, o que resulta em

    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
    
  4. Tome a soma (15), módulo 10, que lhe dá 5

  5. Digit (5), não é zero
  6. Calcular 10-5, o que lhe dá 5, o último dígito deve ser 5.

Assim, o resultado é 123455.

Outras dicas

seu php é buggy, que leva em um loop infinito. Esta é a versão de trabalho que eu estou usando, modificado a partir do seu código

função 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; 

}

Criar um php e correr em sua localhost Luhn (xxxxxxxx) para confirmar.

BAD

Eu literalmente não posso acreditar quantas implementações crummy existem lá fora.

IDAutomation tem uma .NET conjunto com um MOD10 () função para criar, mas ele simplesmente não parece trabalho. Em Refletor o código é um caminho muito longo para o que é suposto estar a fazer qualquer maneira.


BAD

Essa bagunça de uma página que na verdade é actualmente ligada a partir de Wikipedia (!) para Javascript tem várias implementações de verificação que nem sequer retornam o mesmo valor quando eu chamo cada um.


BOA

O href="https://sites.google.com/site/abapexamples/javascript/luhn-validation" rel="nofollow"> página tem um codificador Javascript que parece funcionar:

// 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());​

BOA

Este muito úteis verifica página EE4253 a verificação de dígitos e também mostra o cálculo integral e explicações.


BOA

Eu precisava de código C # e acabou usando este projeto de código código :

// 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;
        }

BOA

Este código de validação em C # parece funcionar, se um pouco pesado. Eu apenas utilizado para verificar o acima foi correta.

Há agora um repo github com base na pergunta / resposta original. Veja

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

Também é disponível em packagist

Esta é uma função que poderia ajudá-lo, é curto e ele funciona muito bem.

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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top