I tried to make a C program that checks if a bank card is valid.
According to Luhn’s algorithm, you can determine if a credit card number is (syntactically) valid as follows:
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!
That’s kind of confusing, so let’s try an example with my AmEx: 378282246310005.
For the sake of discussion, let’s first underline every other digit, starting with the number’s second-to-last digit:
378282246310005
Okay, let’s multiply each of the underlined digits by 2:
7•2 + 2•2 + 2•2 + 4•2 + 3•2 + 0•2 + 0•2
That gives us:
14 + 4 + 4 + 8 + 6 + 0 + 0
Now let’s add those products' digits (i.e., not the products themselves) together:
1 + 4 + 4 + 4 + 8 + 6 + 0 + 0 = 27
Now let’s add that sum (27) to the sum of the digits that weren’t multiplied by 2:
27 + 3 + 8 + 8 + 2 + 6 + 1 + 0 + 5 = 60
Yup, the last digit in that sum (60) is a 0, so my card is legit!
So, validating credit card numbers isn’t hard, but it does get a bit tedious by hand
#include <cs50.h>
#include <math.h>
#include <stdio.h>
int length(long long n);
int num_at(int x,long long y);
long long flip(double a);
int main(void)
{
//int total2=0;
//printf("Number: ");
//int i=get_int();
long long ll=get_long_long();
//if (ll<=0)
//{
// printf("INVALID\n");
//}
//for (int i=1;2*i<length(ll);i++)
//{
// total2=total2+2*num_at(i,ll);
//}
//int total1=0;
//for (int i=0;2*i<length(ll);i++)
//{
// total1=total1+num_at(i,ll);
//}
//int total=total1+total2;
//printf("%i\n",total);
printf("%lli\n",flip(ll));
}
int length(long long n)/* length of number */
{
long long x=1;
int len=0;
while(n-n%x!=0)/* x % y means x mod y*/
{
len++;
x=x*10;
}
return len;
}
int num_at(int x,long long y)/* digit at specific spot in number */
{
int digit=0;//the digit at position z(see below)
int z=1;
for(z=x;z>=0;z--)
{
digit=y%10;
y=y-y%10;
y=y/10;
}
return digit;
}
long long flip(double a)//flips a
{
long long b=0;
for(int y=0;y<length(a);y++)
{
b=b*10;
b=b+(a%pow(10.0,y+1)-a%pow(10.0,y))/pow(10.0,y);
}
return b;
}
The error when compiling is:
credit.c:99:15: error: invalid operands to binary expression ('double' and 'double')
b=b+(a%pow(10.0,y+1)-a%pow(10.0,y))/pow(10.0,y);
~^~~~~~~~~~~~~~
credit.c:99:31: error: invalid operands to binary expression ('double' and 'double')
b=b+(a%pow(10.0,y+1)-a%pow(10.0,y))/pow(10.0,y);
~^~~~~~~~~~~~
You cannot perform the modulus operation on double(or floats).
This is for the simple reason that remainders are defined only for integer arithmetic. What would be the remainder if I divide 2.3 by 0.4??
So to do what you want you need to cast the double ( a and the result of pow) as long long.
So you can do ((long long) a) % (long long) pow( ... ) and it should be fine.
In the future you should look at the error messages (and also the warnings).
Here it clearly says invalid operands to binary expression and % operator is marked.
pow returns a double and % operator is defined only for integers. I think that should solve your problem.
Algo should be more simple that you write it.
I'm not a C programmer but:
// transform LL to char []
int len = length(ll);
char card[len];
sprintf(card, "%lld", ll);
int sum =0;
for(i=0;i < len; i++)
{
int num = card[i] - '0';
if(i % 2 == 0)
{ sum += num; }
else
{
int tmp = num * 2;
sum += (tmp / 10);
sum += (tmp % 10);
}
}
if(sum % 10)
{
// card valid
}
Is more readable in my opinion.
(Sorry if typo, this post was written with an old smartphone...)
EDIT
I know you like using loops but your num_at could also be enhanced :
int num_at(int pos, long long x)
{
return (x / (int)pow(10, pos)) % 10;
}
How do you expect to do a modulo operation (%) between two double values?
What is 10.3 % 5.2, exactly?
The error message is simply saying, "You cannot do double-modulo-double"
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;
}
#include <stdio.h>
#include <string.h>
int main() {
int start, end, i, sum = 0, temp, x, y, z;
puts("Please enter the starting number: ");
scanf("%d", &start);
temp = start;
x = start; y = start + 1;
puts("Please enter the last number till you want to add: ");
scanf("%d", &end);
for (i = start; i < end; i++) {
z = x + y;
x = z;
y = y + 1;
}
printf("The sum of the numbers starting from %d to %d is %d", temp, end, z);
}
Can anyone find what's wrong with this code?
It is working fine for less numbers but when I assign large numbers like 1 to 1000000, it doesn't work i.e. it returns strange values.
Case 1:
Please enter the starting number:
1
Please enter the last number till you want to add:
100
The sum of the numbers starting from 1 to 100 is 5050
But in this case -
Case 2:
Please enter the starting number:
1
Please enter the last number till you want to add:
1000000
The sum of the numbers starting from 1 to 1000000 is 1784293664
What's wrong in this case? Thanks in advance.
You are using int which only have value range -32,768 to 32,767 or -2,147,483,648 to 2,147,483,647 for 2 byte and 4 byte respectively.
You should use unsigned long long for your code which have a value range 0 to 18,446,744,073,709,551,615.
It's a case of data overflow.
For your example of computing from 1 to 1000000, the sum of consecutive numbers from 1 to x is x(x+1)/2, which is 500.000.500.000, a value largely above the range of an int (2.147.483 647).
Consider using long long int, and don't forget that regardless of the byte size of your variable up to a certain value you'll overflow anyway.
For large numbers you must use unsigned long long int instead of int. Here int means integer which is up to 3 or 4 digit numbers but when you enter 5 digit numbers or more digit numbers it gives you strange values.
Problem in finding sum of consecutive numbers
OP's code repeatedly adds and attempts a sum greater than INT_MAX leading to incorrect results.
Use a wider type for the computation of the sum such as long long or intmax_t.
Performance: Do not use a loop. The sum of integers [0...N] is N*(N+1)/2
Instead:
//for (i = start; i < end; i++) {
// z = x + y;
// x = z;
// y = y + 1;
//}
// Sum 0 to start
long long sum_start = ((long long) start + 1) * start / 2; // or (start + 1LL)*start/2;
// Sum 0 to end
long long sum_end = ((long long) end + 1) * end / 2;
// vvvv specifier change
printf("The sum of the numbers starting from %d to %d is %lld",
temp, end, sum_end - (sum_start - start));
The range of type int is not sufficient to represent the value of the sum for large values of start and stop (for example the sum for 0 to 65536 exceeds 2147483647). The variables x, y and z should be defined with type long long which is guaranteed to represent values up to 9223372036854775807 (9.22E18) or unsigned long up to 18446744073709551615 (1.84E19).
Furthermore your algorithm to compute the sum is convoluted, with unnecessary or unused intermediary variables. Simplicity is king!
Here is a modified version:
#include <stdio.h>
#include <string.h>
int main() {
int start, end, i;
long long sum;
puts("Please enter the starting number: ");
if (scanf("%d", &start) != 1)
return 1;
puts("Please enter the last number till you want to add: ");
if (scanf("%d", &end) != 1)
return 1;
sum = 0;
for (i = start; i <= end; i++) {
sum = sum + i;
}
printf("The sum of the numbers from %d to %d is %lld\n", start, end, sum);
return 0;
}
For positive start and stop values, there is a direct formula to compute the result. You could replace the for loop with this expression:
if (start <= stop) {
// take the sum of integers from 0 to stop: stop * (stop + 1) / 2
sum = (long long)stop * (stop + 1) / 2;
// subtract the sum of integers from 0 to start - 1: (start - 1) * start / 2
sum -= (long long)start * (start - 1) / 2;
} else {
sum = 0;
}
which simplifies as:
if (start <= stop) {
sum = ((long long)stop + start)(stop - start + 1) / 2;
} else {
sum = 0;
}
If start and/or stop can be negative, the sum can still be computed directly as:
if (start <= stop) {
// take the sum of integers from 0 to stop - start:
sum = (long long)(start - stop) * ((long long)stop - start + 1) / 2;
// add the initial value (stop - start + 1) times
sum += ((long long)stop - start + 1) * start;
} else {
sum = 0;
}
Which simplifies as:
if (start <= stop) {
sum = ((long long)stop + start)((long long)stop - start + 1) / 2;
} else {
sum = 0;
}
Sorry, but don't you know the following way to compute the sum?
long sum_from_a_to_b(long a, long b)
{
int n;
int acc = 0;
for (n = a; n <= b; n++)
acc += n;
return acc;
}
by the way, there's a better formula, which requires no loop:
#include <assert.h>
/* assumed that a <= b */
long sum_from_a_to_b(long a, long b)
{
assert(a <= b);
return b*(b + 1)/2 - a*(a - 1)/2;
}
In the second case, you have used a number that is greater than the allowed range for integer data type(it's from -32768 to 32767 for a 16-bit architecture and from -2,147,483,648 to 2,147,483,647 for a 32-bit architecture.) and that's why it leads to integer overflow issue. To fix this problem, you could use other bigger data types such as long int or long long int but I recommend you use their unsigned data types i.e unsigned long int or unsigned long long int if you're not going to use negative numbers because the range of unsigned data types begins from 0.
Let's say I've been given two integers a, b where a is a positive integer and is smaller than b. I have to find an efficient algorithm that's going to give me the sum of number of base2 digits (number of bits) over the interval [a, b]. For example, in the interval [0, 4] the sum of digits is equal to 9 because 0 = 1 digit, 1 = 1 digit, 2 = 2 digits, 3 = 2 digits and 4 = 3 digits.
My program is capable of calculating this number by using a loop but I'm looking for something more efficient for large numbers. Here are the snippets of my code just to give you an idea:
int numberOfBits(int i) {
if(i == 0) {
return 1;
}
else {
return (int) log2(i) + 1;
}
}
The function above is for calculating the number of digits of one number in the interval.
The code below shows you how I use it in my main function.
for(i = a; i <= b; i++) {
l = l + numberOfBits(i);
}
printf("Digits: %d\n", l);
Ideally I should be able to get the number of digits by using the two values of my interval and using some special algorithm to do that.
Try this code, i think it gives you what you are needing to calculate the binaries:
int bit(int x)
{
if(!x) return 1;
else
{
int i;
for(i = 0; x; i++, x >>= 1);
return i;
}
}
The main thing to understand here is that the number of digits used to represent a number in binary increases by one with each power of two:
+--------------+---------------+
| number range | binary digits |
+==============+===============+
| 0 - 1 | 1 |
+--------------+---------------+
| 2 - 3 | 2 |
+--------------+---------------+
| 4 - 7 | 3 |
+--------------+---------------+
| 8 - 15 | 4 |
+--------------+---------------+
| 16 - 31 | 5 |
+--------------+---------------+
| 32 - 63 | 6 |
+--------------+---------------+
| ... | ... |
A trivial improvement over your brute force algorithm would then be to figure out how many times this number of digits has increased between the two numbers passed in (given by the base two logarithm) and add up the digits by multiplying the count of numbers that can be represented by the given number of digits (given by the power of two) with the number of digits.
A naive implementation of this algorithm is:
int digits_sum_seq(int a, int b)
{
int sum = 0;
int i = 0;
int log2b = b <= 0 ? 1 : floor(log2(b));
int log2a = a <= 0 ? 1 : floor(log2(a)) + 1;
sum += (pow(2, log2a) - a) * (log2a);
for (i = log2b; i > log2a; i--)
sum += pow(2, i - 1) * i;
sum += (b - pow(2, log2b) + 1) * (log2b + 1);
return sum;
}
It can then be improved by the more efficient versions of the log and pow functions seen in the other answers.
First, we can improve the speed of log2, but that only gives us a fixed factor speed-up and doesn't change the scaling.
Faster log2 adapted from: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
The lookup table method takes only about 7 operations to find the log
of a 32-bit value. If extended for 64-bit quantities, it would take
roughly 9 operations. Another operation can be trimmed off by using
four tables, with the possible additions incorporated into each. Using
int table elements may be faster, depending on your architecture.
Second, we must re-think the algorithm. If you know that numbers between N and M have the same number of digits, would you add them up one by one or would you rather do (M-N+1)*numDigits?
But if we have a range where multiple numbers appear what do we do? Let's just find the intervals of same digits, and add sums of those intervals. Implemented below. I think that my findEndLimit could be further optimized with a lookup table.
Code
#include <stdio.h>
#include <limits.h>
#include <time.h>
unsigned int fastLog2(unsigned int v)
{
static const char LogTable256[256] =
{
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};
register unsigned int t, tt; // temporaries
if (tt = v >> 16)
{
return (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else
{
return (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}
}
unsigned int numberOfBits(unsigned int i)
{
if (i == 0) {
return 1;
}
else {
return fastLog2(i) + 1;
}
}
unsigned int findEndLimit(unsigned int sx, unsigned int ex)
{
unsigned int sy = numberOfBits(sx);
unsigned int ey = numberOfBits(ex);
unsigned int mx;
unsigned int my;
if (sy == ey) // this also means sx == ex
return ex;
// assumes sy < ey
mx = (ex - sx) / 2 + sx; // will eq. sx for sx + 1 == ex
my = numberOfBits(mx);
while (ex - sx != 1) {
mx = (ex - sx) / 2 + sx; // will eq. sx for sx + 1 == ex
my = numberOfBits(mx);
if (my == ey) {
ex = mx;
ey = numberOfBits(ex);
}
else {
sx = mx;
sy = numberOfBits(sx);
}
}
return sx+1;
}
int main(void)
{
unsigned int a, b, m;
unsigned long l;
clock_t start, end;
l = 0;
a = 0;
b = UINT_MAX;
start = clock();
unsigned int i;
for (i = a; i < b; ++i) {
l += numberOfBits(i);
}
if (i == b) {
l += numberOfBits(i);
}
end = clock();
printf("Naive\n");
printf("Digits: %ld; Time: %fs\n",l, ((double)(end-start))/CLOCKS_PER_SEC);
l=0;
start = clock();
do {
m = findEndLimit(a, b);
l += (b-m + 1) * (unsigned long)numberOfBits(b);
b = m-1;
} while (b > a);
l += (b-a+1) * (unsigned long)numberOfBits(b);
end = clock();
printf("Binary search\n");
printf("Digits: %ld; Time: %fs\n",l, ((double)(end-start))/CLOCKS_PER_SEC);
}
Output
From 0 to UINT_MAX
$ ./main
Naive
Digits: 133143986178; Time: 25.722492s
Binary search
Digits: 133143986178; Time: 0.000025s
My findEndLimit can take long time in some edge cases:
From UINT_MAX/16+1 to UINT_MAX/8
$ ./main
Naive
Digits: 7784628224; Time: 1.651067s
Binary search
Digits: 7784628224; Time: 4.921520s
Conceptually, you would need to split the task to two subproblems -
1) find the sum of digits from 0..M, and from 0..N, then subtract.
2) find the floor(log2(x)), because eg for the number 77 the numbers 64,65,...77 all have 6 digits, the next 32 have 5 digits, the next 16 have 4 digits and so on, which makes a geometric progression.
Thus:
int digits(int a) {
if (a == 0) return 1; // should digits(0) be 0 or 1 ?
int b=(int)floor(log2(a)); // use any all-integer calculation hack
int sum = 1 + (b+1) * (a- (1<<b) +1); // added 1, due to digits(0)==1
while (--b)
sum += (b + 1) << b; // shortcut for (b + 1) * (1 << b);
return sum;
}
int digits_range(int a, int b) {
if (a <= 0 || b <= 0) return -1; // formulas work for strictly positive numbers
return digits(b)-digits(a-1);
}
As efficiency depends on the tools available, one approach would be doing it "analog":
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
unsigned long long pow2sum_min(unsigned long long n, long long unsigned m)
{
if (m >= n)
{
return 1;
}
--n;
return (2ULL << n) + pow2sum_min(n, m);
}
#define LN(x) (log2(x)/log2(M_E))
int main(int argc, char** argv)
{
if (2 >= argc)
{
fprintf(stderr, "%s a b\n", argv[0]);
exit(EXIT_FAILURE);
}
long a = atol(argv[1]), b = atol(argv[2]);
if (0L >= a || 0L >= b || b < a)
{
puts("Na ...!");
exit(EXIT_FAILURE);
}
/* Expand intevall to cover full dimensions: */
unsigned long long a_c = pow(2, floor(log2(a)));
unsigned long long b_c = pow(2, floor(log2(b+1)) + 1);
double log2_a_c = log2(a_c);
double log2_b_c = log2(b_c);
unsigned long p2s = pow2sum_min(log2_b_c, log2_a_c) - 1;
/* Integral log2(x) between a_c and b_c: */
double A = ((b_c * (LN(b_c) - 1))
- (a_c * (LN(a_c) - 1)))/LN(2)
+ (b+1 - a);
/* "Integer"-integral - integral of log2(x)'s inverse function (2**x) between log(a_c) and log(b_c): */
double D = p2s - (b_c - a_c)/LN(2);
/* Corrective from a_c/b_c to a/b : */
double C = (log2_b_c - 1)*(b_c - (b+1)) + log2_a_c*(a - a_c);
printf("Total used digits: %lld\n", (long long) ((A - D - C) +.5));
}
:-)
The main thing here is the number and kind of iterations done.
Number is
log(floor(b_c)) - log(floor(a_c))
times
doing one
n - 1 /* Integer decrement */
2**n + s /* One bit-shift and one integer addition */
for each iteration.
Here's an entirely look-up based approach. You don't even need the log2 :)
Algorithm
First we precompute interval limits where the number of bits would change and create a lookup table. In other words we create an array limits[2^n], where limits[i] gives us the biggest integer that can be represented with (i+1) bits. Our array is then {1, 3, 7, ..., 2^n-1}.
Then, when we want to determine the sum of bits for our range, we must first match our range limits a and b with the smallest index for which a <= limits[i] and b <= limits[j] holds, which will then tell us that we need (i+1) bits to represent a, and (j+1) bits to represent b.
If the indexes are the same, then the result is simply (b-a+1)*(i+1), otherwise we must separately get the number of bits from our value to the edge of same number of bits interval, and add up total number of bits for each interval between as well. In any case, simple arithmetic.
Code
#include <stdio.h>
#include <limits.h>
#include <time.h>
unsigned long bitsnumsum(unsigned int a, unsigned int b)
{
// generate lookup table
// limits[i] is the max. number we can represent with (i+1) bits
static const unsigned int limits[32] =
{
#define LTN(n) n*2u-1, n*4u-1, n*8u-1, n*16u-1, n*32u-1, n*64u-1, n*128u-1, n*256u-1
LTN(1),
LTN(256),
LTN(256*256),
LTN(256*256*256)
};
// make it work for any order of arguments
if (b < a) {
unsigned int c = a;
a = b;
b = c;
}
// find interval of a
unsigned int i = 0;
while (a > limits[i]) {
++i;
}
// find interval of b
unsigned int j = i;
while (b > limits[j]) {
++j;
}
// add it all up
unsigned long sum = 0;
if (i == j) {
// a and b in the same range
// conveniently, this also deals with j == 0
// so no danger to do [j-1] below
return (i+1) * (unsigned long)(b - a + 1);
}
else {
// add sum of digits in range [a, limits[i]]
sum += (i+1) * (unsigned long)(limits[i] - a + 1);
// add sum of digits in range [limits[j], b]
sum += (j+1) * (unsigned long)(b - limits[j-1]);
// add sum of digits in range [limits[i], limits[j]]
for (++i; i<j; ++i) {
sum += (i+1) * (unsigned long)(limits[i] - limits[i-1]);
}
return sum;
}
}
int main(void)
{
clock_t start, end;
unsigned int a=0, b=UINT_MAX;
start = clock();
printf("Sum of binary digits for numbers in range "
"[%u, %u]: %lu\n", a, b, bitsnumsum(a, b));
end = clock();
printf("Time: %fs\n", ((double)(end-start))/CLOCKS_PER_SEC);
}
Output
$ ./lookup
Sum of binary digits for numbers in range [0, 4294967295]: 133143986178
Time: 0.000282s
Algorithm
The main idea is to find the n2 = log2(x) rounded down. That is the number of digits in x. Let pow2 = 1 << n2. n2 * (pow2 - x + 1) is the number of digits in the values [x...pow2]. Now find the sun of digits in the powers of 2 from 1 to n2-1
Code
I am certain various simplifications can be made.
Untested code. Will review later.
// Let us use unsigned for everything.
unsigned ulog2(unsigned value) {
unsigned result = 0;
if (0xFFFF0000u & value) {
value >>= 16; result += 16;
}
if (0xFF00u & value) {
value >>= 8; result += 8;
}
if (0xF0u & value) {
value >>= 4; result += 4;
}
if (0xCu & value) {
value >>= 2; result += 2;
}
if (0x2 & value) {
value >>= 1; result += 1;
}
return result;
}
unsigned bit_count_helper(unsigned x) {
if (x == 0) {
return 1;
}
unsigned n2 = ulog2(x);
unsigned pow2 = 1u << n;
unsigned sum = n2 * (pow2 - x + 1u); // value from pow2 to x
while (n2 > 0) {
// ... + 5*16 + 4*8 + 3*4 + 2*2 + 1*1
pow2 /= 2;
sum += n2 * pow2;
}
return sum;
}
unsigned bit_count(unsigned a, unsigned b) {
assert(a < b);
return bit_count_helper(b - 1) - bit_count_helper(a);
}
For this problem your solution is the simplest, the one called "naive" where you look for every element in the sequence or in your case interval for check something or execute operations.
Naive Algorithm
Assuming that a and b are positive integers with b greater than a let's call the dimension/size of the interval [a,b], n = (b-a).
Having our number of elements n and using some notations of algorithms (like big-O notation link), the worst case cost is O(n*(numberOfBits_cost)).
From this we can see that we can speed up our algorithm by using a faster algorithm for computing numberOfBits() or we need to find a way to not look at every element of the interval that costs us n operations.
Intuition
Now looking at a possible interval [6,14] you can see that for 6 and 7 we need 3 digits, with 4 need for 8,9,10,11,12,13,14. This results in calling numberOfBits() for every number that use the same number of digits to be represented, while the following multiplication operation would be faster:
(number_in_subinterval)*digitsForThisInterval
((14-8)+1)*4 = 28
((7-6)+1)*3 = 6
So we reduced the looping on 9 elements with 9 operations to only 2.
So writing a function that use this intuition will give us a more efficient in time, not necessarily in memory, algorithm. Using your numberOfBits() function I have created this solution:
int intuitionSol(int a, int b){
int digitsForA = numberOfBits(a);
int digitsForB = numberOfBits(b);
if(digitsForA != digitsForB){
//because a or b can be that isn't the first or last element of the
// interval that a specific number of digit can rappresent there is a need
// to execute some correction operation before on a and b
int tmp = pow(2,digitsForA) - a;
int result = tmp*digitsForA; //will containt the final result that will be returned
int i;
for(i = digitsForA + 1; i < digitsForB; i++){
int interval_elements = pow(2,i) - pow(2,i-1);
result = result + ((interval_elements) * i);
//printf("NumOfElem: %i for %i digits; sum:= %i\n", interval_elements, i, result);
}
int tmp1 = ((b + 1) - pow(2,digitsForB-1));
result = result + tmp1*digitsForB;
return result;
}
else {
int elements = (b - a) + 1;
return elements * digitsForA; // or digitsForB
}
}
Let's look at the cost, this algorithm costs is the cost of doing correction operation on a and b plus the most expensive one that of the for-loop. In my solution however I'm not looping over all elements but only on numberOfBits(b)-numberOfBits(a) that in the worst case, when [0,n], become log(n)-1 thats equivalent to O(log n).
To resume we passed from a linear operations cost O(n) to a logartmic one O(log n) in the worst case. Look on this diagram the diferinces between the two.
Note
When I talk about interval or sub-interval I refer to the interval of elements that use the same number of digits to represent the number in binary.
Following there are some output of my tests with the last one that shows the difference:
Considered interval is [0,4]
YourSol: 9 in time: 0.000015s
IntuitionSol: 9 in time: 0.000007s
Considered interval is [0,0]
YourSol: 1 in time: 0.000005s
IntuitionSol: 1 in time: 0.000005s
Considered interval is [4,7]
YourSol: 12 in time: 0.000016s
IntuitionSol: 12 in time: 0.000005s
Considered interval is [2,123456]
YourSol: 1967697 in time: 0.005010s
IntuitionSol: 1967697 in time: 0.000015s
So i'm supposed to find out the last 10 digits of 2^n(0<=n<=100) where n is the input. I found a method to handle large numbers but the program fails when n>64. Any leads on how to go about with this would be appreciated.
#include<stdio.h>
#include<math.h>
/* Iterative Function to calculate (x^y)%p in O(log y) */
int power(long long int x, long long int y, long long int p)
{
long long int res = 1; // Initialize result
x = x % p; // Update x if it is more than or
// equal to p
while (y > 0) {
// If y is odd, multiply x with result
if (y & 1)
res = (res * x) % p;
// y must be even now
y = y >> 1; // y = y/2
x = (x * x) % p;
}
return res;
}
// C function to print last 10 digits of a^b
void printLastDigits(long long int a,long long int b)
{
long long int temp = pow(10,10);
// Calling modular exponentiation
temp = power(a, b, temp);
if (temp)
printf("%d",temp);
}
int main()
{
long long int n;
scanf("%d",&n);
printLastDigits(2,n);
return 0;
}
You don't need to worry about the 'high' bits, since multiplication by 2 left shifts them out of range of the lower part of the product you're interesting in. Just be sure you're using the unsigned long long type (of at least 64 bits) to hold integral types that are wide enough, e.g.,
#include <inttypes.h>
#include <stdio.h>
void low_digits (unsigned int n)
{
unsigned long long base = 2, modulus = 10000000000ULL;
for (unsigned int i = 1; i <= n; i++)
{
fprintf(stdout, "2^%u mod 10^10 = %llu\n", i, base);
base = (base * 2) % modulus;
}
}
You can test 2^1000 with a bignum calculator:
10715086071862673209484250490600018105614048117055336074437503883703\
51051124936122493198378815695858127594672917553146825187145285692314\
04359845775746985748039345677748242309854210746050623711418779541821\
53046474983581941267398767559165543946077062914571196477686542167660\
429831652624386837205668069376
while n = 1000 above yields: 5668069376
Others have noted, that this is a naive method, and modular exponentiation is far more efficient for sufficiently large values of (n). Unfortunately, this is going to require products that exceed the range of an unsigned 64-bit value, so unless you're prepared to implement [hi64][lo64] multi-precision mul / mod operations, it's probably beyond the scope of your task.
Fortunately, later versions of gcc and clang do provide an extended 128 bit integral type:
#include <inttypes.h>
#include <stdio.h>
void low_digits (unsigned int n)
{
unsigned long long base = 2, modulus = 10000000000ULL;
__extension__ unsigned __int128 u = 1, w = base;
while (n != 0)
{
if ((n & 0x1) != 0)
u = (u * w) % modulus; /* (mul-reduce) */
if ((n >>= 1) != 0)
w = (w * w) % modulus; /* (sqr-reduce) */
}
base = (unsigned long long) u;
fprintf(stdout, "2^%u mod 10^10 = %llu\n", n, base);
}
The following uses strings to perform the multiplication:
void lastdigits(char digits[11], int n)
{
int i, j, x, carry;
for (i=0; i<n;i++) {
for (j=9, carry=0; j>=0; j--) {
x= digits[j]-'0';
x *= 2;
x += carry;
if (x>9) {carry= 1; x -= 10;}
else carry= 0;
digits[j]= x+'0';
}
}
}
void test(void)
{
char digits[11];
strcpy(digits,"0000000001");
lastdigits(digits,10);
printf("%s\n",digits);
strcpy(digits,"0000000001");
lastdigits(digits,20);
printf("%s\n",digits);
strcpy(digits,"0000000001");
lastdigits(digits,100);
printf("%s\n",digits);
}
Output:
0000001024
0001048576
6703205376
Since the other answers you've received don't actually show what you're doing wrong:
x = (x * x) % p;
You're assuming that x * x still fits in long long int. But if x is 0x100000000 (4294967296, for 10 decimal digits) and long long int is 64 bits, then it will not fit.
Either:
You need a way to accurately multiply two arbitrary 10-digit numbers. The result may have 20 digits and may not fit in long long int or even unsigned long long int. This means you'd need to use some bigint library or implement something like that yourself.
Or:
You need to avoid multiplying multiple possibly-10-digit numbers.
The answer you've accepted opts for simple repeated multiplication by 2. This is sufficient for your problem now, but beware that this does significantly increase the complexity if you want to allow very large exponents.
Let's say you are finding the last digit of 2^n, you just need to consider last digit and ignore every other digit
1. 2*2 = 4
2. 4*2 = 8
3. 8*2 = 16 (ignore last-but-one digit i.e 1)
4. 6*2 = 12 (ignore last-but-one digit i.e 1)
5. 2*2 = 4
6. 4*2 = 8
7. 8*2 = 16 (ignore last-but-one digit i.e 1)
8. 6*2 = 12 (ignore last-but-one digit i.e 1)
9. 2*2 = 4
... n-1 iterations
To find the last 2 digits of 2^n, ignore all digits except last 2 digits.
1. 2*2 = 4
2. 4*2 = 8
3. 8*2 = 16
4. 16*2 = 32
5. 32*2 = 64
6. 64*2 = 128 (Consider last 2 digits)
7. 28*2 = 56
8. 56*2 = 112 (Consider last 2 digits)
9. 12*2 = 24
... n-1 iterations
Similarly, to find the last 10 digits of 2^n, consider just last 10 digits at each iteration and repeat it for n-1 iterations.
Note:
With this approach, the biggest number you'll get during the calculation can be of 11 digits ~ 10^11, while for a 64-bit machine the max value is ~ 2^64 = ~ 10^18
When I run this code, I get outputs that don't really make any sense. I'm most likely just missing something, but I've been working on trying to find the problem with my code by working out the problems by hand, but I'm getting the values I should be getting when I do it by hand. A simplified version of this calculation is (c%10^n - c%10^(n-1)) / 10^(n-1).
The goal of this calculation is to assign the digits of a number to an array of ints. I'm not really looking for alternate solutions.
int cNumberV[nLength];
for(int n = nLength; n > 0; n--) {
cNumberV[nLength - n] = (cNumber % (long long) pow(10, n) - cNumber % (long long) pow(10, n - 1)) / (long long) pow(10, n - 1);
printf("%i\n", cNumberV[n]);
}
This is my output when cNumber = 5105105105105100 and nLength = 16:
-1981492631
232830
-1530494976
1188624
-397102900
134514540
-1081801416
1188624
0
1
5
0
1
5
0
1
The problem is that your loop sets cNumberV[nLength - n], but then prints out cNumberV[n].
So the first half of the loop prints uninitialized array entries, and the second half of the loop prints the result of the first half's calculation in reverse order (but due to an off-by-one error as pointed out by rowan.G, it never prints the first digit).
pow() is an expensive and inaccurate floating function. You only
need simple integer divide by ten to get digits. If you really want
to get them left-to-right as you do above, make a lookup table with
the 19 powers of 10 as integers.
#include <stdio.h>
#define nLength 20
long long cNumber = 5105105105105100;
int cNumberV[nLength];
int negative = 0;
int main(int argc, char *argv[]) {
if (cNumber < 0) {
negative = 1;
cNumber = -cNumber;
}
int n;
for (n = nLength - 1; n >= 0; n -= 1) {
cNumberV[n] = cNumber % 10;
cNumber /= 10;
if (0 == cNumber) break;
}
if (negative) printf("-");
for (int i = n; i < nLength; i += 1) {
printf("%1d", cNumberV[i]);
}
printf("\n");
}