I want to multiply every other digit by 2, starting with the number’s second-to-last digit, and then add those products’ digits together, but the first printed values seem completely nonsensical.
#include <stdio.h>
#include <math.h>
int len(long li);
int main(void)
{
// Get credit card number
long credit_card = 378282246310005;
// Adding every second digit's double's digits, starting with the second to last
int sum = 0;
int digit_doubled;
for (int i = 0; i < len(credit_card); i++)
{
if (i % 2 == 0)
{
continue;
}
digit_doubled = (int) (credit_card / pow(10, i)) % 10 * 2;
printf("Digit doubled %i: %i\n", i, digit_doubled);
for (int j = 0; j < len(digit_doubled); j++)
{
sum += (int) (digit_doubled / pow(10, j)) % 10;
}
}
}
int len(long li)
{
if (li == 0)
{
return 1;
}
return floor(log10(li)) + 1;
}
I have tried modifying the expression to see what results I'd get. When I deleted the
% 10 * 2 from the end of digit_doubled = (int) (credit_card / pow(10, i)) % 10 * 2;, I got results that indicate some kind of integer overflow, but I have absolutely no idea where it could be coming from since my program isn't really producing any high values anywhere.
Having suggested using a LUT to deliver the single digit value required by the OP's question, here is a snippet of code to do just that:
unsigned long long copy = longValue; // perhaps correct on OP's compiler??
copy /= 10; // dispose of the checksum digit (rightmost)
// The following line depends on the OP's problem statement
// Is this digit odd (checksum digit being digit zero)
// or is this digit even? (checksum digit being disregarded.)
// Incl-/excl- next line to suit problem statement.
copy /= 10; // dispose of the rightmost "odd" digit
while( copy ) {
int digit = copy % 10; // get "even" digit from the right end.
// Crafted LUT to return correctly "doubled & folded" value of this digit.
sum += "0246813579"[digit] - '0';
copy /= 100; // "shift" value down by 100 (ie: 2 digits of value)
}
I leave it as an exercise for the OP to determine what characterises "even" and "odd" in the sequence of digits of a credit card "number". Does one count the checksum digit as '1' or not? For the OP to work out...
For fun, here's the loop after compaction.
for( /**/; copy; copy /= 100 )
sum += "0246813579"[ copy % 10 ] - '0';
And, a more advanced version would simply calculate the desired value from the supplied value:
for( /**/; copy; copy /= 100 )
sum += copy%5 * 2 + copy%10/5;
For those who (reasonably) question the validity of the formula above:
#include <stdio.h>
int main( void ) {
for( int i = 0; i < 10; i++ ) {
int j = 2 * i;
printf( "Digit Value: %d ", i );
printf( " x2 = %02d ", j );
printf( "==> %d + %d ", j/10, j%10 );
printf( "==> %d ", j/10 + j%10 );
printf( "==== %d\n", i%5 * 2 + i%10/5 ); // <== Formula version
}
return 0;
}
Digit Value: 0 x2 = 00 ==> 0 + 0 ==> 0 ==== 0
Digit Value: 1 x2 = 02 ==> 0 + 2 ==> 2 ==== 2
Digit Value: 2 x2 = 04 ==> 0 + 4 ==> 4 ==== 4
Digit Value: 3 x2 = 06 ==> 0 + 6 ==> 6 ==== 6
Digit Value: 4 x2 = 08 ==> 0 + 8 ==> 8 ==== 8
Digit Value: 5 x2 = 10 ==> 1 + 0 ==> 1 ==== 1
Digit Value: 6 x2 = 12 ==> 1 + 2 ==> 3 ==== 3
Digit Value: 7 x2 = 14 ==> 1 + 4 ==> 5 ==== 5
Digit Value: 8 x2 = 16 ==> 1 + 6 ==> 7 ==== 7
Digit Value: 9 x2 = 18 ==> 1 + 8 ==> 9 ==== 9
In the first loop when i=1 you cast a value greater than the maximum value that can be stored in an integer (2147483647) which cause it to be truncated and on my system go negative:
digit_doubled = (int) (credit_card / pow(10, i)) % 10 * 2; // =>
digit_doubled = (int) 37828224631000 % 10 * 2; / =>
digit_doubled = -1847312168 % 10 * 2; // =>
digit_doubled = -16;
I suggest you include stdint.h and use uint64_t instead of silently assume that a long is 64 bit. Alternatively, consider using a string as a credit number is an identifier not a number (even though it is written as one).
Mixing functions that operate on double for integer type values will open you up to floating point rounding errors. You could use uint64_t versions of these functions instead of double. Below I implemented a len() and my_pow10() functions for you. As the value of digit_doubled is at most 18 you can just inline that calculation instead of using a loop.
#include <stdio.h>
#include <stdint.h>
uint64_t my_pow10(uint64_t x) {
if(x == 0) return 1;
size_t v = 10;
for(size_t i = 1; i < x; i++, v *= 10);
return v;
}
size_t len(uint64_t v) {
size_t i = 0;
for(; v; i++, v /= 10);
return i;
}
int main(void) {
const uint64_t credit_card = 378282246310005;
const uint8_t credit_card_len = len(credit_card);
uint8_t sum = 0;
for (uint8_t i = 1; i < credit_card_len; i += 2) {
uint8_t digit_doubled = credit_card / my_pow10(i) % 10 * 2;
printf("Digit doubled %hhu: %hhu\n", i, digit_doubled);
sum += digit_doubled / 10 + digit_doubled % 10;
}
printf("sum = %u\n", sum);
}
You don't need floating-point arithmetic at all here, or logs or pows. You can use % 10 to extract the next digit, and / 10 to discard it. Like this:
#include <stdint.h>
#include <stdio.h>
int main(void)
{
uint64_t credit_card = 378282246310005ULL;
int sum = 0;
while (credit_card != 0)
{
credit_card /= 10; // Discard rightmost digit
int double_digit = 2 * (credit_card % 10);
credit_card /= 10;
sum += double_digit % 10;
double_digit /= 10;
sum += double_digit % 10;
}
printf ("%d\n", sum);
}
I am trying to finish an assignment in C for the CS50 course in which I must implement Luhn's algorithm to validate a credit card number. Here is a quick example to elaborate:
credit card number: 4003600000000014.
Now for every other digit, starting with the number’s second-to-last digit:
1-0-0-0-0-6-0-4
Let’s multiply each of the digits by 2:
1•2 + 0•2 + 0•2 + 0•2 + 0•2 + 6•2 + 0•2 + 4•2
That gives us:
2 + 0 + 0 + 0 + 0 + 12 + 0 + 8
Now let’s add those products’ digits (i.e., not the products themselves) together:
2 + 0 + 0 + 0 + 0 + 1 + 2 + 0 + 8 = 13
Now let’s add that sum (13) to the sum of the digits that weren’t multiplied by 2 (starting from the end):
13 + 4 + 0 + 0 + 0 + 0 + 0 + 3 + 0 = 20
Yup, the last digit in that sum (20) is a 0, so the number is valid.
I figured out how to extract each number in the credit card individually (I know my way is boring and probably not practical), so the next step is to multiply every other number by two and add (the products' digits, not the digits themselves) and this is what I need help of how to do it?
MY code:
#include <cs50.h>
#include <stdio.h>
#include <math.h>
int main(void)
{
long credit_card_number;
do
{
credit_card_number = get_long("Enter your credit card number: ");
}
while (credit_card_number < 1 || credit_card_number > 9999999999999999);
//American Express uses 15-digit numbers. American Express numbers start with 34 or 37
//MasterCard uses 16-digit numbers. MasterCard numbers start with 51, 52, 53, 54, or 55.
//Visa uses 13- and 16-digit numbers. Visa numbers start with 4.
// checksum
long last_number;
long credit_card_without_last_number;
long second_to_last_number;
long credit_card_without_second_number;
long third_number;
long credit_card_without_third_number;
long fourth_number;
long credit_card_without_fourth_number;
long fifth_number;
long credit_card_without_fifth_number;
long sixth_number;
long credit_card_without_sixth_number;
long seventh_number;
long credit_card_without_seventh_number;
long eighth_number;
long credit_card_without_eighth_number;
long ninth_number;
long credit_card_without_ninth_number;
long tenth_number;
long credit_card_without_tenth_number;
long eleventh_number;
long credit_card_without_eleventh_number;
long twelfth_number;
long credit_card_without_twelfth_number;
long thirteenth_number;
long credit_card_without_thirteenth_number;
long fourteenth_number;
long credit_card_without_fourteenth_number;
long fifteenth_number;
long credit_card_without_fifteenth_number;
long sixteenth_number;
long multiply_digits;
//separating each number starting from the last (right)in its own variable.
last_number = credit_card_number % 10;
credit_card_without_last_number = credit_card_number / 10;
second_to_last_number = credit_card_without_last_number % 10;
credit_card_without_second_number = credit_card_without_last_number / 10;
third_number = credit_card_without_second_number % 10;
credit_card_without_third_number = credit_card_without_second_number / 10;
fourth_number = credit_card_without_third_number % 10;
credit_card_without_fourth_number = credit_card_without_third_number / 10;
fifth_number = credit_card_without_fourth_number % 10;
credit_card_without_fifth_number = credit_card_without_fourth_number / 10;
sixth_number = credit_card_without_fifth_number % 10;
credit_card_without_sixth_number = credit_card_without_fifth_number / 10;
seventh_number = credit_card_without_sixth_number % 10;
credit_card_without_seventh_number = credit_card_without_sixth_number / 10;
eighth_number = credit_card_without_seventh_number % 10;
credit_card_without_eighth_number = credit_card_without_seventh_number / 10;
ninth_number = credit_card_without_eighth_number % 10;
credit_card_without_ninth_number = credit_card_without_eighth_number / 10;
tenth_number = credit_card_without_ninth_number % 10;
credit_card_without_tenth_number = credit_card_without_ninth_number / 10;
eleventh_number = credit_card_without_tenth_number % 10;
credit_card_without_eleventh_number = credit_card_without_tenth_number / 10;
twelfth_number = credit_card_without_eleventh_number % 10;
credit_card_without_twelfth_number = credit_card_without_eleventh_number / 10;
thirteenth_number = credit_card_without_twelfth_number % 10;
credit_card_without_thirteenth_number = credit_card_without_twelfth_number / 10;
fourteenth_number = credit_card_without_thirteenth_number % 10;
credit_card_without_fourteenth_number = credit_card_without_thirteenth_number / 10;
fifteenth_number = credit_card_without_fourteenth_number % 10;
credit_card_without_fifteenth_number = credit_card_without_fourteenth_number / 10;
sixteenth_number = credit_card_without_fifteenth_number % 10;
//Here I need the help to multiply these numbers by two and then add each product's
//digits to the rest of the unused numbers.
multiply_digits = (second_to_last_number*2)+(fourth_number*2)+(sixth_number*2)+(eighth_number*2)+(tenth_number*2)+(twelfth_number*2)+(fourteenth_number*2)+(sixteenth_number*2);
}
Try doing this instead
int main(){
long cNo = 4003600000000014;
int arr[16];
for(int i=0; i<16; i++){
arr[15-i] = cNo % 10;
cNo /= 10;
}
int multipliedSum = 0;
for(int i=0; i<16; i++){
if(i%2==1)
multipliedSum += arr[i];
else{
if(arr[i]*2<10){
multipliedSum += (arr[i]*2);
}else{
int num = arr[i]*2;
while(num){
multipliedSum += num%10;
num/=10;
}
}
}
}
printf("valid = %s\n",multipliedSum%10==0?" True": " False");
}
You will get the following
valid = True
A general algorithm for adding digits (assuming an integer type):
Initialize your sum to 0: sum = 0
Extract the lowest digit from the number using the % modulus operator: digit = number % 10
Add the value of that digit to the sum: sum += digit (shorthand for sum = sum + digit)
Divide the number by 10: number /= 10 (shorthand for number = number / 10
If the number is non-zero after dividing by 10, go back to 2
End
The modulus operator % returns the integer remainder of an integer division - 123 / 10 == 12 rem 3. So the remainder of dividing the number by 10 is the least significant decimal digit of the number. Notice that integer division gives you an integer result - 123 / 10 == 12, not 12.3.
You'll want to put this in a separate function, so you can write something like
int sumdig( int v )
{
...
}
int main( void )
{
int value = 123;
int sum = sumdig( value ); // sumdig will return 1 + 2 + 3, or 6
...
}
When you find yourself creating a bunch of separate variables of the same type with the same name except for some tacked-on ordinal (var1, var2, var3 or first_thing, second_thing, third_thing), that's a real strong hint you want to use an array. You can use an array to store the individual digits of your card number:
int number[16];
and use the % 10 method as described above to extract the individual digits:
long tmp = credit_card_number; // use a temporary so we preserve the original card number
for ( int i = 0; i < 16; i++ )
{
number[i] = tmp % 10;
tmp /= 10;
}
This means that the least significant (rightmost) card number digit will be stored in number[0] and the most significant (leftmost) card number digit will be stored in number[15], so be aware of that. For the purposes of validating the number it doesn't matter, but if you want to display the contents of the array you'll have to take that into account.
Using an array makes it easier to extract subsets of digits:
for ( int i = 1; i < 16; i += 2 ) // hit every other element starting at element 1
{
number[i] *= 2; // multiply these digits by 2
}
That loop above executes the "1•2 + 0•2 + 0•2 + 0•2 + 0•2 + 6•2 + 0•2 + 4•2" portion of your algorithm.
You should be able to figure out the rest from there. Hope this helps.
Hint: to extract one digit from a number, mod it by 10.
So say that you want to figure out the sum of the digits of a number, say 123456, you will do the following:
(pseudocode)
number=123456;
sum=0;
loop if number is not 0{
sum+=number % 10;
number-=number % 10;
number=(int)(number/10);
}
Now try to implement it as a function, say digit(), and when you are trying to add some numbers digit-wise, say 123 and 456, just do digit(123)+digit(456) instead.
I have a question, how to get a result of (2^100)*(3^5) in modulo 10^9 + 7? The program will ask the user to input the power (2^a) and 3^b, after that, the output will show the result of 2^a * 3^b.
I tried to convert all the big numbers into modulo, and times the modulo. But, it doesnt work for 2*100 * 3^5
#include "stdio.h"
int main()
{
long long int testcase,b,c,N,a;
long long int pow2,pow3 = 1;
long long int m = 1000000007;
// input the power
scanf("%lld",&a); getchar();
scanf("%lld",&b); getchar();
// power of 2 (2^a)
for(int i = 1; i <= a; i++){
pow2 = pow2 * 2;
}
// power of 3 (3^b)
for(int j = 1; j <= b; j++){
pow3 = pow3 * 3;
}
// convert the big numbers into modulo
long long int i = 1;
i = (1*pow2) % m ;
long long int j = 1;
j = (1*pow3) % m;
// the result of first modulo times second modulo
printf("%lld\n", i*j);
// doesnt work for 2^100 * 3^5
return 0;
}
For a = 2 and b = 5 its gives the output of 972 (which is correct)
for a = 100 and b = 3 its gives 0 output.
Firstly, pow2 is uninitialized and therefore the behaviour is undefined. If initialized to 1, then the problem is that 2^100 does not fit in the long long int. The best fix is to take the modulo as often as possible.
// power of 2 (2^a)
for(int i = 1; i <= a; i++){
pow2 *= 2;
pow2 %= m;
}
// power of 3 (3^b)
for(int j = 1; j <= b; j++){
pow3 *= 3;
pow3 %= m;
}
Notice that this is still suboptimal - it is possible to calculate much larger powers by using exponentiation by squaring.
Finally you must note that the last product must be mod 1000000007 too, otherwise the result is larger than expected:
printf("%lld\n", i * j % m);
I need to redo printf for a projet, so I actually have a problem with the conversion of float.
I managed to convert almost everything but for the number 1254451555.6
I got an issue: I got 1254451555.59999.
I think it's the calculation to keep the part after the . that doesnt work.
nbr = ((n - nbr) * 100000000);
I tried different things but I haven't managed to fix it yet.
Do you have any idea?
int getlenghtitoa(long long n, int nbase)
{
int i;
i = 0;
while (n >= 0)
{
n /= nbase;
i++;
if (n == 0)
break ;
}
return (i);
}
float ft_nbconv(float n, int i)
{
while (i-- > 0)
n = n *10;
return (n);
}
int ft_power(long long nbr)
{
int i;
i = 1;
while(nbr > 10)
{
i *= 10;
nbr = nbr / 10;
}
return (i);
}
char *ft_conver_f(long double n)
{
char *dest;
int i;
int a;
long long int nbr;
int power;
nbr = (long long) n;
i = getlenghtitoa((long long )n, 10);
if (!(dest = malloc(sizeof(char) * (i + 8))))
return (0);
a = i;
i = 0;
power = ft_power(nbr);
while (a--)
{
dest[i++] = ((nbr / power) % 10) + '0';
if (power != 1)
power /= 10;
}
dest[i++] = '.';
nbr = ((n - nbr) * 100000000);
power = 10000000;
while (a++ < 5)
{
if (a == 5)
if ((((nbr / power)) % 10) >= 5)
{
dest[i++] = ((nbr / power) % 10 + 1) + '0';
break;
}
dest[i++] = ((nbr / power) % 10) + '0';
power /= 10;
}
dest[i] = '\0';
return (dest);
}
Most decimal fractions cannot be represented exactly as binary fractions. A consequence is that, in general, the decimal floating-point numbers you enter are only approximated by the binary floating-point numbers actually stored in the machine.
That's why when implementing a printf, the only way to really be able to convert a floating number to a 2-seperated-by-point integers, is by using the precision factor and rounding manually.
If you are not required to implement the precision, the default is 6.
(Precision is the number of places to print after the dot (and it's rounded)).
And that's what's missing in your implementation.
Let's call the digits before the dot the ipart and the digits after the fpart .
nbr = ((n - nbr) * 100000000);
This should be
nbr = ((n - nbr) * 10000000); // 7 zeros
// nbr is now equal to 5999999
if (nbr % 10 >= 5)
{
nbr = nbr / 10 + 1;
}
else
nbr = nbr / 10;
This way, you get 7 digits after the dot, see if the last one is higher than 5, if it is, you add +1 to nbr (after dividing by 10 to make sure nbr has 6 digits), if it's not, you just divide by 10.
One more note about this rounding method, It will not be able to carry the rounding from the fpart to the ipart .
what if you want to print 3.9999999 ? It should print 4.000000. That means that can't just convert the ipart to a string from the beginning, because sometimes rounding the fpart will add +1 to your ipart
So think about creating a function ltoa for example that takes a long long int and converts it to a string, complete the piece of code about rounding i just gave you to make sure rounding can be carried to the ipart , then convert the whole thing to string using something like
dest = join(ltoa(ipart), ".", ltoa(fpart)).
A couple more notes, your function does not handle negative numbers.
And your int ft_pow can be easily flooded, so consider changing to long long ft_pow
I have to find the sum of the first 4 digits, the sum of the last 4 digits and compare them (of all the numbers betweem m and n). But when I submit my solution online there's a problem with the time limit.
Here's my code:
#include <stdio.h>
int main()
{
int M, N, res = 0, cnt, first4, second4, sum1, sum2;
scanf("%d", &M);
scanf("%d", &N);
for(cnt = M; cnt <= N; cnt++)
{
first4 = cnt % 10000;
sum1 = first4 % 10 + (first4 / 10) % 10 + (first4 / 100) % 10 + (first4 / 1000) % 10;
second4 = cnt / 10000;
sum2 = second4 % 10 + (second4 / 10) % 10 + (second4 / 100) % 10 + (second4 / 1000) % 10;
if(sum1 == sum2)
res++;
}
printf("%d", res);
return 0;
}
I'm trying to find a more efficient way to do this.
Finally, if you are still interested, there is a much faster way to do this.
Your task doesn't specifically require you to calculate the sums for all the numbers,
it only asks for the number of some special numbers.
In such cases optimization techniques like memoization or dynamic programming come really handy.
In this case, when you have the first four digits of some number (let them be 1234),
you calculate their sum (in this case 10) and you immediately know,
what is the sum of the other four digits supposed to be.
Any 4-digit number, that yields sum 10 can now be the other half to create a valid number.
Therefore total number of valid numbers beginning with 1234 is exactly the number of all four digit numbers that give the sum 10.
Now consider another number, say 3412. This number has also sum equal to 10,
therefore any right-side that completes 1234 also completes 3412.
What this means is that the number of valid numbers beginning with 3412 is the same
as the number of valid numbers beginning with 1234, which is in turn the same as the total number of valid numbers, where the first half yields the sum 10.
Therefore if we precompute for each i the number of four digit numbers
that yield the sum i, we would know for each first four digits the exact number of
combinations of last four digits that complete a valid number,
without having to iterate over all 10000 of them.
The following implementation of this algorithm
Precomputes number of different ending halves for each sum of the beginning half
Splits the [M,N] interval in three subintervals, because in the first and the last beginning not every ending is possible
This algorithm runs quadratically faster than the naive implementation (for sufficiently big N-M).
#include <string.h>
int sum_digits(int number) {
return number%10 + (number/10)%10 + (number/100)%10 + (number/1000)%10;
}
int count(int M, int N) {
if (M > N) return 0;
int ret = 0;
int tmp = 0;
// for each i from 0 to 36 precompute number of ways we can get this sum
// out of a four-digit number
int A[37];
memset(A, 0, 37*4);
for (int i = 0; i <= 9999; ++i) {
++A[sum_digits(i)];
}
// nearest multiple of 10000 greater than M
int near_M = ((M+9999)/10000)*10000;
// nearest multiple of 10000 less than N
int near_N = (N/10000)*10000;
// count all numbers up to first multiple of 10000
tmp = sum_digits(M/10000);
if (near_M <= N) {
for (int i = M; i < near_M; ++i) {
if (tmp == sum_digits(i % 10000)) {
++ret;
}
}
}
// count all numbers between the 10000 multiples, use the precomputed values
for (int i = near_M / 10000; i < near_N / 10000; ++i) {
ret += A[sum_digits(i)];
}
// count all numbers after the last multiple of 10000
tmp = sum_digits(N / 10000);
if (near_N >= M) {
for (int i = near_N; i <= N; ++i) {
if (tmp == sum_digits(i % 10000)) {
++ret;
}
}
}
// special case when there are no multiples of 10000 between M and N
if (near_M > near_N) {
for (int i = M; i <= N; ++i) {
if (sum_digits(i / 10000) == sum_digits(i % 10000)) {
++ret;
}
}
}
return ret;
}
EDIT: I fixed the bugs mentioned in the comments.
I don't know if this would be significantly faster or not, but you might try breaking the number into two 4 digit numbers, then use a table lookup to get the sums. That way there's only one division operation instead of eight.
You can pre-compute the table of 10000 sums so it gets compiled in so there's no runtime cost at all.
Another slightly more complicated, but probably much faster, approach that can be used is have a table or map of 10000 elements that's the reverse of the sum lookup table where you can map the sum to the set of four digit numbers that would produce that sum. That way, when you have to find the result for a particular range 10000 number range, it's a simple lookup on the sum of the most significant four digits. For example, to find the result for the range 12340000 - 12349999, you could use a binary search on the reverse lookup table to quickly find how many numbers in the range 0 - 9999 have the sum 10 (1 + 2 + 3 + 4).
Again - this reverse sum lookup table can be pre-computed and compiled in as a static array.
In this way, the results for complete 10000 number ranges are performed with a couple binary searches. Any partial ranges can also be handled with the reverse lookup table with slightly more complication due to having to ignore matches that are from out of the range of interest. But that complication only has to happen at most twice for your whole set of subranges.
This would reduce the complexity of the algorithm from O(N*N) to O(N log N) (I think).
update:
Here's some timings I got (Win32-x86, using VS 2013 (MSVC 12) with release build default options):
range range
start end count time
================================================
alg1(10000000, 99999999): 4379055, 1.854 seconds
alg2(10000000, 99999999): 4379055, 0.049 seconds
alg3(10000000, 99999999): 4379055, 0.001 seconds
with:
alg1() is the original code from the question
alg2() is my first cut suggestion (lookup precomputed sums)
alg3() is the second suggestion (binary search lookup of sum matches using a table sorted by sums)
I'm actually surprised at the difference between alg1() to alg2()
You are going about this the wrong way. A little bit of cleverness is worth a lot of horsepower. You should not be comparing the first and last four digits of every number.
First - notice that the first four digits will change very slowly - so for sure you can have a loop of 10000 of the last four digits without re-computing the first sum.
Second - the sum of digits repeats itself every 9th number (until you get overflow). This is the basis of the "number is divisible by 9 if sum of digits is divisible by 9". example:
1234 - sum = 10
1234 + 9 = 1243 - sum is still 10
What this means is that the following will work pretty well (pseudo code):
take first 4 digits of M, find sum (call it A)
find sum of last four digits of M (call it B)
subtract: C = (A - B)
If C < 9:
D = C%9
first valid number is [A][B+D]. Then step by 9, until...
You need to think a bit about the "until", and also about what to do when C >= 9. This means you need to find a zero in B and replace it with a 9, then repeat the above.
If you want to do nothing else, then see that you don't need to re-compute the sum of digits that did not change. In general when you add 1 to a number, the sum of digits increases by 1 (unless there is carry - then it decreases by 9; and that happens every 9th, 99th (twice -> sum drops by 18), 999th (drop by 27), etc.
I hope this helps you think about the problem differently.
I am going to try an approach which doesn't make use of the lookup table (even though I know that the second one should be faster) to investigate how much we can speedup just optimizing calculus. This algorithm can be used where stack is an important resource...
Let's work on the idea that divisions and modulus are slow, for example in cortex R4 a 32 bit division requires up to 16 loops while a multiplication can be done in a single loop, with older ARMs things can be even worse.
This basic idea will try to get rid of them using digit arrays instead of integers. To keep it simple let's show an implementation using printf before a pseudo optimized version.
void main() {
int count=0;
int nmax;
char num[9]={0};
int n;
printf( "Insert number1 ");
scanf( "%d", &nm );
printf( "Insert number2 ");
scanf( "%d", &nmax );
while( nm <= nmax ) {
int sumup=0, sumdown=0;
sprintf( num, "%d", nm );
for( n=0; n<4; n++ ) {
sumup += num[n] -'0'; // subtracting '0' is not necessary (see below)
sumdown += num[7-n]-'0'; // subtracting '0' is not necessary (see below)
}
if( sumup == sumdown ) {
/* whatever */
count++;
}
nm++;
}
}
You may want to check that the string is a valid number using strtol before calling the for loop and the length of the string using strlen. I set here fixed values as you required (I assume length always 8).
The downside of the shown algorithm is the sprintf for any loop that may do thing worse... So we apply two major changes
we use [0-9] instead of ['0';'9']
we drop the sprintf for a faster solution which takes in account that we need to format a digit string starting from the previous number (n-1)
Finally the pseudo optimized algorithm should look something like the one shown below in which all divisions and modules are removed (apart from the first number) and bytes are used instead of ASCII.
void pseudo_optimized() {
int count=0;
int nmax,nm;
char num[9]={0};
int sumup=0, sumdown=0;
int n,i;
printf( "Insert number1 ");
scanf( "%d", &nm );
printf( "Insert number2 ");
scanf( "%d", &nmax );
n = nm;
for( i=7; i>=0; i-- ) {
num[i]=n%10;
n/=10;
}
while( nm <= nmax ) {
sumup = num[0] + num[1] + num[2] + num[3];
sumdown = num[7] + num[6] + num[5] + num[4];
if( sumup == sumdown ) {
/* whatever */
count++;
}
nm++;
/* Following loop is a faster sprintf replacement and
* it will exit at the first value 9 times on 10
*/
for( i=7; i>=0; i-- ) {
if( num[i] == 9 ) {
num[i]=0;
} else {
num[i] += 1;
break;
}
}
}
}
Original algo on my vm 5.500000 s, this algo 0.950000 s tested for [00000000=>99999999]
The weak point of this algorithm is that it uses sum of digits (which are not necessary and a for...loop that can be unrolled.
* update *
further optimization. The sums of digits are not necessary.... thinking about it I could improve the algorithm in the following way:
int optimized() {
int nmax=99999999,
int nm=0;
clock_t time1, time2;
char num[9]={0};
int sumup=0, sumdown=0;
int n,i;
int count=0;
n = nm;
time1 = clock();
for( i=7; i>=0; i-- ) {
num[i]=n%10;
n/=10;
}
sumup = num[0] + num[1] + num[2] + num[3];
sumdown = num[7] + num[6] + num[5] + num[4];
while( nm <= nmax ) {
if( sumup == sumdown ) {
count++;
}
nm++;
for( i=7; i>=0; i-- ) {
if( num[i] == 9 ) {
num[i]=0;
if( i>3 )
sumdown-=9;
else
sumup-=9;
} else {
num[i] += 1;
if( i>3 )
sumdown++;
else
sumup++;
break;
}
}
}
time2 = clock();
printf( "Final-now %d %f\n", count, ((float)time2 - (float)time1) / 1000000);
return 0;
}
with this we arrive to 0.760000 s which is 3 times slower than the result achieved on the same machine using lookup tables.
* update* Optimized and unrolled:
int optimized_unrolled(int nm, int nmax) {
char num[9]={0};
int sumup=0, sumdown=0;
int n,i;
int count=0;
n = nm;
for( i=7; i>=0; i-- ) {
num[i]=n%10;
n/=10;
}
sumup = num[0] + num[1] + num[2] + num[3];
sumdown = num[7] + num[6] + num[5] + num[4];
while( nm <= nmax ) {
if( sumup == sumdown ) {
count++;
}
nm++;
if( num[7] == 9 ) {
num[7]=0;
if( num[6] == 9 ) {
num[6]=0;
if( num[5] == 9 ) {
num[5]=0;
if( num[4] == 9 ) {
num[4]=0;
sumdown=0;
if( num[3] == 9 ) {
num[3]=0;
if( num[2] == 9 ) {
num[2]=0;
if( num[1] == 9 ) {
num[1]=0;
num[0]++;
sumup-=26;
} else {
num[1]++;
sumup-=17;
}
} else {
num[2]++;
sumup-=8;
}
} else {
num[3]++;
sumup++;
}
} else {
num[4]++;
sumdown-=26;
}
} else {
num[5]++;
sumdown-=17;
}
} else {
num[6]++;
sumdown-=8;
}
} else {
num[7]++;
sumdown++;
}
}
return count;
}
Unrolling vectors improves the speed of about 50%. The algorithm costs now 0.36000 s, by the way it makes use of the stack a bit more than the previous solution (as some 'if' statements may result in a push, so it cannot be always used). The result is comparable with Alg2#Michael Burr on the same machine, [Alg3-Alg5]#Michael Burr are a lot faster where stack isn't a concern.
Note all test where performed on a intel VMS. I will try to run all those algos on a ARM device if I will have time.
#include <stdio.h>
int main(){
int M, N;
scanf("%d", &M);
scanf("%d", &N);
static int table[10000] = {0,1,2,3,4,5,6,7,8,9};
{
register int i=0,i1,i2,i3,i4;
for(i1=0;i1<10;++i1)
for(i2=0;i2<10;++i2)
for(i3=0;i3<10;++i3)
for(i4=0;i4<10;++i4)
table[i++]=table[i1]+table[i2]+table[i3]+table[i4];
}
register int cnt = M, second4 = M % 10000;
int res = 0, first4 = M / 10000, sum1=table[first4];
for(; cnt <= N; ++cnt){
if(sum1 == table[second4])
++res;
if(++second4>9999){
second4 -=10000;
if(++first4>9999)break;
sum1 = table[first4];
}
}
printf("%d", res);
return 0;
}
If you know that the numbers are fixed like that, then you can you substring functions to get the components and compare them. Otherwise, your modulator operations are contributing unnecessary time.
i found faster algorithm:
#include <stdio.h>
#include <ctime>
int main()
{
clock_t time1, time2;
int M, N, res = 0, cnt, first4, second4, sum1, sum2,last4_ofM,first4_ofM,last4_ofN,first4_ofN,j;
scanf("%d", &M);
scanf("%d", &N);
time1 = clock();
for(cnt = M; cnt <= N; cnt++)
{
first4 = cnt % 10000;
sum1 = first4 % 10 + (first4 / 10) % 10 + (first4 / 100) % 10 + (first4 / 1000) % 10;
second4 = cnt / 10000;
sum2 = second4 % 10 + (second4 / 10) % 10 + (second4 / 100) % 10 + (second4 / 1000) % 10;
if(sum1 == sum2)
res++;
}
time2 = clock();
printf("%d\n", res);
printf("first algorithm time: %f\n",((float)time2 - (float)time1) / 1000000.0F );
res=0;
time1 = clock();
first4_ofM = M / 10000;
last4_ofM = M % 10000;
first4_ofN = N / 10000;
last4_ofN = N % 10000;
for(int i = first4_ofM; i <= first4_ofN; i++)
{
sum1 = i % 10 + (i / 10) % 10 + (i / 100) % 10 + (i / 1000) % 10;
if ( i == first4_ofM )
j = last4_ofM;
else
j = 0;
while ( j <= 9999)
{
sum2 = j % 10 + (j / 10) % 10 + (j / 100) % 10 + (j / 1000) % 10;
if(sum1 == sum2)
res++;
if ( i == first4_ofN && j == last4_ofN ) break;
j++;
}
}
time2 = clock();
printf("%d\n", res);
printf("second algorithm time: %f\n",((float)time2 - (float)time1) / 1000000.0F );
return 0;
}
i just dont need to count sum of the first four digits all the time the number in changed. I need to count it one time per 10000 iterations. In worst case output is:
10000000
99999999
4379055
first algorithm time: 5.160000
4379055
second algorithm time: 2.240000
about half the better result.