Codewars Question: (Sum of Digits / Digital Root)
Given n, take the sum of the digits of n. If that value has more than one digit, continue reducing in this way until a single-digit number is produced. The input will be a non-negative integer.
Test Cases:
16 --> 1 + 6 = 7
942 --> 9 + 4 + 2 = 15 --> 1 + 5 = 6
132189 --> 1 + 3 + 2 + 1 + 8 + 9 = 24 --> 2 + 4 = 6
493193 --> 4 + 9 + 3 + 1 + 9 + 3 = 29 --> 2 + 9 = 11 --> 1 + 1 = 2
My code:
#include <bits/stdc++.h>
using namespace std;
int singleDigit(int n)
{
int ans;
while (n > 0)
{
int lastDigit = n % 10;
n /= 10;
ans += lastDigit;
}
while (ans > 9)
{
int n1 = ans;
ans = 0;
while (n1 > 0)
{
int lastDigit = n1 % 10;
n1 /= 10;
ans += lastDigit;
}
}
return ans;
}
int main()
{
cout << singleDigit(49319366) << endl;
return 0;
}
Is there a better or optimized way to solve this problem or to reduce time complexity?
This function works for non-negative integers, adapting for negative numbers is straightforward.
int singleDigit(int n)
{
return (n-1) % 9 + 1;
}
It has the following advantages:
no variables to forget to initialise
no loops to commit an off-by-one error
fast
The disadvantages are:
it is not immediately clear how or why it works
For more information on the last bullet point, see:
Direct formulas for the digital root
Modulo operation with negative numbers
Related
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'm trying to implement Luhn's algorithm. For every other digit you have to take it, multiply it by 2, and add it to a running total. If the ith digit * 2 is greater than 10, you split it up, and just add it to the running total. I create a void function that does this and pass in 123456789 as a test value.
#include <stdio.h>
// #include <cs50.h> //
void LuhnsAlgorithm(long); // needs to be changed to str return type when submitted
int main()
{
int num = 123456789;
LuhnsAlgorithm(num);
}
void LuhnsAlgorithm(long cc) // return type will be str when submitting
{
int sum = 0;
// case 1
// case 2 (every other odd digit, multiplied by 2, and then added)
long case2 = cc;
while (case2 > 0)
{
if ((case2 / 10 % 10) * 2 >= 10) // checks if the every-other-digit's product when multiplied by 2 is bigger than 10 (aka, has 2 digits)
{
// creating this stupid temp variable
int digitBreakUp = case2 / 10 % 10 * 2;
sum += (digitBreakUp / 10) + (digitBreakUp % 10);
}
else // if the product is just 1 digit then add it to the sum
{
sum += (case2 / 10 % 10) * 2;
}
case2 = case2 / 10;
}
printf("The sum of every last other digit in 123456789 is %i\n", sum);
}
The expected sum should be 22 (8 --> 1 + 6, 6 --> 1 + 2, 4 --> 8, 2 --> 4). But, I get 36. What's wrong? How do I get to 0 / get it to 'stop iterating' until it reaches the beginning of the number?
Thanks
Since your test is for every other digit, the line of code:
case2 = case2 / 10;
Should be:
case2 = case2 / 100;
If you do that, your summation of every other digit does come out to 22.
Give that a try.
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.
So this should do Luhn's algorithms needs from Harward cs50 pset1. But it has some flow in it which ı could not find. Normally the while loop's condition is not coun it 8 times but while testing it ı thought: first ı should solve the flow in functions work. Luhn's Algorithm work as the following for the ones who can't recall:
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!
And that's the sample input:
That’s kind of confusing, so let’s try an example with David’s Visa: 4003600000000014.
For the sake of discussion,
let’s first underline every other digit,
starting with the number’s second-to-last digit:
4003600000000014
Okay, let’s multiply each of the underlined 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 David’s card is legit!
And this is my function.
bool checkdigits(long long x)
{ int power();
int checklength();
long counter,sum,summ2,multp2,index;
sum=0;
summ2=0;
index=1;
counter=0;
while(!(counter == 8))
{
sum+=x/power(10,index)%10;
multp2=(x/power(10,index))%10*2;
if(multp2 >= 10)
{summ2 += (multp2 % 10)+1;}
else if(multp2 < 10)
{summ2 += multp2 % 10;}
index = index+2;
counter++;
}
if((sum + summ2)%10 == 0)
{return true;}
else
{return false;}
}
As Weather Vane pointed out, it's easier to use a string instead:
bool checkdigits(const char* number)
{
long long sum1 = 0, sum2 = 0;
for (int i = strlen(number) - 1; i >= 0; i -= 2) // single add every second
sum1 += number[i] - '0';
for (int i = strlen(number) - 2; i >= 0; i -= 2) // double add every other second
sum2 += 2 * (number[i] - '0') - ((number[i] - '0') > 4 ? 9 : 0);
// number > 4 => 2 * number > 10
// therefore subtract 9
return (sum1 + sum2) % 10 == 0;
}
I don't know cs50 but that should work.
Days ago I had a job interview were they ask me how I would calculate the sum of all the numbers multiples of 2 or 5 from 1 to 10000 using a the c language. I did this:
int mult2_5()
{
int i, sum=0;
for(i = 1; i <= 10000; i++)
if(i % 2 == 0 || i % 5 == 0)
sum += i;
return sum;
}
I as wonder if it was any faster implementation that this one?
The modulus operator is inefficient. A more faster implementation would be something like this:
int multiply2_5(int max)
{
int i, x2 = 0,x5 = 0,x10 = 0;
for(i = 2; i < max; i+=2) x2 += i; // Store all multiples of 2 O(max/2)
for(i = 5; i < max; i+=5) x5 += i; // Store all multiples of 3 O(max/5)
for(i = 10; i < max; i+=10) x10 += i; // Store all multiples 10; O(max/10)
return x2+x5-x10;
}
In this solution I had to take out multiples of 10 because, 2 and 5 have 10 as multiple so on the second loop it will add multiples of 10 that already been added in the first loop; The three loops combine have O(8/10 max).
Another even better solution is if you take a mathematical approach.
You are trying to sum all numbers like this 2 + 4 + 6 + 8 ... 10000 and 5 + 10 + 15 +20 + ... 10000 this is the same of having 2 * (1 + 2 + 3 + 4 + … + 5000) and 5 * ( 1 + 2 + 3 + 4 + ... + 2000), the sum of 'n' natural number is (n * (n + 1)) (source) so you can calculate in a constant time, as it follows:
int multiply2_5(int max)
{
// x = 2 + 4 + 6 + ... = 2 * (1 + 2 + 3 +...)
// y = 5 + 10 + 15 + ... = 5 * (1 + 2 + 3 +...)
// The sun of n natural numbers is sn = (n (n + 1)) / 2
int x2 = max/ 2; // 2 * ( 1 +2 + 3 … max/2)
int x5 = max /5; // 5 * ( 1 +2 + 3 … max/5)
int x10 = max/ 10;
int sn2 = 0.5 * (x2 * (x2+1)); // (n * (n + 1)) / 2
int sn5 = 0.5 * (x5 * (x5+1));
int sn10 = 0.5 * (x10 * (x10+1));
return (2*sn2) + (5 *sn5) - (10*sn10);
}
As mentioned in an earlier answer, explicitly looping through the relevant multiples is better than testing the remainder each loop. But it is not necessary to calculate the multiples of 10 and subtract. Just start at 5 and step by 10 to skip them all together.
int multiply2_5b(int max)
{
int i, x2 = 0,x5 = 0;
for (i = 2; i < max; i += 2) x2 += i; // Sum all multiples of 2
for (i = 5; i < max; i += 10) x5 += i; // Sum all odd multiples of 5
return x2 + x5;
}
Just work it out on paper first, if that's what you mean by "faster".
$2\sum_{1<=2k<=10000}k + 5\sum_{1<=5k<=10000} - 10\sum_{1<=10k<=10000}k$
Sorry, my SO equation-fu is weak...Anyway this route will give you something you can almost handle on paper: 5000*6001 after reducing a few steps
int
mult2_5(void)
{
return 5000*6001;
}
Project Euler problem 1 is very similar. There's lots of folks who've posted their solution to this one.
This can just be done using math. Something like 2 * sum(1 to 5000) + 5 * sum(1 to 2000) - 10 * sum(1 to 1000). off-by-one errors left as exercise.
I almost got to a pure and simple multiplication by doing a simple loop that starts with 35 (sum of 2 + 4 + 5 + 6 + 8 + 10) with a step of 60, as that's how much your result will increase by when you take the next lot e.g. 12 + 14 + 15 + 16 + 18 + 20 etc. for(int i=35;i<5976;i=i+60) { sum=sum+i }
The 5976 comes from 5975 being the last row of numbers that end in 1000 i.e. 992 + 994 + 995 + 996 + 998 + 1000.
So it turns out that this loop runs 100 times, increasing the sum by 35 the first turn and increasing by 60 the remaining 99 times. Which is now reducable to a simple multiplication or such.