Credit

Person holding credit cards

要解决的问题

信用卡(或借记卡)是一张塑料卡,可以用来支付商品和服务。卡上印有一串号码,这串号码也存储在某个数据库中;当你用卡购物时,发卡机构就知道该向谁收费。世界上有很多人拥有信用卡,因此这些号码相当长:American Express 使用 15 位数字,MasterCard 使用 16 位数字,Visa 使用 13 位和 16 位数字。而且这些是十进制数字(0 到 9),不是二进制数字,这意味着例如 American Express 最多可以印出 10^15 = 1,000,000,000,000,000 张不同的卡!(也就是一千万亿。)

不过,这有点夸张,因为信用卡号码实际上有一定结构。所有 American Express 号码都以 34 或 37 开头;大多数 MasterCard 号码以 51、52、53、54 或 55 开头(还有一些其他可能的起始数字,本题暂不考虑);所有 Visa 号码都以 4 开头。此外,信用卡号码还内置了一个“校验和”,也就是至少一个数字与其他数字之间的数学关系。这个校验和让计算机(或喜欢数学的人)无需查询可能很慢的数据库,就能检测拼写错误(例如数字调换),即便不一定能识别欺诈号码。当然,不诚实的数学家完全可以构造一个仍然满足数学约束的假号码,所以更严格的检查仍需要查询数据库。

请在名为 credit 的文件夹中创建 credit.c,并用 C 实现一个程序,用来检查给定信用卡号码是否有效。

Luhn 算法

那么这个秘密公式是什么呢?大多数卡使用的是 IBM 的 Hans Peter Luhn 发明的算法。根据 Luhn 算法,可以按如下方式判断信用卡号码在语法上是否有效:

  1. 从号码的倒数第二位开始,每隔一位乘以 2,然后把这些乘积的各位数字相加。
  2. 将这个和与未乘以 2 的那些数字之和相加。
  3. 如果总和的最后一位是 0(更正式地说,如果总和模 10 等于 0),则该号码有效!

这听起来有点绕,所以我们用 David 的 Visa 卡号 4003600000000014 举个例子。

  1. 为了便于说明,先从倒数第二位开始,每隔一位加上下划线:

    4003600000000014

    接着,把每个带下划线的数字乘以 2:

    1•2 + 0•2 + 0•2 + 0•2 + 0•2 + 6•2 + 0•2 + 4•2

    得到:

    2 + 0 + 0 + 0 + 0 + 12 + 0 + 8

    现在把这些乘积的各位数字相加(注意不是把乘积本身相加):

    2 + 0 + 0 + 0 + 0 + 1 + 2 + 0 + 8 = 13

  2. 现在把这个和(13)加上那些没有乘以 2 的数字之和(从末尾开始):

    13 + 4 + 0 + 0 + 0 + 0 + 0 + 3 + 0 = 20

  3. 没错,这个总和(20)的最后一位是 0,所以 David 的卡号是有效的!

因此,验证信用卡号码并不难,但手工完成会有些繁琐。让我们写一个程序来完成它。

实现细节

credit 目录中的 credit.c 文件里,编写一个程序,提示用户输入信用卡号码,然后根据这里对各卡种格式的定义,用 printf 报告它是否是有效的 American Express、MasterCard 或 Visa 卡号。为了能自动测试你的代码,我们要求程序最后一行输出必须是 AMEX\nMASTERCARD\nVISA\nINVALID\n,不能多也不能少。为简化起见,你可以假设用户输入完全由数字组成(也就是不包含真实卡片上可能印有的连字符),并且没有前导零。但不要假设用户输入一定能放进 int!最好使用 CS50 库中的 get_long 来获取用户输入。(为什么?)

下面展示了当输入一个有效信用卡号码(不带连字符)时,你的程序应有的表现。

$ ./credit
Number: 4003600000000014
VISA

另外,get_long 本身会拒绝连字符以及其他非数字输入:

$ ./credit
Number: 4003-6000-0000-0014
Number: foo
Number: 4003600000000014
VISA

但需要由你来捕获那些不是信用卡号码的输入(例如电话号码),即使它们是数字:

$ ./credit
Number: 6176292929
INVALID

用大量有效和无效的输入来测试你的程序。(我们当然会这样做!)以下是 PayPal 推荐用于测试的一些卡号

如果你的程序在某些输入上表现不正确(或根本无法编译),那就是时候调试了!

讲解视频

如何测试你的代码

你也可以执行下面的命令,使用 check50 来评估代码的正确性。但请务必自己编译并测试它!

check50 cs50/problems/2026/x/credit

执行下面的命令,使用 style50.

style50 credit.c

如何提交

在终端中执行以下命令提交你的作业。

submit50 cs50/problems/2026/x/credit