4
votes

Essayer d'implémenter l'algorithme de Luhn en C

J'essaie d'implémenter l'algorithme de Luhn en langage C pour vérifier la validité de la carte de crédit, pour ceux qui ne le savent pas ... c'est ça:

  • Multipliez tous les autres chiffres par 2, en commençant par l'avant-dernier chiffre du numéro, puis ajoutez les chiffres de ces produits ensemble.

  • Ajoutez la somme à la somme des chiffres qui n'ont pas été multipliés par 2.

  • Si le dernier chiffre du total est 0 (ou, plus formellement, si le total
    modulo 10 est congru à 0), le nombre est valide!


et pour implémenter cela, j'ai parcouru le nombre entier et si la place du nombre dans laquelle je me trouvais avait un modulo 2 égal à 0, je multiplierais par deux et ajouterais à une variable appelée totalEven .

si ce n'était pas le cas, j'ajouterais le nombre dans totalOdd à totalOdd sans multiplication.

Je incrémenterais alors la place de un et vérifierais les autres nombres jusqu'à ce que j'atteigne 16 (le nombre maximum de chiffres pour une carte).

J'ajouterais plus tard les deux variables et vérifierais si le modulo dix total était égal à 0. Si cela signifie que le numéro de carte de crédit est correct, sinon il est faux.

voici le code:

#include <stdio.h>
#include <cs50.h>


long power();


int main(void)
{
    //AMERX 15 STRT 34 OR 37
    //MC 16 STRT 51, 52, 53, 54, 55
    //VZA 13 OR 16 STRT 4

   long input;
   bool isValid = true;
   string type;
   int mod = 10;
   int place = 1;
   int num = 0;
   int totalEven = 0;
   int totalOdd = 0;

   do
   {
      input = get_long("Number: ");
      

   }

   while(input < 0);
   
      
      for(int i = 0; i < 16; i++)
      {
          num = ((input % mod) - (input % (mod /10))) / (mod/10);
          if(place % 2 == 0)
          {
              totalEven = totalEven + num * 2;
          }
          else
          {
             totalOdd = totalOdd + num; 
          }
          
          mod = mod * 10;
          place++;
      }
      
      if((totalEven + totalOdd) % 10 == 0 )
      {
          isValid = true;
      }
      else
      {
          isValid = false;
          
          printf("%i , %i", totalEven, totalOdd);
      }
   if (isValid == true){
   if((input < (38 * power(10, 13)) && input >=(37 * power(10, 13))) || (input < (35 * power(10,13)) && input >= (34 * power(10, 13)))) 
   {
       type = "AMEX\n";
   }
   else if(input >= (51 * power(10, 14)) && input < (56 * power(10, 14)))
   {
       type = "MASTERCARD\n";
   }
   else if((input < (5 * power(10, 12)) && input >= (4 * power(10, 12))) || (input < (5 * power(10, 15)) && input >= (4 * power(10, 15))))
   {
       type = "VISA\n";
   } 
       else{
       type = "error\n";
   }
}
   else
   {
       type = "INVALID\n";
   }
   

    if((totalEven + totalOdd) % 10 == 0 )
    {
      isValid = true;
    }
    else
    {
      isValid = false;
    }
      
    printf("%s", type);

}





long power(int n, int p)
{
    long result = 1;
    for(int i = 0; i<p; i++)
    {
        result = result * n;
    }
    return result;

     //gets input and stores it in well.. input
     input = get_long("Number: ");
      
      // a formula to single out a number, starting with the ones and then as you can see, mod is muliplied by 10 to go over the second number.

      num = ((input % mod) - (input % (mod /10))) / (mod/10);
      
      //loops 16 times
      for(int i = 0; i < 16; i++)
      {
          // if the place is even execute below
          if(place % 2 == 0)
          {
              totalEven = totalEven + num * 2;
          }   
          //else do this
          else if (place % 2 != 0)
          {
             totalOdd = totalOdd + num; 
          }
          //moves to the next number
          mod = mod * 10;
          place++;
      }
      
      //fufils the last step of the algorithm

      if((totalEven + totalOdd) % 10 == 0 )
      {
          isValid = true;
      }
      else
      {
          isValid = false;
      }

Le problème est que ce bloc de code me donne une valeur invalide ou !isValid même si le numéro de carte de crédit est censé être correct et j'ai vérifié ma "formule" et cela fonctionne très bien ...

Je ne sais absolument pas quoi faire ... Je suis un modeste amateur, alors ne me rôtissez pas pour la monstruosité ci-dessus.

voici une version complète du code

#include <stdio.h>
#include <cs50.h>

//list of variables

   //is the card valid
   bool isValid = true;
   // the creditcard number
   long input;
   //mod stands for modules, and is used to single out each number as seen later
   int mod = 10;
   //the location at which number I am checking
   int place = 1;
   //num is the number I am checking that has been singled out
   int num = 0;
   //total of numbers * 2 located at locations numbered with even numbers
   int totalEven = 0;
   //total of numbers located at locations numbered with odd numbers
   int totalOdd = 0;


11 commentaires

Traitez les numéros de carte de crédit comme une chaîne de chiffres plutôt que comme un nombre. Il est plus facile de les traiter. Et vous pouvez vérifier qu'il y a 16 chiffres plus facilement. Il y a de nombreuses questions connexes dans la balise cs50 qui pourraient vous aider - toutes n'auront pas la balise luhn . Recherchez « [cs50] luhn ».


mod/10 est juste 10/10 qui est juste 1, donc je soupçonne qu'il y a une erreur dans `num = ((input% mod) - (input% (mod / 10))) / (mod / 10);`. Bien que sensiblement plus lent, je seconde l'idée que la manipulation des cordes serait plus facile à comprendre.


Vous n'obtenez jamais chaque chiffre dans la boucle.


La long de votre ordinateur est-elle assez grande pour 16 chiffres? Essayez #include <limits.h> et printf("%ld\n", LONG_MAX);


suggestion de débogage: si vous êtes prêt à utiliser le module et les mathématiques pour faire l'extraction des chiffres, faites d'abord fonctionner cet algorithme et écrivez un programme qui le teste, en prenant un ensemble d'entrées et en vérifiant chaque sortie. Ensuite, développez à partir de là pour obtenir tous les autres chiffres pour chaque numéro. Une fois que cela fonctionne, terminez l'algorithme luhn avec ce code dont vous savez qu'il fonctionne.


C'est fou cependant, puisque le numéro de carte était à l'origine entré sous forme de chiffres. «Numéro» ne signifie pas automatiquement «entier», comme le "221b Baker Street" également les numéros de maison, tels que "221b Baker Street" et les numéros de téléphone comme "0123456789" où le 0 principal essentiel serait perdu.


@DanielFarrel Je sais que 10/10 est égal à 1, mais cela est censé se produire ... par exemple, si l' input était 1069 et mod était 10, alors num serait .... `num = ((1069% 10) - (1069% (10/10))) / (10/10); num = (9 - (1069% 1) / 1); num = (9 - 0/1); num = 9; `alors comme vous pouvez le voir si vous regardez le code plus profondément, le mod est multiplié par 10 pour passer au chiffre suivant


@Someprogrammerdude est-il possible de clarifier?


ah, alors le problème est que `num = ((input% mod) - (input% (mod / 10))) / (mod / 10); `est censé être dans la boucle for ?


@DanielFarrell oui, il est censé être là-dedans, et boucle 16 fois en déplaçant 16 chiffres


Votre code est bien trop complexe. Utilisez un tableau de chiffres au lieu d'un nombre où vous devez calculer le reste à l'intérieur de la boucle.


3 Réponses :


1
votes

Pendant que je regardais votre code, je voudrais signaler certaines erreurs.

  1. Vous avez oublié: #include <string.h> car vous avez déclaré string type dans le code.
  2. input = get_long("Number: "); devrait avoir sa propre boucle do-while while au cas où l'utilisateur entre des lettres ou des nombres incorrects.
  3. if(place % 2 == 0){ totalEven = totalEven + num * 2; } else if (place % 2 != 0){ totalEven = totalEven + num; } devrait totalOdd = totalOdd + num pour la deuxième partie
  4. totalEven = totalEven + num * 2 est juste et faux en même temps. Cela ne fonctionne que si le nombre multiplié par 2 est inférieur à 10. Si le nombre * 2> = 10, disons num = 6, alors 6 * 2 est 12 qui serait alors 1 + 2 + totalEven.
  5. num = ((input % mod) - (input % (mod /10))) / (mod/10); Cela devrait être dans la première for loop .
  6. Dans #include <math.h> , il y a une fonction power appelée pow qui fait exactement comme votre fonction power() .

5 commentaires

A part: le type de string n'est pas dans string.h mais est un type non standard (et peu judicieux) dans cs50.h qui a été inclus. Et pow() est assez différent - il fonctionne avec des valeurs en virgule flottante. Il n'y en a même pas besoin, il existe de meilleures façons de décomposer un nombre.


Je vois, je n'étais pas au courant que la string n'était pas une partie string.h . Merci pour les informations sur pow() .


@WeatherVane oui, en y repensant maintenant, j'aurais pu transformer l'ensemble du processus en un programme plus succinct ... peut-être que transformer l'intégralité de l'int en une chaîne puis l'utiliser comme un tableau pour valider le nombre saisi serait bien mieux ...


@ someGuy5864 il vaut mieux ne jamais avoir de numéro de carte, numéro de téléphone, code PIN, etc. sous forme d'entier en premier lieu. «Nombre» ne signifie pas «entier». Il est préférable de le traiter comme une chaîne de chiffres.


@WeatherVane ouais vrai ... merci pour l'homme de pointe



1
votes

Je ne suis pas un expert en algorithme de Luhn, mais quand je lis https://en.wikipedia.org/wiki/Luhn_algorithm, il me semble que vous vous trompez.

Citation de https://en.wikipedia.org/wiki/Luhn_algorithm :

À partir du chiffre le plus à droite (à l'exclusion du chiffre de contrôle) et en se déplaçant vers la gauche, doublez la valeur de chaque deuxième chiffre. Le chiffre de contrôle n'est ni doublé ni inclus dans ce calcul; le premier chiffre doublé est le chiffre situé immédiatement à gauche du chiffre de contrôle. Si le résultat de cette opération de doublement est supérieur à 9 (par exemple, 8 × 2 = 16), ajoutez les chiffres du résultat (par exemple, 16: 1 + 6 = 7, 18: 1 + 8 = 9) ou, alternativement, le même résultat final peut être trouvé en soustrayant 9 de ce résultat (par exemple, 16:16 - 9 = 7, 18: 18 - 9 = 9).

Je ne vois nulle part dans votre code où vous gérez cette partie en gras.

Au lieu de

// Double every second element and check for overflow
for (idx = 1; idx < 16; idx += 2)
{
    digits[idx] = 2 * digits[idx];
    if (digits[idx] > 9) digits[idx] = digits[idx] - 9;
}

// Calculate the sum
sum = 0;
for (idx = 0; idx < 16; ++idx)
{
    sum = sum + digits[idx];
}

Je pense que tu as besoin

int digits[] = {8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1};
// Notice that index zero is the rightmost digit

Cela dit - je pense que vous rendez la mise en œuvre beaucoup plus complexe que nécessaire en stockant l'entrée sous forme de nombre. Au lieu d'un nombre, vous pouvez utiliser un tableau de chiffres.

C'est - au lieu de

long input = 1122334455667788

utilisation

int tmp = num * 2;
if (tmp > 9) tmp = tmp - 9;
totalEven = totalEven + tmp;

De cette façon, l'algorithme est beaucoup plus simple:

totalEven = totalEven + num * 2;

Si vous devez recevoir l'entrée sous forme de nombre, commencez par appeler une fonction qui convertit le nombre en un tableau de chiffres. Vous pouvez trouver de très nombreux exemples de la façon dont cette conversion est effectuée ici sur SO. Ici, la conversion d'un entier en tableau de chiffres n'est qu'un des nombreux exemples.


1 commentaires

Merci mon pote pour le conseil, j'ai réécrit le code en utilisant un hybride de ce que vous avez suggéré et de ma méthode et cela a fonctionné !!



0
votes

Attention: j'ai utilisé CS50X Library car la question semble être la même.

#include <stdio.h>
#include <cs50.h>

// Luhn's Algorithm

int main(void) 
{
    long cardNumber = get_long("Please, enter your card number: ");
    int sum1 = 0, num = 0, remainder = 0, sum2 = 0;
    long temp = cardNumber;
    
    while (temp > 0) 
    {
        num = ((temp / 10) % 10) * 2; // Multiplying every other digit by 2, starting with the number’s second-to-last digit
        while (num > 0) 
        {
            remainder = num % 10;
            sum1 += remainder; // Adding those products’ digits together
            num /= 10;
        }
        temp /= 100;
    }
    
    // So as to restore the initial values of remainder and temp for the use in next loop
    remainder = 0;
    temp = cardNumber;
    
    while (temp > 0) 
    {
        remainder = temp % 10;
        sum2 += remainder; // Sum of the digits that weren’t multiplied by 2
        temp /= 100;
    }
    
    ((sum1 + sum2) % 10) == 0 ? printf("Valid\n") : printf("Invalid\n");
    return 0;
}


0 commentaires