Попытка реализовать алгоритм Луна на C

Я пытаюсь реализовать алгоритм Луна на языке C, чтобы проверить действительность кредитной карты, для тех, кто не знает ... вот оно:

  • Умножьте все остальные цифры на 2, начиная с предпоследней цифры номера, а затем сложите цифры этих продуктов.

  • Добавьте сумму к сумме цифр, которые не были умножены на 2.

  • Если последняя цифра суммы равна 0 (или, выражаясь более формально, если сумма
    по модулю 10 сравнима с 0), число действительно!


и чтобы реализовать это, я перебрал все число, и если бы в числовом месте, в котором я находился, был модуль 2, равный 0, я бы умножил его на два и добавил бы к переменной с именем totalEven.

если бы это было не так, я бы добавил число, в котором я был, к totalOdd без умножения.

Затем я увеличивал бы это место на единицу и проверял остальные числа, пока не достигну 16 (максимальное количество цифр для карты).

Позже я бы добавил обе переменные и проверил, равняется ли сумма по модулю десять нулю. Если это означает, что номер кредитной карты правильный, иначе это ложь.

вот код:

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


person someGuy5864    schedule 07.08.2020    source источник
comment
Считайте номера кредитных карт цепочкой цифр, а не числом. Их легче обрабатывать. И вы можете легче проверить, что есть 16 цифр. В теге cs50 есть много связанных вопросов, которые могут вам помочь - не все из них будут иметь luhn. Найдите "[cs50] luhn".   -  person Jonathan Leffler    schedule 07.08.2020
comment
mod/10 составляет всего 10/10, а это всего лишь 1, поэтому я подозреваю, что есть ошибка в `num = ((input% mod) - (input% (mod / 10))) / (mod / 10);`. Хотя это значительно медленнее, я считаю, что манипуляциями со струнами будет легче осознать.   -  person Daniel Farrell    schedule 07.08.2020
comment
Вы никогда не получите каждую цифру внутри цикла.   -  person Some programmer dude    schedule 07.08.2020
comment
Достаточно ли long на вашем компьютере для 16 цифр? Попробуйте #include <limits.h> и printf("%ld\n", LONG_MAX);   -  person pmg    schedule 07.08.2020
comment
Предложение по отладке: если вы настроены на использование модуля и математики для извлечения цифр, сначала запустите этот алгоритм и напишите программу, которая его проверяет, принимая набор входных данных и проверяя каждый выход. Затем разверните оттуда, чтобы получить все остальные цифры для каждого числа. Как только это сработает, доработайте алгоритм Люна тем кодом, который, как вы знаете, работает.   -  person Daniel Farrell    schedule 07.08.2020
comment
Это безумие, поскольку номер карты изначально вводился цифрами. Число не означает автоматически целое число, что также может быть подтверждено номерами домов, такими как "221b Baker Street", и телефонными номерами, такими как "0123456789", где ведущее начало 0 было бы потеряно.   -  person Weather Vane    schedule 07.08.2020
comment
@DanielFarrel, я знаю, что 10/10 равно 1, но это должно произойти ... например, если input было 1069, а mod было 10, тогда num было бы .... `num = ((1069% 10) - (1069% (10/10))) / (10/10); число = (9 - (1069% 1) / 1); число = (9 - 0/1); число = 9; `тогда, как вы можете видеть, если вы посмотрите на код более глубоко, mod умножается на 10, чтобы перейти к следующей цифре   -  person someGuy5864    schedule 07.08.2020
comment
@Someprogrammerdude можно уточнить?   -  person someGuy5864    schedule 07.08.2020
comment
ах, тогда проблема в том, что `num = ((input% mod) - (input% (mod / 10))) / (mod / 10); `должен быть в for цикле?   -  person Daniel Farrell    schedule 07.08.2020
comment
@DanielFarrell да, он должен быть там, и цикл 16 раз, перемещая 16 цифр   -  person someGuy5864    schedule 07.08.2020
comment
Ваш код слишком сложен. Используйте массив цифр вместо числа, где вам нужно вычислить остаток внутри цикла.   -  person 4386427    schedule 07.08.2020


Ответы (3)


Я не специалист по алгоритму Луна, но когда я прочитал 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];
}

Если вы должны получить ввод как число, начните с вызова функции, которая преобразует число в массив цифр. Вы можете найти много-много примеров того, как это преобразование выполняется здесь, на SO. Здесь Преобразование целого числа в массив цифр - лишь один из многих примеров.

person 4386427    schedule 07.08.2020
comment
Спасибо за подсказку, я переписал код, используя гибрид того, что вы предложили, и моего метода, и это сработало !! - person someGuy5864; 08.08.2020

Когда я смотрел ваш код, я хотел указать на некоторые ошибки.

  1. Вы забыли: #include <string.h> как вы объявили string type в коде.
  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() функция.
person Manav Dubey    schedule 07.08.2020
comment
В сторону: тип string отсутствует в string.h, но является нестандартным (и не рекомендуется) типом в cs50.h, который был включен. А pow() совсем другое дело - он работает со значениями с плавающей запятой. В этом даже нет необходимости, есть способы лучше разложить число. - person Weather Vane; 07.08.2020
comment
Понятно, я не знал, что string не был частью string.h. Спасибо за информацию о pow(). - person Manav Dubey; 07.08.2020
comment
@WeatherVane да, оглядываясь на это сейчас, я мог бы превратить весь процесс в более лаконичную программу ... может быть, превратить весь int в строку, а затем использовать ее как массив для проверки введенного числа, было бы намного лучше ... - person someGuy5864; 17.11.2020
comment
@ someGuy5864 лучше никогда не использовать номер карты, номер телефона, PIN-код и т. д. в виде целого числа. Число не означает целое число. Лучше всего рассматривать как строку цифр. - person Weather Vane; 17.11.2020

Внимание! Я воспользовался библиотекой 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;
}
person Aarush Aggarwal    schedule 13.11.2020