A bit curtailed, but time reaches practical zero:
private static long getSumOfSumOfDigitsBelow(long num) { if (num == 0) return 0; // 1 -> 1 ; 12 -> 10; 123 -> 100; 321 -> 100, ... int pow10 = (int)Math.Pow(10, Math.Floor(Math.Log10(num))); long firstDigit = num / pow10; long sum = 0; var sum999 = getSumOfSumOfDigitsBelow(pow10 - 1); var sumRest = getSumOfSumOfDigitsBelow(num % pow10); sum += (firstDigit - 1)*(firstDigit - 0)/2*pow10 + firstDigit*sum999; sum += firstDigit*(num%pow10 + 1) + sumRest; return sum; } getSumOfSumOfDigitsBelow(123456789) -> 4366712385 (80us) getSumOfSumOfDigitsBelow(9223372036854775807) -> 6885105964130132360 (500us - unverified)
The trick is to avoid repeating the same answer over and over. for example 33:
your approach:
sum = 1+2+3+4+5+6+7+8+9+(1+0)+(1+1)+(1+2)+ ... +(3+2)+(3+3)
my approach:
sum = 10*(0 + (1+2+3+4+5+6+7+8+9)) + 10*(1 + (1+2+3+4+5+6+7+8+9)) + 10*(2 + (1+2+3+4+5+6+7+8+9)) + 3*(3 + (1 + 2 + 3))
(1+2+3+4+5+6+7+8+9) -part should only be calculated once. The 0..firstDigit-1 loop can be avoided with n(n-1)/2 -trick. Hope this makes sense.
The complexity is O(2^N) with N counting the number of digits. It looks very bad, but fast enough for your threshold of 5 seconds, even for long-max. Perhaps this algorithm can be converted to something running in O(n) by calling getSumOfSumOfDigitsBelow() only once, but it would look a lot more complicated.
The first optimization step: look at your algorithm;)
Returning to this problem after DineMartine's answer:
To further optimize the algorithm, sum999 -part can be replaced by an explicit formula. Let's take some number 9999...9=10^k-1 in the code and replace it accordingly:
sum(10^k-1) = (9 - 1)*(9 - 0)/2*pow10 + 9*sum999 + 9*(num%pow10 + 1) + sumRest sum(10^k-1) = 36*pow10 + 9*sum999 + 9*(num%pow10 + 1) + sumRest
sum999 and sumRest same for numbers like 10^k :
sum(10^k-1) = 36*pow10 + 10*sum(10^(k-1)-1) + 9*(num%pow10 + 1) sum(10^k-1) = 36*pow10 + 10*sum(10^(k-1)-1) + 9*((10^k-1)%pow10 + 1) sum(10^k-1) = 36*pow10 + 10*sum(10^(k-1)-1) + 9*pow10 sum(10^k-1) = 45*pow10 + 10*sum(10^(k-1)-1)
We have the definition of sum(10^k-1) and we know sum(9)=45 . And we get:
sum(10^k-1) = 45*k*10^k
Updated code:
private static long getSumOfSumOfDigitsBelow(long num) { if (num == 0) return 0; long N = (int) Math.Floor(Math.Log10(num)); int pow10 = (int)Math.Pow(10, N); long firstDigit = num / pow10; long sum = (firstDigit - 1)*firstDigit/2*pow10 + firstDigit* 45 * N * pow10 / 10 + firstDigit*(num%pow10 + 1) + getSumOfSumOfDigitsBelow(num % pow10); return sum; }
This is the same algorithm as DineMartine, but expressed in a recursive way (I compared both implementations and yes, I'm sure it is;)). The execution time is reduced to almost zero, and the time complexity O(n) counts the number of digits or O(long(N)) , taking the value.