尝试在C语言中实现Luhn算法

5

我正在尝试用C语言实现Luhn算法来检查信用卡的有效性,对于那些不知道的人... 就是这个:

  • 从数字的倒数第二位开始,每隔一个数字乘以2,然后将这些乘积的数字相加。

  • 将上述求和结果加上所有未被乘以2的数字的总和。

  • 如果总和的个位数为0(或更正式地说,如果总和模10等于0),则该数字是有效的!


为了实现这一点,我循环遍历整个数字,如果我所处的数字位置具有模2等于0,则将其乘以2并加到名为totalEven的变量中。

如果不是这种情况,我会将我所在的数字添加到totalOdd中,而不进行乘法运算。

然后我会递增位置一,并检查其他数字,直到达到16(卡片的最大数字)。

稍后我会添加两个变量并检查总数模10是否等于0。 如果等于0,则表示信用卡号码正确,否则是错误的。

以下是代码:

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

//list of variables

   //is the card valid
   bool isValid = true;
   // the creditcard number
   long input;
   //mod stands for modules, and is used to single out each number as seen later
   int mod = 10;
   //the location at which number I am checking
   int place = 1;
   //num is the number I am checking that has been singled out
   int num = 0;
   //total of numbers * 2 located at locations numbered with even numbers
   int totalEven = 0;
   //total of numbers located at locations numbered with odd numbers
   int totalOdd = 0;

     //gets input and stores it in well.. input
     input = get_long("Number: ");
      
      // a formula to single out a number, starting with the ones and then as you can see, mod is muliplied by 10 to go over the second number.

      num = ((input % mod) - (input % (mod /10))) / (mod/10);
      
      //loops 16 times
      for(int i = 0; i < 16; i++)
      {
          // if the place is even execute below
          if(place % 2 == 0)
          {
              totalEven = totalEven + num * 2;
          }   
          //else do this
          else if (place % 2 != 0)
          {
             totalOdd = totalOdd + num; 
          }
          //moves to the next number
          mod = mod * 10;
          place++;
      }
      
      //fufils the last step of the algorithm

      if((totalEven + totalOdd) % 10 == 0 )
      {
          isValid = true;
      }
      else
      {
          isValid = false;
      }

问题是即使信用卡号码正确,这个代码块也会给我无效或!isValid的错误提示,而且我已经检查了我的“公式”,它仍然能够很好地工作...

我完全不知道该怎么办...我只是一个谦卑的业余爱好者,请不要因为上面的畸形代码而嘲笑我。

这里是完整版本的代码

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


long power();


int main(void)
{
    //AMERX 15 STRT 34 OR 37
    //MC 16 STRT 51, 52, 53, 54, 55
    //VZA 13 OR 16 STRT 4

   long input;
   bool isValid = true;
   string type;
   int mod = 10;
   int place = 1;
   int num = 0;
   int totalEven = 0;
   int totalOdd = 0;

   do
   {
      input = get_long("Number: ");
      

   }

   while(input < 0);
   
      
      for(int i = 0; i < 16; i++)
      {
          num = ((input % mod) - (input % (mod /10))) / (mod/10);
          if(place % 2 == 0)
          {
              totalEven = totalEven + num * 2;
          }
          else
          {
             totalOdd = totalOdd + num; 
          }
          
          mod = mod * 10;
          place++;
      }
      
      if((totalEven + totalOdd) % 10 == 0 )
      {
          isValid = true;
      }
      else
      {
          isValid = false;
          
          printf("%i , %i", totalEven, totalOdd);
      }
   if (isValid == true){
   if((input < (38 * power(10, 13)) && input >=(37 * power(10, 13))) || (input < (35 * power(10,13)) && input >= (34 * power(10, 13)))) 
   {
       type = "AMEX\n";
   }
   else if(input >= (51 * power(10, 14)) && input < (56 * power(10, 14)))
   {
       type = "MASTERCARD\n";
   }
   else if((input < (5 * power(10, 12)) && input >= (4 * power(10, 12))) || (input < (5 * power(10, 15)) && input >= (4 * power(10, 15))))
   {
       type = "VISA\n";
   } 
       else{
       type = "error\n";
   }
}
   else
   {
       type = "INVALID\n";
   }
   

    if((totalEven + totalOdd) % 10 == 0 )
    {
      isValid = true;
    }
    else
    {
      isValid = false;
    }
      
    printf("%s", type);

}





long power(int n, int p)
{
    long result = 1;
    for(int i = 0; i<p; i++)
    {
        result = result * n;
    }
    return result;


3
将信用卡号视为数字串,而非数字本身。这样更容易处理,并且可以更轻松地检查是否有16个数字。在 [tag:cs50] 标签中有许多相关问题可供参考,其中并非全部都带有 [tag:luhn] 标签。请搜索 '[cs50] luhn'。 - Jonathan Leffler
1
mod/10 就是 10/10,也就是 1,所以我怀疑 num = ((input % mod) - (input % (mod /10))) / (mod/10); 中有错误。虽然速度较慢,但我同意字符串操作会更容易理解。 - erik258
你永远不会在循环内部获取每个数字。 - Some programmer dude
4
你的计算机中的long类型是否足够大,能够容纳16位数字?尝试使用#include <limits.h>printf("%ld\n", LONG_MAX); - pmg
1
你的代码太过复杂。在需要在循环中计算余数的地方,使用数字数组代替数字。 - Support Ukraine
显示剩余6条评论
3个回答

1

当我查看你的代码时,发现了一些错误需要指出。

  1. 你忘记了在代码中声明了string type,所以需要加上 #include <string.h>
  2. input = get_long("Number: ");应该有自己的do-while循环,以防用户输入字母或错误数字。
  3. if(place % 2 == 0){ totalEven = totalEven + num * 2; } else if (place % 2 != 0){ totalEven = totalEven + num; }第二部分应该是totalOdd = totalOdd + num
  4. totalEven = totalEven + num * 2同时正确和错误。只有当数字乘以2小于10时才有效。如果num * 2 >= 10,例如num = 6,则6 * 2为12,然后是1 + 2 + totalEven。
  5. num = ((input % mod) - (input % (mod /10))) / (mod/10);这应该在第一个for loop中。
  6. #include <math.h>中,有一个名为pow的幂函数,与您的power()函数完全相同。

1
另外,string类型不在string.h中,但是它是一个非标准(且不建议使用)的类型,在包含的cs50.h中。而pow()函数则完全不同 - 它适用于浮点数值。甚至没有必要使用它,有更好的方法来分解一个数字。 - Weather Vane
我明白了,我不知道string不是string.h的一部分。谢谢你提供关于pow()的信息。 - Manav Dubey
@WeatherVane 是的,现在回想起来,我本可以将整个过程转化为更简洁的程序...也许将整个 int 转换为字符串,然后像数组一样使用它来验证输入的数字会更好... - someGuy5864
@someGuy5864 最好从一开始就不要将卡号、电话号码、PIN等作为整数。 "Number"并不意味着"integer"。最好将其视为数字字符串处理。 - Weather Vane

1
我不是 Luhn 算法的专家,但当我阅读 https://en.wikipedia.org/wiki/Luhn_algorithm 时,似乎您没有正确实现。
引自 https://en.wikipedia.org/wiki/Luhn_algorithm 的摘录:
从最右边(不包括校验位)开始向左移动,将每个第二位数的值加倍。校验位既不被加倍也不被包括在此计算中;第一个加倍的数字是校验位左侧紧邻的数字。如果这个加倍操作的结果大于9(例如,8×2=16),则将结果的数字相加(例如,16:1+6=7,18:1+8=9),或者可以通过从该结果减去9来得到相同的最终结果(例如,16:16-9=7,18:18-9=9)。
我没有看到您的代码中有任何处理加粗部分的地方。
与其这样做:
totalEven = totalEven + num * 2;

我认为你需要。
int tmp = num * 2;
if (tmp > 9) tmp = tmp - 9;
totalEven = totalEven + tmp;

说实话,我认为您将实现变得比需要的更加复杂,因为您将输入存储为数字。您可以使用数字数组代替数字。

也就是说,不是

long input = 1122334455667788

使用
int digits[] = {8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1};
// Notice that index zero is the rightmost digit

这样算法就简单多了:

// Double every second element and check for overflow
for (idx = 1; idx < 16; idx += 2)
{
    digits[idx] = 2 * digits[idx];
    if (digits[idx] > 9) digits[idx] = digits[idx] - 9;
}

// Calculate the sum
sum = 0;
for (idx = 0; idx < 16; ++idx)
{
    sum = sum + digits[idx];
}

如果你必须将输入作为数字接收,请先调用一个将数字转换为数字数组的函数。您可以在此处找到许多将数字转换为数字数组的示例。这里 将整数转换为数字数组 只是众多示例之一。


谢谢伙计的建议,我使用了你建议的方法与我的方法结合起来重写了代码,结果它成功了!! - someGuy5864

0

注意:由于问题似乎与CS50X库相关,因此我已经使用了该库。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接