0
votes

Algorithme de CS50 Luhn en C

Je suis nouveau sur C et je fais CS50. Je n'arrive pas à faire fonctionner mon code. Je suis presque sûr que la déclaration for est correcte. La somme donnée par l'instruction for est correcte.

Je pense que c'est quelque chose à voir avec le (floor(ccNumber / pow(10,13) == 34) dans les instructions if.

Explication de l'algorithme de Luhn: https://cs50.harvard.edu/x/2020/psets/1/credit/

Exemples de numéros de carte de crédit à tester: https://www.freeformatter.com/credit-card-number-generator-validator.html

function checkLuhn(string purportedCC) {
    int sum := integer(purportedCC[length(purportedCC)-1])
    int nDigits := length(purportedCC)
    int parity := nDigits modulus 2
    for i from 0 to nDigits - 2 {
        int digit := integer(purportedCC[i])
        if i modulus 2 = parity
            digit := digit × 2
        if digit > 9
            digit := digit - 9
        sum := sum + digit
    }
    return (sum modulus 10) = 0
}

Pseudocode que j'ai plus ou moins suivi sur Wikipédia:

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

void credit(long ccNumber);

int main(void)
{
    long ccNumber = 0;
    credit(ccNumber);
}

void credit(long ccNumber)
{

    do

    {
        ccNumber = get_long("Enter a credit card number: ");
    }

    while (ccNumber < 0);

    {
        int sum = 0;
        long ccNumberFormat = ccNumber;
        int nDigits = floor(log10(ccNumberFormat)) + 1;
        int parity = nDigits % 2; // if parity % 2 == 0 then even number of digits, otherwise odd number of digits


        for (int i = nDigits; i >= 0; i--) {
            int digit = ccNumberFormat % 10;

            if (i % 2 != parity) { // even number credit card digits will be multiplied by 2 every even number (starting from 0)
                digit = digit * 2;
                // printf("%d\n", sum);
            }
            if (digit > 9) {
                digit = digit - 9;
            }

            sum = sum + digit;
            ccNumberFormat /= 10;
            printf("%d\n", sum);
        }

        if (sum % 10 == 0 && nDigits == 15 && (floor(ccNumber / pow(10,13) == 34) || (floor(ccNumber / pow(10,13) == 37))))
        {
            printf("%s\n", "AMEX");
        }
        else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) && (floor(ccNumber / pow(10,12) == 4) || floor(ccNumber / pow(10,15) == 4)))
        {
            printf("%s\n", "VISA");
        }
        else if (sum % 10 == 0 && nDigits == 16 && ((floor(ccNumber / pow(10,14) >= 51) || (floor(ccNumber / pow(10,14) <= 55)))))
        {
            printf("%s\n", "MASTERCARD");
        }
    }
}


7 commentaires

(a) Fournissez toujours un exemple reproductible minimal lorsque vous demandez de l'aide au débogage. Cela inclut un exemple d'entrée qui reproduit le problème. (b) «Je n'arrive pas à faire fonctionner mon code» n'est pas une déclaration de problème appropriée. Indiquez quelle sortie ou quel autre comportement votre programme montre, indiquez quelle sortie ou quel autre comportement vous désirez à la place et, si nécessaire, expliquez la différence. (c) Arrêtez d'utiliser pow pour l'arithmétique entière. Certaines implémentations pow renvoient des résultats inexacts. (d) Traitez les numéros de carte de crédit comme des chaînes de chiffres et non comme des entiers mathématiques. Il est plus facile de travailler avec eux de cette façon. Obtenez une chaîne, pas longtemps.


un numéro de carte de crédit est un grand nombre, êtes-vous sûr que le long est assez long long plutôt que long long ? Aussi dans floor(ccNumber / pow(10,13) == 34) êtes-vous sûr de mettre le ')' au bon endroit? probablement que vous vouliez (floor(ccNumber / pow(10,13)) == 34) et bien sûr pareil pour les autres cas, mais attention lors du calcul en virgule flottante ...


@EricPostpischil (a & b) J'ai ajouté quelques entrées et indiqué la sortie que j'ai obtenue. (c) Que dois-je utiliser à côté de la pow ? Je pense que ^ ne fonctionne pas. (d) J'essaie d'appliquer ce que nous avons appris jusqu'à présent dans la leçon, à savoir utiliser% pour obtenir les chiffres. Donc pas de tableaux, de nombres sous forme de chaînes, etc.


Ce problème a déjà été abordé des dizaines de fois . Un pourcentage élevé d'étudiants qui rencontrent des problèmes, se heurtent à la taille du problème int. Dans votre cas, vous l'avez, et pourquoi utilisez-vous des flottants dans l'algorithme de Luhn? Veuillez lire la lettre ouverte aux étudiants ayant des problèmes de devoirs


Vous savez, je commence à penser que cet exercice particulier a été explicitement conçu pour montrer aux étudiants, comment mettre en œuvre un algorithme simple; peut être gâché par l'utilisation de valeurs entières de taille inappropriée. La plupart des détritus semblent être motivés par des solutions de contournement inappropriées lorsque les choses ne fonctionnent pas ou lorsque l'élève se sent obligé de masquer le code juste assez pour qu'il ne semble pas avoir été copié.


Que suis-je censé utiliser sinon flotter? cs50.harvard.edu/x/2020/psets/1/credit dit d'utiliser get_long. J'essaie simplement d'utiliser ce qui est enseigné dans les leçons jusqu'à présent. J'ai passé 10 heures sur ce problème et seulement quand je ne pouvais pas trouver la réponse que je publie ici.


Selon leur documentation, le type de retour get_long est long. Qu'est-ce que sizeof(long) sur votre système?


3 Réponses :


1
votes

Un numéro de carte de crédit est un grand nombre, êtes-vous sûr que le long est suffisant et que vous n'avez pas besoin de long long

Hors de cela dans vos expressions comme floor(ccNumber / pow(10,13) == 34) le ')' est mal placé et vous vouliez floor(ccNumber / pow(10,13)) == 34 et bien sûr le même pour les autres :

pi@raspberrypi:/tmp $ gcc -Wall f.c -lm
pi@raspberrypi:/tmp $ ./a.out
4532057997187363 VISA
4485661945778178 VISA
2720995573736457 MASTERCARD
2720998284576493 MASTERCARD
375137447049450 AMEX
378572901284556 AMEX
pi@raspberrypi:/tmp $ 

mais en

#include <stdio.h>
#include <math.h>

void credit(long long ccNumber);

int main(void)
{
  long long cc[] = { 4532057997187363ll, // visa
                     4485661945778178ll, // visa
                     2720995573736457ll, // MasterCard
                     2720998284576493ll, // MasterCard
                     375137447049450ll, // amex
                     378572901284556ll, // amex
  };

 for (int i = 0; i != sizeof(cc)/sizeof(*cc); ++i)
    credit(cc[i]);
  
  return 0;
}

void credit(long long ccNumber)
{
  int sum = 0;
  long long ccNumberFormat = ccNumber;
  int nDigits = floor(log10(ccNumberFormat)) + 1;
  int parity = nDigits % 2; // if parity % 2 == 0 then even number of digits, otherwise odd number of digits
  
  
  for (int i = nDigits; i >= 0; i--) {
    int digit = ccNumberFormat % 10;
    
    if (i % 2 != parity) { // even number credit card digits will be multiplied by 2 every even number (starting from 0)
      digit = digit * 2;
      // printf("%d\n", sum);
    }
    if (digit > 9) {
      digit = digit - 9;
    }
    
    sum = sum + digit;
    ccNumberFormat /= 10;
    //printf("%d\n", sum);
  }
  
  if (sum % 10 == 0 && nDigits == 15 && ((floor(ccNumber / pow(10,13)) == 34) || (floor(ccNumber / pow(10,13)) == 37))) {
      printf("%lld %s\n", ccNumber, "AMEX");
    }
  else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&(floor(ccNumber / pow(10,nDigits-1)) == 4)) {
    printf("%lld %s\n", ccNumber, "VISA");
  }
  else if (sum % 10 == 0 && nDigits == 16 && ((floor(ccNumber / pow(10,14)) >= 51) || (floor(ccNumber / pow(10,14)) <= 55))) {
    printf("%lld %s\n", ccNumber, "MASTERCARD");
  }

}

vous prenez le risque d'avoir un mauvais résultat, vous devez vérifier (floor(ccNumber / pow(10,12)) == 4) uniquement si nDigits == 13 et (floor(ccNumber / pow(10,15)) == 4) uniquement si nDigits == 16 et vous pouvez simplifier pour avoir:

    else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&(floor(ccNumber / pow(10,nDigits-1)) == 4))

Alors enfin:

    else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&((floor(ccNumber / pow(10,12)) == 4) || (floor(ccNumber / pow(10,15)) == 4)))

Compilation et exécution:

    if (sum % 10 == 0 && nDigits == 15 && ((floor(ccNumber / pow(10,13)) == 34) || (floor(ccNumber / pow(10,13)) == 37)))
    {
        printf("%s\n", "AMEX");
    }
    else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&((floor(ccNumber / pow(10,12)) == 4) || (floor(ccNumber / pow(10,15)) == 4)))
    {
        printf("%s\n", "VISA");
    }
    else if (sum % 10 == 0 && nDigits == 16 && ((floor(ccNumber / pow(10,14)) >= 51) || (floor(ccNumber / pow(10,14)) <= 55)))
    {
        printf("%s\n", "MASTERCARD");
    }

notez aussi que faire du calcul en virgule flottante est un risque, vous pouvez tout faire uniquement en utilisant long long


0 commentaires

0
votes

En ce qui concerne:

(ccNumber / 10000000000000 == 34)

L'appel à credit() passe le contenu de la variable: ccnumber plutôt qu'un pointeur vers ccnumber . Ainsi, la fonction appelée: credit() ne peut jamais changer cette valeur dans main() .

Suggérer:

(floor(ccNumber / pow(10,13) == 34)

En général, ne devrait jamais comparer une valeur à virgule flottante avec une valeur entière pour l'égalité, donc le type d'expression suivant est très peu fiable:

string ccnumber = get_string( "enter credit card number" );

De plus, une telle expression compare simplement deux chiffres du numéro ccnumber à 34. BEAUCOUP mieux d'avoir lu le numéro de carte de crédit sous forme de chaîne, puis de comparer les deux chiffres à ccnumber[ first digit position ] == 3 && ccnumber[ second digit position ] == 4 . Des considérations similaires existent pour toutes les autres expressions qui utilisent pow()

Par conséquent, suggérez d'obtenir le numéro de carte de crédit via:

(floor(ccNumber / pow(10,13) == 34)

le résultat sera un pointeur vers la chaîne contenant le numéro de carte de crédit. chacun des chiffres `` intéressants '' peut ensuite être consulté sous forme d'index (commençant par 0) dans le numéro de carte de crédit

cependant, si vous ne voulez vraiment pas utiliser un tableau de caractères, vous pouvez remplacer des expressions telles que;

int main(void)
{
    long ccNumber = 0;
    credit( &ccNumber );
}

void credit(long * ccNumber)
{
    do
    {
        *ccNumber = get_long("Enter a credit card number: ");
    }

avec:

int main(void)
{
    long ccNumber = 0;
    credit(ccNumber);
}

void credit(long ccNumber)
{
    do
    {
        ccNumber = get_long("Enter a credit card number: ");
    }


2 commentaires

Je comprends la conversion du nombre en chaîne maintenant. Ils ne l'ont pas encore introduit dans cs50, donc j'essaie d'utiliser des stratégies enseignées jusqu'à ce point. Je ne savais pas non plus si la syntaxe Python comme str[0] pour obtenir la position fonctionnait en C.


dans Internet d'aujourd'hui, toutes les déclarations disponibles de cs50 sont très bien expliquées (et démontrées). Par exemple, get_string() est entièrement documenté sur: get_string et: toutes les fonctions cs50



0
votes
#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;
}

1 commentaires

Veuillez expliquer comment votre code fonctionne et ce que vous avez changé.