您的算法可能并不完全正确。因此,我将提出一种不同的方法。您可以查看循环中的每个数字。而且,您还可以一步完成整个校验和计算。
我将向您展示如何做并解释其背后的算法。
顺便说一句。选择正确的算法始终是成功的关键。
因此,首先我们需要考虑如何从数字中提取数字。这可以通过重复以下步骤在循环中完成:
- 执行模 10 除以得到一个数字
- 将整数除以 10
- 重复
让我们看一下 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。如果结果大于 10,则再次获取单个数字并将两个数字添加到校验和中
- 重复
所以,秘密在于,分析某种神秘的规范,给定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 << 0 相同
-
x * 2 与 x << 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;