I am trying to make a recursive function in C that calculates the sum of digits in 2ⁿ, where n < 10⁷. I made something that works, but it's very slow (for n = 10⁵ it takes 19 seconds). The function must return the sum in maximum 1 second. My algorithm calculates 2ⁿ using arrays to store its digits and it's not using a recursive function.
Is there any way to compute this sum of digits without calculating 2ⁿ? Or a faster way to calculate 2ⁿ and its digits sum?
P.S.: The recursive function must get only the n
parameter, i.e. int f(int n);
Late edit: I wrote a recursive solution; it is faster, but it doesn't work for n > 10⁵.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int sumOfDigits(int* num, int n) {
if (n == 0) {
int sum = 0;
for (int i = 1; i <= num[0]; ++i) {
while (num[i] > 0) {
sum += num[i] % 10;
num[i] /= 10;
}
}
return sum;
}
int carry = 0;
for (int i = 1; i <= num[0]; ++i) {
num[i] = num[i] * 2 + carry;
carry = num[i] / 1000000000;
num[i] %= 1000000000;
if (carry != 0 && i == num[0]) {
++num[0];
}
}
return sumOfDigits(num, n - 1);
}
int main (void) {
int n = 100000;
int size = (n*log10(2) + 1) / 9 + 2;
int* num = calloc(size, sizeof(int));
num[0] = 1;
num[1] = 1;
printf("\n%d", sumOfDigits(num, n));
free(num);
return 0;
}
It seems that the posted code is using an "implicit" arbitrary precision type (with "digits" in the range [0, 999999999]) to recursively calculate all the multiplication by 2, which means, for e.g. n = 100, to perform 100 times those expansive calculation.
It should be more efficient (O(log(n)) instead of O(n)) to perform each time a multiplication of the number by itself or by 2, based on whether the exponent is even or odd. E.g. 27 = 2 * (23 * 23).
Another approach would be to explicitly implement a Bing Int type, but with a binary underlying type (say a uint32_t
). It would be trivial to calculate 2n, it'd be just an array of zeroes with a final power of two (again, just one non-zero bit).
Now, to get the sum of the (base 10) digits you need to transform that number in base, say 100000000 (like the OP did), and to do that, you have to implement a long subtraction between two Big Ints and a long division by 100000000, which will give you the remainder too. Use that remainder to calculate the partial sum of the digits and iterate.
The following is a minimal implementation, testable here.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#define D_BASE 1000000
#define MSB_MASK 1 << 31
typedef struct
{
uint32_t size;
uint32_t capacity;
uint32_t *digits;
} BigInt;
void divide_bigint(BigInt *n, uint32_t x, uint32_t *remainder);
BigInt *make_bigint_of_two_raised_to(uint32_t n)
{
BigInt *p = malloc(sizeof *p);
if (!p)
{
perror("Fatal error");
exit(1);
}
uint32_t pos = n / 32;
uint32_t remainder = n % 32;
uint32_t capacity = (remainder == 31) ? pos + 2 : pos + 1;
uint32_t *pp = calloc(capacity, sizeof *pp);
if (!pp)
{
perror("Error initializing a Big Int as a power of two");
free(p);
exit(1);
}
p->capacity = capacity;
p->size = capacity;
pp[pos] = 1u << remainder;
p->digits = pp;
return p;
}
void free_bigint(BigInt **p);
uint64_t sum_of_digits_of_two_raised_to_the_power(uint32_t n)
{
BigInt *power_of_two = make_bigint_of_two_raised_to(n);
uint32_t remainder;
uint64_t sum = 0;
while (!(power_of_two->size == 1 && power_of_two->digits[0] == 0))
{
divide_bigint(power_of_two, 1000000000, &remainder);
while (remainder)
{
sum += remainder % 10;
remainder /= 10;
}
}
free_bigint(&power_of_two);
return sum;
}
void test(uint32_t n)
{
uint64_t sum = sum_of_digits_of_two_raised_to_the_power(n);
printf("Sum of digits of 2^%d: %" PRIu64 "\n", n, sum);
}
int main(void)
{
test(5);
test(10);
test(1000);
test(10000);
test(100000);
test(1000000);
return 0;
}
void shrink_size(BigInt *n)
{
while ( n->size > 1 )
{
if ( n->digits[n->size - 1] == 0 && !(n->digits[n->size - 2] & MSB_MASK) )
--n->size;
else
break;
}
}
void divide_bigint(BigInt *n, uint32_t x, uint32_t *remainder)
{
uint64_t carry = 0;
uint32_t i = n->size;
while ( i-- > 0 )
{
carry <<= 32;
carry += n->digits[i];
if ( carry < x )
{
n->digits[i] = 0;
continue;
}
uint64_t multiplier = (carry / x);
carry -= multiplier * x;
n->digits[i] = (uint32_t)multiplier;
}
shrink_size(n);
*remainder = carry;
}
void free_bigint(BigInt **p)
{
if (p && *p)
{
free((*p)->digits);
free(*p);
*p = NULL;
}
}