CS50 Luhn 的 C 算法

CS50 Luhn's Algorithm in C

我是 C 的新手,正在学习 CS50。我无法让我的代码工作。我几乎肯定 for 语句是正确的。 for语句给出的和是正确的。

我认为这与 if 语句中的 (floor(ccNumber / pow(10,13) == 34) 有关。

卢恩算法的解释:https://cs50.harvard.edu/x/2020/psets/1/credit/

用于测试的示例信用卡号码:https://www.freeformatter.com/credit-card-number-generator-validator.html

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

我或多或少在维基百科上关注的伪代码::

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
}

信用卡号很大,你确定long就够了,不需要long long

除此之外,在您的表达式中,例如 floor(ccNumber / pow(10,13) == 34),')' 位置错误,您想要 floor(ccNumber / pow(10,13)) == 34,当然其他人也一样:

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

但在

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

你冒着得到不好结果的风险,你需要检查 (floor(ccNumber / pow(10,12)) == 4) 仅当 nDigits == 13(floor(ccNumber / pow(10,15)) == 4) 仅当 nDigits == 16 并且你可以简化为:

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

最后 :

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

}

编译与执行:

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 $ 

还请注意,进行浮点计算是有风险的,您可以只使用 long long

关于:

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

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

credit() 的调用正在传递变量的内容:ccnumber 而不是指向 ccnumber 的指针。所以被调用的函数:credit() 永远不能改变 main().

中的那个值

建议:

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

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

一般来说,永远不要比较浮点值和整数值是否相等,所以下面这种表达式是非常不可靠的:

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

此外,这样的表达式实际上只是将 ccnumber 的两个数字与 34 进行比较。最好将信用卡号作为字符串读取,然后将这两个数字与 ccnumber[ first digit position ] == 3 && ccnumber[ second digit position ] == 4 进行比较。所有其他使用 pow()

的表达式都存在类似的考虑

因此,建议通过以下方式获取信用卡号:

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

结果将是指向包含信用卡号的字符串的指针。每个 'interesting' 数字都可以作为索引(从 0 开始)进入信用卡号码

但是,如果您真的不想使用 char 数组,您可以替换像这样的表达式;

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

与:

(ccNumber / 10000000000000 == 34)
#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;
}