CS50 - Validating Credit Card Numbers in C Language [duplicate] - c

This is the credit problem from CS50x
I wrote this code on Windows and it didn't work there. After entering a 15 digit card number on windows it wasn't accepting the number and was prompting me again and again.
But when i pasted the same code on CS50's cloudspace in VSCode which is on linux, the code runs perfectly fine there.
The issue seems to be in the get_long function, it wasnt letting me input a 15 digit card number on windows.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number;
number = get_long("Enter your card number: ");
//Check length of number
int i = 0;
long length = number;
while (length > 0 )
{
length = length / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
}
else
{
int sum1 = 0;
int sum2 = 0;
long x = number;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
do
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
} while (x > 0 );
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
}
else
{
long y = number;
int j = 0;
do
{
y = y / 10;
j++;
} while (y > 100 );
if ( y / 10 == 4)
{
printf ("VISA");
}
else if ((y / 10 == 5) && (0 < y % 10 && y % 10 < 6))
{
printf("MASTERCARD");
}
else if ((y / 10 == 3) && (y % 10 ==4 || y % 10 == 7) )
{
printf("AMEX");
}
}
}
}

Your code works because on your Linux platform, type long happens to be an 8-byte, 64-bit type that can easily handle integers matching even 15- or 16-digit credit card numbers.
Your code fails on Windows because there, type long is a 32-bit type, not capable of handling numbers greater than 2,147,483,647 or 4,294,967,295 (depending on whether signed or unsigned).
This discrepancy between the two platforms is not a bug: C's short, int, and long types have always been somewhat loosely defined. The requirement for type long is, in effect, that it have at least 32 bits, which on both platforms it certainly does.
It was rather unfair and misleading for your assignment to have said "Best to use get_long from CS50’s library to get users’ input". This is a nonportable programming practice for this task.
In portable C, type long long is guaranteed to be at least 64 bits long, so it would work reliably here. I do not know if CS50's library has a get_long_long function. (Update: per comments below, it does.)
In the long run, it's generally easier to deal with credit card numbers as strings, not integers. But during week 1 of CS50, students haven't learned about strings yet.

The discussion in the comments of 32-bit v. 64-bit should be educational for you, and I hope that you can integrate that information into your program.
Compliments to the layout and logic of your beginner code. Very clear and very easy to read and follow (including appropriate comments.) Keep this up and you will become a fine programmer!
I've revised your code (without appreciably changing any of its logic) to suggest how it may be slightly improved. The following is offered for your consideration. It is your code only slightly modified. It's up to you to consider if you want to apply any of these "alterations" to your code.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number = get_long("Enter A card number: ");
long x = number; // will reload and reuse x several times
int i = 0; // handy to have around
// Check length of number
while (x > 0 )
{
x = x / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
return 1; // early termination saves one level of indentation
}
int sum1 = 0;
int sum2 = 0;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
x = number; // reload
while ( x > 0 )
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
}
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
return 1; // early termination
}
x = number; // reload
while ( x > 99 ) // NB! subtle change!!
{
x = x / 10; // shrink until 10 <= x <= 99
}
if ( 40 <= x && x <= 49 ) // check this
{
printf ("VISA");
}
else if ( 50 <= x && x < 56 ) // check this
{
printf("MASTERCARD");
}
else if ( x == 34 || x == 37 ) // check this
{
printf("AMEX");
}
else
{
printf("INVALID\n");
return 1; // early termination
}
return 0;
}

The instructions for your task state the following:
But do not assume that the user’s input will fit in an int! Best to use get_long from CS50’s library to get users’ input.
This advice is misleading when using the Microsoft Windows platform.
On Microsoft Windows, the data type long has a width of only 32 bits, whereas on most other common platforms, such Linux and MacOS, it has a width of 64 bits. The ISO C standard only requires long to have a minimum width of 32 bits, so all the mentioned operating systems are complying with the standard in this respect.
A 32-bit signed integer is able to represent numbers up to 2,147,483,647, whereas a 64-bit signed integer is able to represent numbers up to 9,223,372,036,854,775,807. Therefore, a 32-bit integer data type is insufficient to represent a credit card number; you require a 64-bit integer data type.
In constrast to long, the data type long long is guaranteed to be at least 64 bits wide on all platforms.
For the reasons stated above, if you want your program to work on Microsoft Windows, you will have to use the long long data type to represent a credit card number, instead of long. You will also have to use the function get_long_long instead of get_long.

Related

Same code works on Linux but not on windows

This is the credit problem from CS50x
I wrote this code on Windows and it didn't work there. After entering a 15 digit card number on windows it wasn't accepting the number and was prompting me again and again.
But when i pasted the same code on CS50's cloudspace in VSCode which is on linux, the code runs perfectly fine there.
The issue seems to be in the get_long function, it wasnt letting me input a 15 digit card number on windows.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number;
number = get_long("Enter your card number: ");
//Check length of number
int i = 0;
long length = number;
while (length > 0 )
{
length = length / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
}
else
{
int sum1 = 0;
int sum2 = 0;
long x = number;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
do
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
} while (x > 0 );
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
}
else
{
long y = number;
int j = 0;
do
{
y = y / 10;
j++;
} while (y > 100 );
if ( y / 10 == 4)
{
printf ("VISA");
}
else if ((y / 10 == 5) && (0 < y % 10 && y % 10 < 6))
{
printf("MASTERCARD");
}
else if ((y / 10 == 3) && (y % 10 ==4 || y % 10 == 7) )
{
printf("AMEX");
}
}
}
}
Your code works because on your Linux platform, type long happens to be an 8-byte, 64-bit type that can easily handle integers matching even 15- or 16-digit credit card numbers.
Your code fails on Windows because there, type long is a 32-bit type, not capable of handling numbers greater than 2,147,483,647 or 4,294,967,295 (depending on whether signed or unsigned).
This discrepancy between the two platforms is not a bug: C's short, int, and long types have always been somewhat loosely defined. The requirement for type long is, in effect, that it have at least 32 bits, which on both platforms it certainly does.
It was rather unfair and misleading for your assignment to have said "Best to use get_long from CS50’s library to get users’ input". This is a nonportable programming practice for this task.
In portable C, type long long is guaranteed to be at least 64 bits long, so it would work reliably here. I do not know if CS50's library has a get_long_long function. (Update: per comments below, it does.)
In the long run, it's generally easier to deal with credit card numbers as strings, not integers. But during week 1 of CS50, students haven't learned about strings yet.
The discussion in the comments of 32-bit v. 64-bit should be educational for you, and I hope that you can integrate that information into your program.
Compliments to the layout and logic of your beginner code. Very clear and very easy to read and follow (including appropriate comments.) Keep this up and you will become a fine programmer!
I've revised your code (without appreciably changing any of its logic) to suggest how it may be slightly improved. The following is offered for your consideration. It is your code only slightly modified. It's up to you to consider if you want to apply any of these "alterations" to your code.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number = get_long("Enter A card number: ");
long x = number; // will reload and reuse x several times
int i = 0; // handy to have around
// Check length of number
while (x > 0 )
{
x = x / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
return 1; // early termination saves one level of indentation
}
int sum1 = 0;
int sum2 = 0;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
x = number; // reload
while ( x > 0 )
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
}
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
return 1; // early termination
}
x = number; // reload
while ( x > 99 ) // NB! subtle change!!
{
x = x / 10; // shrink until 10 <= x <= 99
}
if ( 40 <= x && x <= 49 ) // check this
{
printf ("VISA");
}
else if ( 50 <= x && x < 56 ) // check this
{
printf("MASTERCARD");
}
else if ( x == 34 || x == 37 ) // check this
{
printf("AMEX");
}
else
{
printf("INVALID\n");
return 1; // early termination
}
return 0;
}
The instructions for your task state the following:
But do not assume that the user’s input will fit in an int! Best to use get_long from CS50’s library to get users’ input.
This advice is misleading when using the Microsoft Windows platform.
On Microsoft Windows, the data type long has a width of only 32 bits, whereas on most other common platforms, such Linux and MacOS, it has a width of 64 bits. The ISO C standard only requires long to have a minimum width of 32 bits, so all the mentioned operating systems are complying with the standard in this respect.
A 32-bit signed integer is able to represent numbers up to 2,147,483,647, whereas a 64-bit signed integer is able to represent numbers up to 9,223,372,036,854,775,807. Therefore, a 32-bit integer data type is insufficient to represent a credit card number; you require a 64-bit integer data type.
In constrast to long, the data type long long is guaranteed to be at least 64 bits wide on all platforms.
For the reasons stated above, if you want your program to work on Microsoft Windows, you will have to use the long long data type to represent a credit card number, instead of long. You will also have to use the function get_long_long instead of get_long.

Failed to reuse variable in C

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

Is it possible to increment the modulo operator in later loop iterations?

I am trying to construct a simple program which adds together the digits of a long number. I attempted to do this by using a loop employing the modulo operator and some basic arithmetic. I want to increment the modulo operator by multiplying it by ten on each iteration of the loop in order to reach the next digit. I want to check if my code is correct, however, I receive errors pertaining to the lines involving the modulo operations and I'm not quite sure why.
This was my attempted construction:
{
long i = 0;
long b;
int m = 1;
do
{
long number = get_long("Number?\n");
long a = number % m;
b = number - a;
long c = b % m x 10;
long d = c / m;
{
i = i + d;
}
{
m = m x 10
}
}
while (b > 0);
printf("%ld\n", i);
}
Edit:
I made the basic error of writing "x" instead of "*". However, having fixed this, I no longer receive errors, but the program simply returns "0". Any diagnosis would be appreciated.
int main(void)
{
long i = 0;
long b;
int m = 10;
long number = get_long("Number?\n");
do
{
long a = number % m;
b = number - a;
long c = b % m * 10;
long d = c / m;
{
i = i + d;
}
{
m = m * 10;
}
}
while (b > 0);
printf("%ld\n", i);
}
For your revised code:
long c = b % m * 10;
this line will evaluate (b % m) and then multiply it by 10 because of the order of operations.
I presume what you actually want is:
long c = b % (m * 10);
Secondly, the following line determines which digit you start at:
int m = 10;
and this line determines how many digits between the ones you include in your total:
m = m * 10;
So for this configuration, it will start at the 2nd digit from the right and add every digit.
So for the number 1234, you'd get 3 + 2 + 1 = 6.
If you want to add every digit, you could set:
int m = 10;
and you'd get 4 + 3 + 2 + 1 = 10.
Alternatively, if you had used:
m = m * 10;
you'd have 3 + 1 = 4.
First, you're likely getting errors due to these lines:
long c = b % m x 10;
m = m x 10
This is because x is not a valid operator.
The multiplication operator is *:
long c = b % m * 10;
m = m * 10;
As for your approach, I would suggest, instead of changing the modulo operand, you simply divide the original number by 10 to shift it one digit each operation.
For example:
#include <stdio.h>
int main()
{
int sumofdigits = 0;
int num = 12345;
while(num > 0) {
sumofdigits += num % 10;
num /= 10;
}
printf("%d", sumofdigits);
return 0;
}
The reduced-sum of the digits of a number is the same as that number modulo 9.
Example:
#include <stdio.h>
int main(void) {
int number = 57283;
printf("%d \n", number%9);
// 5 + 7 + 2 + 8 + 3 == 25 ==> 2 + 5 == 7
// 57283 % 9 == 7
return 0;
}
If you want to use loops to get the reduced sum:
int sum_of_digits(int num)
{
int sum;
do
{
sum = 0;
while(num)
{
sum += num%10;
num /= 10;
}
num = sum;
} while (sum >9);
return sum;
}
But if you only want the simple sum of digits (one pass only):
int sum_of_digits(int num)
{
int sum = 0;
while(num)
{
sum += num%10;
num /= 10;
}
return sum;
}
You have to find the sum of the digits of a variable of type long by the two operators modulo (%) and division (/), you start with the operator modulo to find the remainder of the division (the digits) then, you add this degit to the sum, then you do the division / 10 to overwrite (the summed digit) until the number is equal to 0 like this:
int main()
{
long number=0,m=0;
printf("Give a number :");
scanf("%ld",&number);
long s=0,temp=number;
while(number != 0)
{
m=number%10;
s+=m;
number/=10;
}
printf("\n%The sum of the digits of the Number %ld is : %ld\n",temp,s);
}

How to generate random 64-bit unsigned integer in C

I need generate random 64-bit unsigned integers using C. I mean, the range should be 0 to 18446744073709551615. RAND_MAX is 1073741823.
I found some solutions in the links which might be possible duplicates but the answers mostly concatenates some rand() results or making some incremental arithmetic operations. So results are always 18 digits or 20 digits. I also want outcomes like 5, 11, 33387, not just 3771778641802345472.
By the way, I really don't have so much experience with the C but any approach, code samples and idea could be beneficial.
Concerning "So results are always 18 digits or 20 digits."
See #Thomas comment. If you generate random numbers long enough, code will create ones like 5, 11 and 33387. If code generates 1,000,000,000 numbers/second, it may take a year as very small numbers < 100,000 are so rare amongst all 64-bit numbers.
rand() simple returns random bits. A simplistic method pulls 1 bit at a time
uint64_t rand_uint64_slow(void) {
uint64_t r = 0;
for (int i=0; i<64; i++) {
r = r*2 + rand()%2;
}
return r;
}
Assuming RAND_MAX is some power of 2 - 1 as in OP's case 1073741823 == 0x3FFFFFFF, take advantage that 30 at least 15 bits are generated each time. The following code will call rand() 5 3 times - a tad wasteful. Instead bits shifted out could be saved for the next random number, but that brings in other issues. Leave that for another day.
uint64_t rand_uint64(void) {
uint64_t r = 0;
for (int i=0; i<64; i += 15 /*30*/) {
r = r*((uint64_t)RAND_MAX + 1) + rand();
}
return r;
}
A portable loop count method avoids the 15 /*30*/ - But see 2020 edit below.
#if RAND_MAX/256 >= 0xFFFFFFFFFFFFFF
#define LOOP_COUNT 1
#elif RAND_MAX/256 >= 0xFFFFFF
#define LOOP_COUNT 2
#elif RAND_MAX/256 >= 0x3FFFF
#define LOOP_COUNT 3
#elif RAND_MAX/256 >= 0x1FF
#define LOOP_COUNT 4
#else
#define LOOP_COUNT 5
#endif
uint64_t rand_uint64(void) {
uint64_t r = 0;
for (int i=LOOP_COUNT; i > 0; i--) {
r = r*(RAND_MAX + (uint64_t)1) + rand();
}
return r;
}
The autocorrelation effects commented here are caused by a weak rand(). C does not specify a particular method of random number generation. The above relies on rand() - or whatever base random function employed - being good.
If rand() is sub-par, then code should use other generators. Yet one can still use this approach to build up larger random numbers.
[Edit 2020]
Hallvard B. Furuseth provides as nice way to determine the number of bits in RAND_MAX when it is a Mersenne Number - a power of 2 minus 1.
#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))
#define RAND_MAX_WIDTH IMAX_BITS(RAND_MAX)
_Static_assert((RAND_MAX & (RAND_MAX + 1u)) == 0, "RAND_MAX not a Mersenne number");
uint64_t rand64(void) {
uint64_t r = 0;
for (int i = 0; i < 64; i += RAND_MAX_WIDTH) {
r <<= RAND_MAX_WIDTH;
r ^= (unsigned) rand();
}
return r;
}
If you don't need cryptographically secure pseudo random numbers, I would suggest using MT19937-64. It is a 64 bit version of Mersenne Twister PRNG.
Please, do not combine rand() outputs and do not build upon other tricks. Use existing implementation:
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt64.html
Iff you have a sufficiently good source of random bytes (like, say, /dev/random or /dev/urandom on a linux machine), you can simply consume 8 bytes from that source and concatenate them. If they are independent and have a linear distribution, you're set.
If you don't, you MAY get away by doing the same, but there is likely to be some artefacts in your pseudo-random generator that gives a toe-hold for all sorts of hi-jinx.
Example code assuming we have an open binary FILE *source:
/* Implementation #1, slightly more elegant than looping yourself */
uint64_t 64bitrandom()
{
uint64_t rv;
size_t count;
do {
count = fread(&rv, sizeof(rv), 1, source);
} while (count != 1);
return rv;
}
/* Implementation #2 */
uint64_t 64bitrandom()
{
uint64_t rv = 0;
int c;
for (i=0; i < sizeof(rv); i++) {
do {
c = fgetc(source)
} while (c < 0);
rv = (rv << 8) | (c & 0xff);
}
return rv;
}
If you replace "read random bytes from a randomness device" with "get bytes from a function call", all you have to do is to adjust the shifts in method #2.
You're vastly more likely to get a "number with many digits" than one with "small number of digits" (of all the numbers between 0 and 2 ** 64, roughly 95% have 19 or more decimal digits, so really that is what you will mostly get.
If you are willing to use a repetitive pseudo random sequence and you can deal with a bunch of values that will never happen (like even numbers? ... don't use just the low bits), an LCG or MCG are simple solutions. Wikipedia: Linear congruential generator can get you started (there are several more types including the commonly used Wikipedia: Mersenne Twister). And this site can generate a couple prime numbers for the modulus and the multiplier below. (caveat: this sequence will be guessable and thus it is NOT secure)
#include <stdio.h>
#include <stdint.h>
uint64_t
mcg64(void)
{
static uint64_t i = 1;
return (i = (164603309694725029ull * i) % 14738995463583502973ull);
}
int
main(int ac, char * av[])
{
for (int i = 0; i < 10; i++)
printf("%016p\n", mcg64());
}
I have tried this code here and it seems to work fine there.
#include <time.h>
#include <stdlib.h>
#include <math.h>
int main(){
srand(time(NULL));
int a = rand();
int b = rand();
int c = rand();
int d = rand();
long e = (long)a*b;
e = abs(e);
long f = (long)c*d;
f = abs(f);
long long answer = (long long)e*f;
printf("value %lld",answer);
return 0;
}
I ran a few iterations and i get the following outputs :
value 1869044101095834648
value 2104046041914393000
value 1587782446298476296
value 604955295827516250
value 41152208336759610
value 57792837533816000
If you have 32 or 16-bit random value - generate 2 or 4 randoms and combine them to one 64-bit with << and |.
uint64_t rand_uint64(void) {
// Assuming RAND_MAX is 2^31.
uint64_t r = rand();
r = r<<30 | rand();
r = r<<30 | rand();
return r;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
unsigned long long int randomize(unsigned long long int uint_64);
int main(void)
{
srand(time(0));
unsigned long long int random_number = randomize(18446744073709551615);
printf("%llu\n",random_number);
random_number = randomize(123);
printf("%llu\n",random_number);
return 0;
}
unsigned long long int randomize(unsigned long long int uint_64)
{
char buffer[100] , data[100] , tmp[2];
//convert llu to string,store in buffer
sprintf(buffer, "%llu", uint_64);
//store buffer length
size_t len = strlen(buffer);
//x : store converted char to int, rand_num : random number , index of data array
int x , rand_num , index = 0;
//condition that prevents the program from generating number that is bigger input value
bool Condition = 0;
//iterate over buffer array
for( int n = 0 ; n < len ; n++ )
{
//store the first character of buffer
tmp[0] = buffer[n];
tmp[1] = '\0';
//convert it to integer,store in x
x = atoi(tmp);
if( n == 0 )
{
//if first iteration,rand_num must be less than or equal to x
rand_num = rand() % ( x + 1 );
//if generated random number does not equal to x,condition is true
if( rand_num != x )
Condition = 1;
//convert character that corrosponds to integer to integer and store it in data array;increment index
data[index] = rand_num + '0';
index++;
}
//if not first iteration,do the following
else
{
if( Condition )
{
rand_num = rand() % ( 10 );
data[index] = rand_num + '0';
index++;
}
else
{
rand_num = rand() % ( x + 1 );
if( rand_num != x )
Condition = 1;
data[index] = rand_num + '0';
index++;
}
}
}
data[index] = '\0';
char *ptr ;
//convert the data array to unsigned long long int
unsigned long long int ret = _strtoui64(data,&ptr,10);
return ret;
}

Find the largest prime number factor?

I need to find
The prime factors of 13195 are 5, 7, 13 and 29.
/ * Largest is 377. * /
What is the largest prime factor of the number 600851475143 ?
#include<stdio.h>
int main()
{
int i, j = 0;
/*Code works really fine for 13195 or 26*/
long value, large = 600851475143 /*13195*/;
for(value = (large - 1) ; value >= 3; value--)
{
if(large % value == 0)
{
/*printf("I am here \n");*/
if((value % 2 != 0) && (value % 3 != 0) && (value % 5 != 0) && (value % 7 != 0) )
{
j = 1;
break;
}
}
}
if (j == 1)
{
printf("%ld", value);
}
return 0;
}
Where it is going wrong?
600851475143 is too big to fit in 32 bit integer. long may be 32 bit on your machine. You need to use 64 bit type. The exact data type will be dependent on your platform, compiler.
Your prime checking code is wrong. You are assuming that if something is not devided by 2, 3, 5, 7 then that is prime.
The most important thing that is wrong here is that your code is too slow: even if you fix other issues, such as using a wrong data type for your integers and trying out some divisors that are definitely not prime, iterating by one down from 10^11 will simply not finish in your computer's lifetime is extremely wasteful.
I highly recommend that you read through the example on page 35 of this classic book, where Dijkstra takes you through the process of writing a program printing the first 1000 prime numbers. This example should provide enough mathematical intuition to you to speed up your own calculations, including the part where you start your search from the square root of the number that you are trying to factor.
600851475143 is probably above the precision of your platform's long data type. It requires at least 40 bits to store. You can use this to figure out how many bits you have:
#include <limits.h>
printf("my compiler uses %u bits for the long data type\n", (unsigned int) (CHAR_BIT * sizeof (long)));
#include<stdio.h>
//Euler problem #3
int main(){
long long i, sqi;
long long value, large = 600851475143LL;
long long max = 0LL;
i = 2LL;
sqi = 4LL; //i*i
for(value = large; sqi <= value ; sqi += 2LL * i++ + 1LL){
while(value % i == 0LL){
value /= (max=i);
}
}
if(value != 1LL && value != large){
max = value;
}
if(max == 0LL){
max = large;
}
printf("%lld\n", max);
return 0;
}
You need to add an L as suffix to a number that overflow MAX INT, so this line:
long value, large = 600851475143;
Should be:
long value, large = 600851475143L;
// ^
In order to do this you need to establish that the value is prime - i.e. that is has no prime factors.
Now your little piece of code checking 3/5/7 simply isn't good enough - you need to check is value has ANY lower prime factors (for example 11/13/17).
From a strategic perspective if you want to use this analysis you need to check a list of every prime factor you have found so far and check against them as you are checking against the first 3 primes.
An easier (but less efficient) method would be to write an IsPrimeFunction() and check the primality of the each divisor and store the largest.
public class LargeFactor{
public static void main(String []args){
long num = 600851475143L;
long largestFact = 0;
long[] factors = new long[2];
for (long i = 2; i * i < num; i++) {
if (num % i == 0) { // It is a divisor
factors[0] = i;
factors[1] = num / i;
for (int k = 0; k < 2; k++) {
boolean isPrime = true;
for (long j = 2; j * j < factors[k]; j++) {
if (factors[k] % j == 0) {
isPrime = false;
break;
}
}
if (isPrime && factors[k] > largestFact) {
largestFact = factors[k];
}
}
}
}
System.out.println(largestFact);
}
}
Above code utilises the fact that we only need to check all numbers up to the square root when looking for factors.

Resources