【问题标题】:Error with two credit card numbers. Identifies the number as the wrong credit card type两个信用卡号出错。将号码识别为错误的信用卡类型
【发布时间】:2021-09-14 10:48:53
【问题描述】:

这些是我目前的错误,我认为我在数学上做错了,但我尝试的一切都没有奏效。

Ps:对不起,如果我的问题格式不好,第一次使用stackflow。

:) credit.c 存在

:) credit.c 编译

:) 将 378282246310005 识别为 AMEX

:) 将 371449635398431 识别为 AMEX

:) 将 5555555555554444 识别为 MASTERCARD

:) 将 5105105105105100 识别为 MASTERCARD

:) 将 4111111111111111 识别为 VISA

:) 将 4012888888881881 识别为 VISA

:) 将 4222222222222 识别为 VISA

:) 将 1234567890 标识为无效

:) 将 369421438430814 标识为无效

:) 将 4062901840 标识为无效

:) 将 5673598276138003 标识为无效

:( 将 4111111111111113 标识为无效 预期为“INVALID\n”,而不是“VISA\n”

:( 将 4222222222223 标识为无效 预期为“INVALID\n”,而不是“VISA\n”


#include <cs50.h>

#include <math.h>

// Prompt user for credit card number
int main(void)
{
    long credit_card, credit_number;
    do
    {
        credit_card = get_long("Enter credit card number: ");
    }
    while (credit_card < 0);
    
    credit_number = credit_card;
    

    // Calculate total number of digits
    int count = (credit_number == 0) ? 1  : (log10(credit_number) + 1);
    int summation = 0;

    while (credit_number == 0)
    {
        int x = credit_number % 10; summation += x;
        int y = 2 * ((credit_number / 10) % 10);
        int r = (y % 10) + floor((y / 10) % 10); summation += r; credit_number /= 100;
    }

    string card;
    // Identify which card type you get after inputing your credit card number
    int test = cc / pow(10, count - 2);
    if ((count == 13 || count == 16) && test / 10 == 4)
    {
        card = "VISA";
    }
    else if (count == 16  && test >= 51 && test <= 55)
    {
        card = "MASTERCARD";
    }
    else if (count == 15 && (test == 34 || test == 37))
    {
        card = "AMEX";
    }
    else
    {
        card = "INVALID";
    }

    // Final verification
    if (sum % 10 == 0)
    {
        printf("%s\n", card);
    }
    else
    {
        printf("INVALID\n");
    }

}```

【问题讨论】:

  • 你的数字对于long来说是不是太大了?
  • 避免将 "float functions" 作为 pow/floor 进行整数计算。
  • 十进制 16 位数字需要约 50 位。 long 很可能有 32 个。将输入视为一串数字而不是数字。
  • 信用卡号是一个 ID,您不太可能需要对其进行任何算术运算,因此 std::string(加上检查有效数字)更合适
  • CS50 是 C 课程,而不是 C++ 课程。如果你试图从中学习 C++,你会变得非常困惑。

标签: c++ cs50


【解决方案1】:

您的算法可能并不完全正确。因此,我将提出一种不同的方法。您可以查看循环中的每个数字。而且,您还可以一步完成整个校验和计算。

我将向您展示如何做并解释其背后的算法。

顺便说一句。选择正确的算法始终是成功的关键。

因此,首先我们需要考虑如何从数字中提取数字。这可以通过重复以下步骤在循环中完成:

  1. 执行模 10 除以得到一个数字
  2. 将整数除以 10
  3. 重复

让我们看一下 1234 的例子。

  • 第 1 步将得到 4 -- (1234 % 10 = 4)
  • 第 2 步会将原始数字转换为 123 -- (1234 / 10 = 123)
  • 第 1 步将得到 3 -- (123 % 10 = 3)
  • 第 2 步会将之前的数字转换为 12 -- (123 / 10 = 12)
  • 第 1 步将得到 2 -- (12 % 10 = 2)
  • 第 2 步会将之前的数字转换为 1 -- (12 / 10 = 1)
  • 第 1 步将得到 1 -- (1 % 10 = 1)
  • 第 2 步会将之前的数字转换为 0 -- (1 / 10 = 0)

然后循环停止。此外,我们可以观察到循环停止,当结果除法变为 0 时。而且,我们还看到循环执行的次数等于数字中的位数。但这在某种程度上是显而易见的。

好的,那么让我们看看,到目前为止我们学到了什么

while (creditCardNumber > 0) {

    unsigned int digit = creditCardNumber % 10;
    creditCardNumber /= 10;

    ++countOfDigits;
}

这将获取所有数字并计算它们。

很好。让我们进入下一步。

为了以后的验证和比较,我们需要获取数字的最高位(第一位)和第二位(第二位)。

为此,我们定义了 2 个变量来保存数字。我们只需将当前评估的数字(并在每个循环执行中覆盖它)分配给“mostSignificantDigit”。在循环结束时,我们将把它放在我们想要的变量中。

对于“secondMostSignificantDigit”,我们将简单地复制“mostSignificantDigit”的“旧”或“先前”值,然后将新值分配给“mostSignificantDigit”。这样一来,我们将始终拥有这两个值。

循环现在看起来像这样:

while (creditCardNumber > 0) {

    const unsigned int digit = creditCardNumber % 10;

    secondMostSignificantDigit = mostSignificantDigit;
    mostSignificantDigit = digit;

    creditCardNumber /= 10;

    ++countOfDigits;
}

好的,现在我们来看看可能更复杂的部分。校验和。计算方法是。

  1. 从最低有效(最后一个)数字开始
  2. 不要将数字相乘,相当于将其乘以1,并将其添加到校验和中
  3. 转到下一个数字。将其乘以 2。如果结果大于 10,则再次获取单个数字并将两个数字添加到校验和中
  4. 重复

所以,秘密在于,分析某种神秘的规范,给定here。如果我们从最后一个数字开始,我们不相乘,下一个数字将被相乘,下一个数字将被相乘,以此类推。

“不相乘”与乘以 1 相同。这意味着:在循环中,我们需要交替乘以 1 或 2。

如何在循环中获取交替数字?算法相当简单。如果您需要交替数字,可以说 x,y,x,y,x,y,x...,然后,构建 x 和 y 的总和并执行减法“值 = 总和 - 值”。例子: 我们需要交替的值 1 和 2。总和是 3。为了得到下一个值,我们从总和中减去当前值。

  • 初始值 = 1
  • 总和 = 3
  • 当前值 = 初始值 = 1
  • 下一个值 = 3 - 1 = 2。当前值 = 2
  • 下一个值 = 3 - 2 = 1。当前值 = 1
  • 下一个值 = 3 - 1 = 2。当前值 = 2
  • 下一个值 = 3 - 2 = 1。当前值 = 1
  • 下一个值 = 3 - 1 = 2。当前值 = 2
  • 下一个值 = 3 - 2 = 1。当前值 = 1
  • 。 . .

很好,现在我们明白了,如何制作交替值。

接下来,如果我们将一个数字乘以 2,那么最大的结果可能是一个 2 位的值。我们得到一个以模数和整数除以 10 的个位数。

而且,现在重要的是,我们是否相乘并不重要,因为如果我们不相乘,那么高位将始终为 0。这不会对总和做出贡献。

尽管如此,我们始终可以进行乘法运算并将结果拆分为 2 位数字(其中许多数字的高位为 0)。

结果将是:

checkSum += (digit * multiplier) % 10 + (digit * multiplier) / 10;
multiplier = 3 - multiplier;

一个惊人的简单公式。

接下来,如果我们知道 C 或 C++,我们也知道与 2 的乘法可以通过左移一位非常有效地完成。而且,此外,“无乘法”可以通过位移 0 来完成。这比乘法效率更高,速度更快。

  • x * 1 与 x &lt;&lt; 0 相同
  • x * 2 与 x &lt;&lt; 1 相同

对于最终结果,我们将使用这种机制,在 0 和 1 之间交替乘数并进行移位。

这将给我们一个非常有效的校验和计算。

在程序结束时,我们将使用所有收集的值并将它们与规范进行比较。

这将导致:

int main() {

    // Get the credit card number. Unfortunately I do not know CS50. I use the C++ standard iostream lib.
    // Please replace the following 4 lines with your CS50 equivalent
    unsigned long long creditCardNumber;
    std::cout << "Enter credit card number: ";
    std::cin >> creditCardNumber;
    std::cout << "\n\n";

    // We need to count the number of digits for validation
    unsigned int countOfDigits = 0;

    // Here we will calculate the checksum
    unsigned int checkSum = 0;

    // We need to multiply digits with 1 or with 2
    unsigned int multiplier = 0;

    // For validation purposes we need the most significant 2 digits
    unsigned int mostSignificantDigit = 0;
    unsigned int secondMostSignificantDigit = 0;

    // Now we get all digits from the credit card number in a loop
    while (creditCardNumber > 0) {

        // Get the least significant digits (for 1234 it will be 4)
        const unsigned int digit = creditCardNumber % 10;
        
        // Now we have one digit more. In the end we will have the number of all digits
        ++countOfDigits;

        // Simply remember the most significant digits
        secondMostSignificantDigit = mostSignificantDigit;
        mostSignificantDigit = digit;

        // Calculate the checksum
        checkSum += (digit << multiplier) % 10 + (digit << multiplier) / 10;

        // Multiplier for next loop 
        multiplier = 1 - multiplier;

        creditCardNumber /= 10;
    }
    // Get the least significant digit of the checksum
    checkSum %= 10;

    // Validate all calculated values and show the result
    if ((0 == checkSum) &&                  // Checksum must be correct AND
        (15 == countOfDigits) &&            // Count of digits must be correct AND
        ((3 == mostSignificantDigit) &&     // Most significant digits must be correct 
        ((4 == secondMostSignificantDigit) || (7 == secondMostSignificantDigit)))) {
        std::cout << "AMEX\n";
    }
    else if ((0 == checkSum) &&             // Checksum must be correct AND
        (16 == countOfDigits) &&            // Count of digits must be correct AND
        ((5 == mostSignificantDigit) &&     // Most significant digits must be correct
        ((secondMostSignificantDigit > 0) && (secondMostSignificantDigit < 6)))) {
        std::cout << "MASTERCARD\n";
    }
    else if ((0 == checkSum) &&             // Checksum must be correct AND
        ((16 == countOfDigits) || (13 == countOfDigits)) && // Count of digits must be correct AND
        ((4 == mostSignificantDigit))) {    // Most significant digit must be correct
        std::cout << "VISA\n";
    }
    else {
        std::cout << "INVALID\n";
    }
    return 0;
}

我们从这个例子中学到的是整数除法和模除法以及identity element 用于二进制运算的智能用法。

如有问题,请询问



为了完整起见,我将向您展示基于 std::string 并使用现代 C++ 元素和算法的 C++ 解决方案。

例如,整个校验和计算将使用一条语句完成。整个程序不包含任何循环。

#include <iostream>
#include <string>
#include <regex>
#include <numeric>

int main() {

    // ---------------------------------------------------------------------------------------------------
    // Get user input
    // Inform user, what to do. Enter a credit card number. We are a little tolerant with the input format
    std::cout << "\nPlease enter a credit card number:\t";

    // Get the number, in any format from the user
    std::string creditCardNumber{};
    std::getline(std::cin, creditCardNumber);

    // Remove the noise, meaning, all non digits from the credit card number
    creditCardNumber = std::regex_replace(creditCardNumber, std::regex(R"(\D)"), "");

    // ---------------------------------------------------------------------------------------------------
    // Calculate checksum
    unsigned int checksum = std::accumulate(creditCardNumber.rbegin(), creditCardNumber.rend(), 0U,
        [multiplier = 1U](const unsigned int sum, const char digit) mutable -> unsigned int {
        multiplier = 1 - multiplier; unsigned int value = digit - '0';
        return sum + ((value << multiplier) % 10) + ((value << multiplier) / 10); });

    // We are only interested in the lowest digit
    checksum %= 10;

    // ---------------------------------------------------------------------------------------------------
    // Validation and output
    if ((0 == checksum) &&                      // Checksum must be correct AND
        (15 == creditCardNumber.length()) &&    // Count of digits must be correct AND
        (('3' == creditCardNumber[0]) &&        // Most significant digits must be correct 
        (('4' == creditCardNumber[1]) || ('7' == creditCardNumber[1])))) {
        std::cout << "AMEX\n";
    }
    else if ((0 == checksum) &&                 // Checksum must be correct AND
        (16 == creditCardNumber.length()) &&    // Count of digits must be correct AND
        (('5' == creditCardNumber[0]) &&        // Most significant digits must be correct
        ((creditCardNumber[1] > '0') && (creditCardNumber[1] < '6')))) {
        std::cout << "MASTERCARD\n";
    }
    else if ((0 == checksum) &&                 // Checksum must be correct AND
        ((16 == creditCardNumber.length()) || (13 == creditCardNumber.length())) && // Count of digits must be correct AND
        (('4' == creditCardNumber[0]))) {       // Most significant digit must be correct
        std::cout << "VISA\n";
    }
    else {
        std::cout << "INVALID\n";
    }
    return 0;

【讨论】:

  • 这是对现有答案 stackoverflow.com/a/62356725/4117728 的回收。这不是抄袭(我怀疑它,但实际上不是来自你,不过我检查过),但是如果这个问题与另一个问题的答案相同,那么它应该被标记为重复而不是重复答案
  • “顺便说一句。选择正确的算法始终是成功的关键。”实际上这有点不对劲。 OPs 代码中的主要问题似乎是 ID 的类型而不是算法
  • 我不是模组,我只是一个非常关心抄袭的普通用户,这个答案有点可疑。正如我已经说过的,这绝对不是抄袭,我不知道在这种情况下什么是最好的。当我写“那么它应该被标记为重复”时,我应该写“也许它应该被标记......”
  • 顺便说一句,您可以通过用户名旁边的菱形识别模组。
  • 我确实在不同的帖子中看到了相同的答案,因此我发布了我的问题,但我相信在进一步调查后我的答案在于 Luhns 算法,我似乎错了。发布此消息后我注意到的另一个问题是“
猜你喜欢
  • 1970-01-01
  • 2014-07-17
  • 1970-01-01
  • 2013-05-24
  • 2014-08-14
  • 2012-04-25
  • 2011-08-20
  • 1970-01-01
相关资源
最近更新 更多