2009-09-13 21 views
10

Esistono molte implementazioni per convalidare i checksum di Luhn ma pochissimi per generarli. Mi sono imbattuto nello this one ma nei miei test ha rivelato di essere bacato e non capisco la logica dietro la variabile delta.Generazione di checksum Luhn

Ho creato questa funzione che presumibilmente dovrebbe generare checksum Luhn, ma per qualche motivo non ho ancora capito che i checksum generati non sono validi la metà delle volte.

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

Alcuni esempi:

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

che sto convalidando i checksum generati against this page, che cosa sto facendo male qui?


Per riferimento futuro, ecco la funzione di lavoro.

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

ho lasciato cadere la variabile $ parità dal momento che non abbiamo bisogno per questo scopo, e di verificare:

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

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

    return false; 
} 
+2

proposito, verificando un checksum è identico a generare e controllare se è a zero - così tutte quelle 'verificare' routine possono anche essere utilizzati per generare. –

+0

@Nick: Sì, ma questo implica il controllo di 10 cifre diverse, preferisco il contrario (verifica usando la funzione di generazione). –

+0

Cosa? No, basta solo "verificare" il numero con 0 aggiunto, quindi sostituire l'ultima cifra con (9 risultati). –

risposta

8

Edit: Scusa, mi rendo conto ora che si aveva quasi la mia già tutta la risposta, hai appena determinato in modo errato quale fattore utilizzare per quale cifra.

La mia risposta intera ora si può riassumere con questa sola frase:

Avete il fattore invertito, si sta moltiplicando le cifre sbagliate da 2 a seconda della lunghezza del numero.


Dai uno sguardo allo Wikipedia article on the Luhn algorithm.

Il motivo per cui il checksum non è valido metà del tempo è quello con i vostri assegni, metà del tempo in cui il vostro numero ha un numero dispari di cifre, e quindi raddoppierete la cifra sbagliata.

Per 37283, quando il conteggio da destra, si ottiene questa sequenza di numeri:

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 

L'algoritmo richiede di sommare le singole cifre dal numero originale, e le singole cifre del prodotto di quelli "ogni due cifre da destra".

Così da destra, si somma 3 + (1 + 6) + 2 + (1 + 4) + 3, che vi dà 20.

Se il numero si finisce con le estremità con uno zero, quale 20 fa, il numero è valido.

Ora, i vostri suggerimenti interrogativi nella voi che vogliono sapere come generare il checksum, beh, questo è facile, effettuare le seguenti operazioni:

  1. Tack su uno zero in più, in modo che il numero va da xyxyxyxy a xyxyxyxy0
  2. calcolare la somma Luhn checksum per il nuovo numero
  3. Fate il conto, modulo 10, in modo da ottenere una sola cifra 0-10
  4. Se la cifra è pari a 0, allora congratulazioni, il vostro codice di controllo è stato uno zero
  5. In caso contrario, calcolare 10 cifre, per ottenere ciò che vi serve per l'ultima cifra, invece di quella a zero

Esempio: Il numero è 12345

  1. Tack su uno zero: 123450
  2. Calcolare il Luhn checksum per 123.450, che si traduce in

    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. Fate il conto (15), modulo 10, che vi dà 5

  4. Digit (5), non è zero
  5. Calcolare 10-5, che vi dà 5, l'ultima cifra dovrebbe essere 5.

Così il risultato è 123455.

+0

Grazie, mi sono reso conto che la mia cifra più a destra in questo caso è sempre la mia ultima cifra (dato che sto generando la cifra di checksum). –

2

PHP è buggy, porta in un loop infinito. Questa è la versione di lavoro che sto usando, modificato dal codice

funzione Luhn ($ numero) {

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

}

Creare un php ed eseguire nella vostra localhost Luhn (xxxxxxxx) per confermare.

+0

Non sembra bacato da me: http: //www.ideone.com/y6bkh ... –

2

BAD

io letteralmente non riesco a credere quanto molte implementazioni scadente ci sono là fuori.

IDAutomation ha un .NET assembly with a MOD10() function da creare ma non sembra funzionare. In Reflector il codice è troppo lungo per quello che dovrebbe fare comunque.


BAD

This mess of a page che in realtà è attualmente collegato a da Wikipedia (!) Per Javascript deve diverse implementazioni di verifica che non hanno nemmeno restituiscono lo stesso valore quando chiamo ognuno.


BUONA

Il page linked to from Wikipedia's Luhn page ha un encoder Javascript, che sembra funzionare:

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

BUONA

Questo very useful EE4253 p età verifica la cifra di controllo e mostra anche il calcolo completo e la spiegazione.


BUONA

avevo bisogno di codice C# e finito per usare questo code project code:

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

BUONA

Questo validation code in C# sembra w ork, se un po 'ingombrante. L'ho appena usato per verificare quanto sopra fosse corretto.

+0

In realtà ho finito per spendere * modo * troppo a lungo cercando di trovare codice funzionante e avrei dovuto semplicemente scriverlo da solo. Ricorda che un algoritmo di validazione e un algoritmo di verifica sono fondamentalmente gli stessi: con la convalida si crea semplicemente il checksum per le cifre n-1 nella stringa e si confronta con l'ultima cifra –

0

anche questa è una funzione che potrebbe aiutare, è breve e funziona bene.

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