I'm very new to coding and one of the assignment is to program Luhn's Algorithm. After searching on the internet, everyone's solution looks so different and foreign :( so I don't know where the problem is with my solution. Any help is appreciated!
int main(void)
{
// get card number from user
long number;
do
{
number = get_long("Number: ");
} while (number < 0);
// isolate digits of card number
int digit;
int product;
int sum;
int totalSum;
int counter;
for (counter = 1; number > 9; counter++) {
for (int i = 1; number > 9; i = i * -1) {
digit = number % 10;
// isolate digits that need to be multiplied by 2
if (i == 1) {
product = digit * 2;
// add products' digits
if (product > 9) {
sum = (product % 10) + 1;
}
}
// add sum of digits that weren't multiplied by 2
totalSum = product + sum + digit;
// update "new" number
number = (number - digit) / 10;
}
}
// checksum
int check = totalSum % 10;
if (check != 0) {
printf("INVALID\n");
} else {
printf("VALID\n");
}
}
There are a number of errors in your code, most having to do with how you use variables.
You use totalSum without ever initializing it, which means it can start with any random value!
You add both product and sum to totalSum every time, but you only update their values when some condition applies.
This means at least half the time (maybe more) you add old values you already added previously.
Your loops exit when number is 9 or less, meaning you never check the leftmost (highest) digit of number.
As the comments suggested, you should read the pseudo code in Wikipedia, look carefully what they put in each variable, and what they sum and multiply.
Related
I'm trying to code a program that can tell apart real and fake credit card numbers using Luhn's algorithm in C, which is
Multiply every other digit by 2, starting with the number’s
second-to-last digit, and then add those products’ digits together.
Add the sum to the sum of the digits that weren’t multiplied by 2.
If the total’s last digit is 0 (or, put more formally, if the total
modulo 10 is congruent to 0), the number is valid!
Then I coded something like this (I already declared all the functions at the top and included all the necessary libraries)
//Luhn's Algorithm
int luhn(long z)
{
int c;
return c = (sumall(z)-sumodd(z)) * 2 + sumaodd(z);
}
//sum of digits in odd position starting from the end
int sumodd(long x)
{
int a;
while(x)
{
a = a + x % 10;
x /= 100;
}
return a;
}
//sum of all digits
int sumall(long y)
{
int b;
while(y)
{
b = b + y % 10;
y /= 10;
}
return b;
}
But somehow it always gives out the wrong answer even though there's no error or bug detected. I came to notice that it works fine when my variable z stands alone, but when it's used multiple times in the same line of code with different functions, their values get messed up (in function luhn). I'm writing this to ask for any fix I can make to make my code run correctly as I intended.
I'd appreciate any help as I'm very new to this, and I'm not a native English speaker so I may have messed up some technical terms, but I hope you'd be able to understand my concerns.
sumall is wrong.
It should be sumeven from:
Add the sum to the sum of the digits that weren’t multiplied by 2.
Your sumall is summing all digits instead of the non-odd (i.e. even) digits.
You should do the * 2 inside sumodd as it should not be applied to the other [even] sum. And, it should be applied to the individual digits [vs the total sum].
Let's start with a proper definition from https://en.wikipedia.org/wiki/Luhn_algorithm
The check digit is computed as follows:
If the number already contains the check digit, drop that digit to form the "payload." The check digit is most often the last digit.
With the payload, start from the rightmost digit. Moving left, double the value of every second digit (including the rightmost digit).
Sum the digits of the resulting value in each position (using the original value where a digit did not get doubled in the previous step).
The check digit is calculated by 10 − ( s mod 10 )
Note that if we have a credit card of 9x where x is the check digit, then the payload is 9.
The correct [odd] sum for that digit is: 9 * 2 --> 18 --> 1 + 8 --> 9
But, sumodd(9x) * 2 --> 9 * 2 --> 18
Here's what I came up with:
// digsum -- calculate sum of digits
static inline int
digsum(int digcur)
{
int sum = 0;
for (; digcur != 0; digcur /= 10)
sum += digcur % 10;
return sum;
}
// luhn -- luhn's algorithm using digits array
int
luhn(long z)
{
char digits[16] = { 0 };
// get check digit and remove from "payload"
int check_expected = z % 10;
z /= 10;
// split into digits (we use little-endian)
int digcnt = 0;
for (digcnt = 0; z != 0; ++digcnt, z /= 10)
digits[digcnt] = z % 10;
int sum = 0;
for (int digidx = 0; digidx < digcnt; ++digidx) {
int digcur = digits[digidx];
if ((digidx & 1) == 0)
sum += digsum(digcur * 2);
else
sum += digcur;
}
int check_actual = 10 - (sum % 10);
return (check_actual == check_expected);
}
// luhn -- luhn's algorithm using long directly
int
luhn2(long z)
{
// get check digit and remove from "payload"
int check_expected = z % 10;
z /= 10;
int sum = 0;
for (int digidx = 0; z != 0; ++digidx, z /= 10) {
int digcur = z % 10;
if ((digidx & 1) == 0)
sum += digsum(digcur * 2);
else
sum += digcur;
}
int check_actual = 10 - (sum % 10);
return (check_actual == check_expected);
}
You've invoked undefined behavior by not initializing a few local variables in your functions, for instance you can remove your undefined behaviour in sumodd() by initializing a to zero like so:
//sum of digits in odd position starting from the end
int sumodd(long x)
{
int a = 0; //Initialize
while(x)
{
a += x % 10; //You can "a += b" instead of "a = a + b"
x /= 100;
}
return a;
}
It's also important to note that long is only required to be a minimum of 4-bytes wide, so it is not guaranteed to be wide enough to represent a decimal-16-digit-integer. Using long long solves this problem.
Alternatively you may find this problem much easier to solve by treating your credit card number as a char[] instead of an integer type altogether, for instance if we assume a 16-digit credit card number:
int luhn(long long z){
char number[16]; //Convert CC number to array of digits and store them here
for(int c = 0; c < 16; ++c){
number[c] = z % 10; //Last digit is at number[0], first digit is at number[15]
z /= 10;
}
int sum = 0;
for(int c = 0; c < 16; c += 2){
sum += number[c] + number[c + 1] * 2; //Sum the even digits and the doubled odd digits
}
return sum;
}
...and you could skip the long long to char[] translation part altogether if you treat the credit card number as an array of digits in the whole program
This expression:
(sumall(z)-sumodd(z)) * 2 + sumall(z);
Should be:
((sumall(z)-sumodd(z)) * 2 + sumodd(z))%10;
Based on your own definition.
But how about:
(sumall(z) * 2 - sumodd(z))%10
If you're trying to be smart and base off sumall(). You don't need to call anything twice.
Also you don't initialise your local variables. You must assign variables values before using them in C.
Also you don't need the local variable c in the luhn() function. It's harmless but unnecessary.
As others mention in a real-world application we can't recommend enough that such 'codes' are held in a character array. The amount of grief caused by people using integer types to represent digit sequence 'codes' and identifiers is vast. Unless a variable represents a numerical quantity of something, don't represent it as an arithmetic type. More issue has been caused in my career by that error than people trying to use double to represent monetary amounts.
#include <stdio.h>
//sum of digits in odd position starting from the end
int sumodd(long x)
{
int a=0;
while(x)
{
a = a + x % 10;
x /= 100;
}
return a;
}
//sum of all digits
int sumall(long y)
{
int b=0;
while(y)
{
b = b + y % 10;
y /= 10;
}
return b;
}
//Luhn's Algorithm
int luhn(long z)
{
return (sumall(z)*2-sumodd(z))%10;
}
int check_luhn(long y,int expect){
int result=luhn(y);
if(result==expect){
return 0;
}
return 1;
}
int check_sumodd(long y,int expect){
int result=sumodd(y);
if(result==expect){
return 0;
}
return 1;
}
int check_sumall(long y,int expect){
int result=sumall(y);
if(result==expect){
return 0;
}
return 1;
}
int main(void) {
int errors=0;
errors+=check_sumall(1,1);
errors+=check_sumall(12,3);
errors+=check_sumall(123456789L,45);
errors+=check_sumall(4273391,4+2+7+3+3+9+1);
errors+=check_sumodd(1,1);
errors+=check_sumodd(91,1);
errors+=check_sumodd(791,8);
errors+=check_sumodd(1213191,1+1+1+1);
errors+=check_sumodd(4273391,15);
errors+=check_luhn(1234567890,((9+7+5+3+1)*2+(0+8+6+4+2))%10);
errors+=check_luhn(9264567897,((9+7+5+6+9)*2+(7+8+6+4+2))%10);
if(errors!=0){
printf("*ERRORS*\n");
}else{
printf("Success\n");
}
return 0;
}
I need to solve the problem on the link below, using Luhn's algorithm and what we have learned so far in CS50 (no arrays).
My program compiles but it doesn't identify the card type. What am I doing wrong?
Many thanks in advance!
Problem: https://cs50.harvard.edu/x/2020/psets/1/credit/#:~:text=check50%20cs50/problems/2020/x/credit
/* This program checks if a bank card number is syntactically valid, using Luhn's algorithm */
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
long long n, temp;
int digit, last, first, product;
int lenght = 0;
int sumOdd, sumEven;
// Prompt user for input as long as the inputted number is equal or smaller than zero
do
{
n = get_long_long("Enter your credit card number without special characters:\n");
} while (n < 0);
// Count the number of digits
while (n > 0)
{
n = n/10;
lenght++;
}
// Check if the number's length is valid
if (lenght != 13 && lenght != 15 && lenght != 16)
{
printf("INVALID");
}
// Find the last digit and add it to the even sum
while (n > 0)
{
last = n % 10;
temp = n - last;
sumEven = sumEven + last;
}
/* Starting with second-to-last digit, multiply every other digit by 2. Add those
products together and then add the sum to the sum of the digits that weren't multiplied by 2 */
while (n > 0)
{
digit = n % 10;
temp = n/10;
if (lenght % 2 == 0)
{
product = digit * 2;
sumOdd = sumOdd + product;
} else
{
sumEven = sumEven + digit;
}
// If the last digit of the sum of sums is zero print the number. Else print INVALID.
if (sumOdd + sumEven % 10 != 0)
{
printf("INVALID\n");
}
else
{
printf("%lld\n", n);
}
// Identify the user's card type as per CS50 Credit instructions. Cards commencing with 3 are AMEX, with 5 MasterCard and with 4, VISA.
while (first >= 10)
{
first = n;
first = first / 10;
if (first == 3)
{
printf("AMEX\n");
}
if (first == 5)
{
printf("MASTERCARD\n");
}
if (first == 1)
{
printf ("VISA\n");
}
}
}
}
You have several consecutive while blocks
while (n>0){
// some code
}
while (n>0){
// some code
}
Your program will only exit the first loop when n is no longer larger than 0. When it reaches the next while loop n will still not be larger than 0 so the body of the next while loop will never be entered. Large chunks of your code are not getting executed.
I'm currently working through CS50x's 2018 programme and I'm completing the 'credit' part of pset1. We're supposed to be executing Luhn's algorithm, but I can't get it to work.
Luhn's algorithm is an algorithm that uses checksum to determine whether or not a credit card is valid. The input should be a credit card number, or something that could be a credit card number. It then takes the number, multiplies every other digit by 2 (or every even digit as implied in my code), sums them, then takes the remaining digits (the odd ones) and sums them with the first sum. The algorithm then states that if the last digit of the summed value is 0 then the number is valid (or if it divides by ten perfectly). If the answer isn't 0 then it should print "INVALID" to the user. If it is 0 the code should then analyse whether or not the number could be a valid card. We are supposed to use American Express, Visa and MasterCard as our potential cards. If it's AMEX it will be 15 digits and start with 34 or 37, MASTERCARD is 16 digits and starts with 51, 52, 53, 54 or 55, and VISA is either 13 or 16 digits and starts with 4. The expected output is either one of the card companies or "INVALID" if the number doesn't fit that criteria.
Instead of this, my code simply runs until overflow after a number is submitted. I know my logic must be flawed somewhere, but I have no idea where I've gone wrong. My code is as follows:
//Checks credit card no. to see if valid
#include <cs50.h>
#include <stdio.h>
#include <math.h>
int main(void)
{
//Get card no. from user
long long n;
do
{
n = get_long_long("Card Number: ");
}
while (n < 0);
//Define variables for checksum process
int odd = 0;
int even = 0;
long long temp_n = n;
int sum_a = 0;
int sum_b = 0;
int counter = 0;
//Execute checksum until all of n assessed
while (temp_n >= 0)
{
//Take final digit, add up, then update temp no., increase digit counter by 1
odd = temp_n % 10;
sum_b = sum_b + odd;
temp_n = (temp_n - odd) / 10;
counter++;
//Take final digit (which is an even digit of n), multiply by 2 and add up, update temp no., increase counter by 1
even = temp_n % 10;
sum_a = sum_a + 2 * even;
temp_n = (temp_n - even) / 10;
counter++;
}
//Validate checksum
int test = (sum_a + sum_b) % 10;
//Return results
if (test == 0)
{
if (counter == 16 && odd == 5 && (even == 1 || even == 2 || even == 3 || even == 4 || even == 5))
{
printf("MASTERCARD\n");
}
else if ((counter == 16 || counter == 13) && odd == 4)
{
printf("VISA\n");
}
else if (counter == 15 && odd == 3 && (even == 4 || even == 7))
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
}
}
else
{
printf("INVALID\n");
}
}
I would appreciate any help I can get about where I've gone wrong. Thank you in advance.
while (temp_n >= 0)
{
//Take final digit, add up, then update temp no., increase digit counter by 1
odd = temp_n % 10;
sum_b = sum_b + odd;
temp_n = (temp_n - odd) / 10;
counter++;
//Take final digit (which is an even digit of n), multiply by 2 and add up, update temp no., increase counter by 1
even = temp_n % 10;
sum_a = sum_a + 2 * even;
temp_n = (temp_n - even) / 10;
counter++;
}
This assumes that input will always have even number of digits. Take a bool flag = true; and change it to:
while (temp_n >= 0)
{
//Take final digit, add up, then update temp no., increase digit counter by 1
if(flag){
odd = temp_n % 10;
sum_b = sum_b + odd;
temp_n = (temp_n - odd) / 10;
counter++;
flag = !flag;}
//Take final digit (which is an even digit of n), multiply by 2 and add up, update temp no., increase counter by 1
else{
even = temp_n % 10;
sum_a = sum_a + 2 * even;
temp_n = (temp_n - even) / 10;
counter++;
flag = !flag;}
}
I've been trying to create a program that can check if a credit card number is valid or not based on Hans Peter Luhn's algorithm. However, I can only get it to work for some inputs.
// Loop through every digit in the card number
for ( int i = 0; i < intlen (num); ++i )
{
nextDigit = getDigit (num, i);
// If every other number...
if ( i % 2 )
{
nextDigit *= 2;
// ...times by two and add the individual digits to the total
for ( int j = 0; j < intlen (nextDigit); ++j )
{
total += getDigit (nextDigit, j);
}
}
else
{
total += nextDigit;
}
}
When I use the AMEX card number 378282246310005 it works fine and tells the user it's valid. However, once I try the VISA card number 4012888888881881 it says it's invalid. I tried to do a sanity check and do it manually to see if my program was wrong but I deduced the same result. These card number were taken from the Paypal test credit card numbers page so I know they are valid.
So what am I doing wrong?
To clarify the details by the program, if total modulo 10 == 0 then the card number is valid.
Functions called:
// Function to return length (number of digits) of an int
int intlen (long long n)
{
int len = 1;
// While there is more than 1 digit...
while ( abs (n) > 9 )
{
// ...discard leading digits and add 1 to len
n /= 10;
++len;
}
return len;
}
// Function to return a digit in an integer at a specified index
short getDigit (long long num, int index)
{
// Calculating position of digit in integer
int pos = intlen (num) - index;
// Discard numbers after selected digit
while ( pos > 1 )
{
num /= 10;
--pos;
}
// Return right-most digit i.e. selected digit
return num % 10;
}
You'll want to change i % 2 to i % 2 == intlen (num) % 2 or similar; you should double every second digit, but starting from the right; i.e. excluding the final check digit:
From the rightmost digit, which is the check digit, moving left, double the value of every second digit; …
The reason the AMEX number you tried validated anyway is because it's an odd number of digits; the same digits get doubled regardless of whether you skip from the front or the back.
While I was looking at this to find the bug, I re-wrote the program to make it a bit simpler. As a side-effect this will be much faster.
We need to grab digits from the right anyway. We don't even need to count the digits; just keep pulling off the right-most digit until the number becomes 0. If the number starts out as 0, the checksum is trivially 0 and the code is still correct.
I grabbed all the numbers from the test page. This seems to be correct, except for one number: 76009244561 (listed as "Dankort (PBS)" in the test page). I tried this number with the Python code from the Wikipedia page, and again this number is rejected. I don't know why this number is different from the others.
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
bool check_one(long long num)
{
int checksum = 0;
int i = 1;
for (int i = 1; num; num /= 10, ++i)
{
int d = num % 10;
if (i % 2 == 0)
{
// even digit: double and add digits of doubled value
d *= 2;
if (d < 10)
{
// only one digit: we doubled a 0-4 so number is 0-8
checksum += d;
}
else
{
// two digits: we doubled a 5-9 so number is 10-18
checksum += (d % 10);
checksum += (d / 10);
}
}
else
{
// odd digit: just add
checksum += d;
}
}
return (checksum % 10) == 0;
}
static long long const valid_nums[] =
{
378282246310005,
371449635398431,
378734493671000,
5610591081018250,
30569309025904,
38520000023237,
6011111111111117,
6011000990139424,
3530111333300000,
3566002020360505,
5555555555554444,
5105105105105100,
4111111111111111,
4012888888881881,
4222222222222,
76009244561,
5019717010103742,
6331101999990016,
};
static size_t len_valid_nums = sizeof(valid_nums) / sizeof(valid_nums[0]);
static long long const non_valid_nums[] =
{
378282246310006, // add 1 to valid
371449635398432,
378734493671001,
5610591081018205, // swap last two digits
30569309025940,
38520000023273,
601111111111111, // delete last digit
601100099013942,
353011133330000,
};
static size_t len_non_valid_nums =
(sizeof(non_valid_nums) / sizeof(non_valid_nums[0]));
main()
{
bool f;
for (int i = 0; i < len_valid_nums; ++i)
{
long long num = valid_nums[i];
f = check_one(num);
if (!f)
{
printf("Number %lld considered invalid but should be valid\n", num);
}
}
for (int i = 0; i < len_non_valid_nums; ++i)
{
long long num = non_valid_nums[i];
f = check_one(num);
if (f)
{
printf("Number %lld considered valid but should be invalid\n", num);
}
}
}
I am trying to create a code that will take the number 2 to 100, and test each for the collatz conjecture.
The goal is that for each number, if it is even, divide it by two, and if it is odd, then multiply it by 3 and add 1.
It prints each step, and each number should stop testing if it reaches 1. Why doesn't it work?
#include <stdio.h>
int main()
{
int number, position;
position == 2;
number == 2;
while (position <= 100)
{
while (number != 1)
{
if (number % 2 == 0)
{
number = number/2;
printf("%d\n", number);
}
else if (number % 2 != 0)
{
number = number*3;
number = number + 1;
printf("%d\n", number);
}
}
position = position + 1;
number = position;
}
}
It prints recurring Os
Fix the == vs =:
position = 2;
number = 2;
Also, the else if is unnecessary. The opposite of even is odd, so a plain else will suffice :-)
You have set position and number with a double equal == (Comparision Operator) instead of using single equal = (Assignment Operator) so that the algorithm is comparing them instead of assigning a value.
The assignment should look like this:
position = 2;
number = 2;
Also you can do it when you first define them:
int number=2, position=2;
Apart from that the code is correct, the only thing to highlight is that you don´t need to use else if because it can just be even or odd so a single else would be enough.
Hope I´ve helped :-)