6

What is the fastest way to find the sum of decimal digits? The following code is what I wrote but it is very very slow for range 1 to 1000000000000000000

long long sum_of_digits(long long input) {
    long long total = 0;
    while (input != 0) {
        total += input % 10;
        input /= 10;
    }
    return total;
}

int main ( int argc, char** argv) {
    for ( long long i = 1L; i <= 1000000000000000000L; i++) {
        sum_of_digits(i);
    }
    return 0;
}
Avinash
  • 12,851
  • 32
  • 116
  • 186
  • 1
    Break up the input, multithread it, then combine the results? – Nic Foster Jan 23 '12 at 05:44
  • 1
    doesn't seem like it would be that slow -- how fast do you need it to be? – Vaughn Cato Jan 23 '12 at 05:49
  • your code should take much less than a second – Vaughn Cato Jan 23 '12 at 05:52
  • @Vaughn Cato, Ok wait let me put complete code – Avinash Jan 23 '12 at 05:53
  • 2
    If this takes more than a second on your computer, I wonder how you compiled it in less than 100 years. – Benjamin Lindley Jan 23 '12 at 05:53
  • http://stackoverflow.com/questions/5276528/fastest-way-to-sum-digits-in-a-number – Pubby Jan 23 '12 at 05:54
  • This is going to be a really large number -- it won't even fit in a long long if you want a grand total. – Vaughn Cato Jan 23 '12 at 05:59
  • 1
    Currently your code isn't doing anything with the return value of sum_of_digits. Are you just going through the loop in main to see how fast it is? – Vaughn Cato Jan 23 '12 at 06:02
  • @Vaughn Cato: Yes check how much time it will take to complete – Avinash Jan 23 '12 at 06:04
  • It's not clear which of two things you are trying to do. Are you trying to find the sum of the digits of a single number as quickly as possible? Or are you trying to find the sum of the sums of digits of a range of numbers as quickly as possible? If the former, you're pretty much there. – David Schwartz Jan 23 '12 at 06:05
  • 8
    A computer can't do anything a billion billion times in a reasonable amount of time. Maybe you are misunderstanding the requirements. – Vaughn Cato Jan 23 '12 at 06:07
  • 5
    @Avinash: Now that you've added your main function, I'd like to completely revise my statement. Even if you could write some magic function that was able to execute in a single cycle, and you had a 6-core computer at 4ghz each, your code could not possibly take less than a year to execute. Do the math: 1000000000000000000 cycles / (4 gigahertz * 6 cores) – Benjamin Lindley Jan 23 '12 at 06:11
  • You're discarding the results of `sum_of_digits()`, so your entire program is equivalent to a no-op. Is there something that you need to do with those sums that you've computed? – NPE Jan 23 '12 at 08:12
  • If you are supposed to accumulate and print the results of `sum_of_digits()`, the grand total is 81000000000000000001. This won't fit in a `long long` or even an `unsigned long long`. – David Hammen Jan 23 '12 at 09:36

14 Answers14

6

I'm assuming what you are trying to do is along the lines of

#include <iostream>
const long long limit = 1000000000000000000LL;
int main () {
   long long grand_total = 0;
   for (long long ii = 1; ii <= limit; ++ii) {
      grand_total += sum_of_digits(i);
   }
   std::cout << "Grand total = " << grand_total << "\n";
   return 0;
}

This won't work for two reasons:

  • It will take a long long time.
  • It will overflow.

To deal with the overflow problem, you will either have to put a bound on your upper limit or use some bignum package. I'll leave solving that problem up to you.

To deal with the computational burden you need to get creative. If you know the upper limit is limited to powers of 10 this is fairly easy. If the upper limit can be some arbitrary number you will have to get a bit more creative.

First look at the problem of computing the sum of digits of all integers from 0 to 10n-1 (e.g., 0 to 9 (n=1), 0 to 99 (n=2), etc.) Denote the sum of digits of all integers from 10n-1 as Sn. For n=1 (0 to 9), this is just 0+1+2+3+4+5+6+7+8+9=45 (9*10/2). Thus S1=45.

For n=2 (0 to 99), you are summing 0-9 ten times and you are summing 0-9 ten times again. For n=3 (0 to 999), you are summing 0-99 ten times and you are summing 0-9 100 times. For n=4 (0 to 9999), you are summing 0-999 ten times and you are summing 0-9 1000 times. In general, Sn=10Sn-1+10n-1S1 as a recursive expression. This simplifies to Sn=(9n10n)/2.

If the upper limit is of the form 10n, the solution is the above Sn plus one more for the number 1000...000. If the upper limit is an arbitrary number you will need to get creative once again. Think along the lines that went into developing the formula for Sn.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
2

Quite late to the party, but anyways, here is my solution. Sorry it's in Python and not C++, but it should be relatively easy to translate. And because this is primarily an algorithm problem, I hope that's ok.

As for the overflow problem, the only thing that comes to mind is to use arrays of digits instead of actual numbers. Given this algorithm I hope it won't affect performance too much.

https://gist.github.com/frnhr/7608873

It uses these three recursions I found by looking and poking at the problem. Rather then trying to come up with some general and arcane equations, here are three examples. A general case should be easily visible from those.

relation 1

Reduces function calls with arbitrary argument to several recursive calls with more predictable arguments for use in relations 2 and 3.

foo(3456) == foo(3000)
           + foo(400) + 400 * (3)
           + foo(50) + 50 * (3 + 4)
           + foo(6) + 6 * (3 + 4 + 5)

relation 2

Reduce calls with an argument in the form L*10^M (e.g: 30, 7000, 900000) to recursive call usable for relation 3. These triangular numbers popped in quite uninvited (but welcome) :)

triangular_numbers = [0, 1, 3, 6, 10, 15, 21, 28, 36]  # 0 not used
foo(3000) == 3 * foo(1000) + triangular_numbers[3 - 1] * 1000

Only useful if L > 1. It holds true for L = 1 but is trivial. In that case, go directly to relation 3.

relation 3

Recursively reduce calls with argument in format 1*10^M to a call with argument that's divided by 10.

foo(1000) == foo(100) * 10 + 44 * 100 + 100 - 9  # 44 and 9 are constants

Ultimately you only have to really calculate the sum or digits for numbers 0 to 10, and it turns out than only up to 3 of these calculations are needed. Everything else is taken care of with this recursion. I'm pretty sure it runs in O(logN) time. That's FAAST!!!!!11one

On my laptop it calculates the sum of digit sums for a given number with over 1300 digits in under 7 seconds! Your test (1000000000000000000) gets calculated in 0.000112057 seconds!

frnhr
  • 12,354
  • 9
  • 63
  • 90
2

Reading your edit: computing that function in a loop for i between 1 and 1000000000000000000 takes a long time. This is a no brainer.

1000000000000000000 is one billion billion. Your processor will be able to do at best billions of operations per second. Even with a nonexistant 4-5 Ghz processor, and assuming best case it compiles down to an add, a mod, a div, and a compare jump, you could only do 1 billion iterations per second, meaning it will take on the order of 1 billion seconds.

James
  • 8,512
  • 1
  • 26
  • 28
2

You can break this down recursively. The sum of the digits of an 18-digit number are the sums of the first 9 digits plus the last 9 digits. Likewise the sum of the digits of a 9-bit number will be the sum of the first 4 or 5 digits plus the sum of the last 5 or 4 digits. Naturally you can special-case when the value is 0.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    It is hard to see how this helps with the problem (summing digits in all numbers up to n). This concerns the sum of digits of a single number, but for that asker's code is just fine. - With the problem it probably helps more not to break numbers into half, but perhaps treat sum of digits in 18-digit numbers as *magic with digits 1-9* combined through a *magic operation* with *sum of digits in 17-digit numbers*. – visitor Jan 23 '12 at 10:11
2

You probably don't want to do it in a bruteforce way. This seems to be more of a logical thinking question.

Note - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = N(N+1)/2 = 45.

---- Changing the answer to make it clearer after David's comment

See David's answer - I had it wrong

Chip
  • 3,226
  • 23
  • 31
  • -1: Good idea for not wanting to do it in a brute force manner, bad idea for the implementation. The correct formula for the sum of digits from 1 to 10^n is 1+(9*n*10^n)/2. Your formula gives 541 and 5041 as the sums from 1 to 100 and 1 to 1000. The correct values are 901 and 13501. – David Hammen Jan 23 '12 at 09:43
  • You may be right .. I didn't follow through and complete the calculation. Do you have a proof of your formula? It looks to me awfully like 1 + 45 ( 1 + 10 + .... + 10^n-1) = 1 + 45 ( 10^n - 1) / 9 = 1 + 5 (10^n -1 ) – Chip Jan 23 '12 at 10:22
  • @David, I've made my proof clearer.. let me know if you see a flaw in the logic. – Chip Jan 23 '12 at 10:28
  • Still not correct. See my answer. The correct formula is 1+(9*n*10^n)/2. – David Hammen Jan 23 '12 at 10:37
  • Can you please explain how you got the formula? – Chip Jan 23 '12 at 11:05
  • Did you read my answer? As of now it has zero votes. Let S_n be the sum of digits of all integers between 0 and 10^n-1 (e.g., S_3 is the sum of digits for integers 0 to 999). I found a recursive formula for S_n, S_n = 10*S_{n-1} + 10^{n-1}*S_1. As for the explicit expression, I just fed Mathematica the recursive relation and let it do the work. – David Hammen Jan 23 '12 at 13:27
  • I was wrong. Upvoted your answer and updated to point to your answer – Chip Jan 24 '12 at 07:28
  • As of the 3rd revision, it is well hidden that the first paragraph is what answer is left. – greybeard Oct 30 '16 at 14:15
1

I think you cannot do better than O(N) where N is the number of digits in the given number(which is not computationally expensive)

However if I understood your question correctly (the range) you want to output the sum of digits for a range of numbers. In that case, you can increment by one when you go from number0 to number9 and then decrease by 8.

Hari
  • 5,057
  • 9
  • 41
  • 51
  • That last paragraph doesn't make sense to me. Additionally, while he can't do better than O(N), he can certainly make it faster or slower. – Arafangion Jan 23 '12 at 08:28
1

You will need to cheat - look for mathematical patterns that let you short-cut your computations.

  • For example, do you really need to test that input != 0 every time? Does it matter if you add 0/10 several times? Since it won't matter, consider unrolling the loop.
  • Can you do the calculation in a larger base, eg, base 10^2, 10^3, etcetera, that might allow you to reduce the number of digits, which you'll then have to convert back to base 10? If this works, you'll be able to implement a cache more easily.
  • Consider looking at compiler intrinsics that let you give hints to the compiler for branch prediction.
  • Given that this is C++, consider implementing this using template metaprogramming.
  • Given that sum_of_digits is purely functional, consider caching the results.

Now, most of those suggestions will backfire - but the point I'm making is that if you have hit the limits of what your computer can do for a given algorithm, you do need to find a different solution.

This is probably an excellent starting point if you want to investigate this in detail: http://mathworld.wolfram.com/DigitSum.html

Arafangion
  • 11,517
  • 1
  • 40
  • 72
  • Before bothering with all the microoptimizations, perhaps start by measuring how long it takes to sum up digits up to, say, 100,000,000, and then multiplying that by 1,000,000,000 to get a rough estimate of how long this algorithm would take? - If something takes a million years, microoptimizations can reduce that to 100 000 years at best. – visitor Jan 23 '12 at 10:15
  • @visitor: He wouldn't be doing that already? Isn't his main() in the question an example to demonstrate the kind of performance he wants? – Arafangion Jan 23 '12 at 15:15
  • I don't understand. Are you suggesting that any amount of microoptimizations are going to let OP perform 1000000000000000000L operations in some meaningful time within human life span? - This problem can't be solved with a `sum_of_digits(n)` function, no matter how much you optimize it. Instead you'd need something like a `sum_of_digits_in_n_digit_numbers(d)` function. – UncleBens Jan 23 '12 at 18:21
  • @UncleBens: Unless he did some form of lazy evaluation such that those optimisations are conceptually done, but not actually evaluated until it's neccessary to do so. He needs to *cheat*. – Arafangion Jan 23 '12 at 22:35
1

Possibility 1:

You could make it faster by feeding the result of one iteration of the loop into the next iteration.

For example, if i == 365, the result is 14. In the next loop, i == 366 -- 1 more than the previous result. The sum is also 1 more: 3 + 6 + 6 = 15.

Problems arise when there is a carry digit. If i == 99 (ie. result = 18), the next loop's result isn't 19, it's 1. You'll need extra code to detect this case.

Possibility 2:

While thinking though the above, it occurred to me that the sequence of results from sum_of_digits when graphed would resemble a sawtooth. With some analysis of the resulting graph (which I leave as an exercise for the reader), it may be possible to identify a method to allow direct calculation of the sum result.

However, as some others have pointed out: Even with the fastest possible implementation of sum_of_digits and the most optimised loop code, you can't possibly calculate 1000000000000000000 results in any useful timeframe, and certainly not in less than one second.

MatthewD
  • 2,509
  • 2
  • 23
  • 27
0

No the best, but simple:

int DigitSumRange(int a, int b) {
    int s = 0;
    for (; a <= b; a++)
        for(c : to_string(a)) 
            s += c-48;

    return s;
}
Juanmabs22
  • 1,194
  • 9
  • 10
0

A Python function is given below, which converts the number to a string and then to a list of digits and then finds the sum of these digits.

def SumDigits(n):
    ns=list(str(n))
    z=[int(d) for d in ns]
    return(sum(z))
Kishor Bhoyar
  • 111
  • 1
  • 1
  • 6
0

In C++ one of the fastest way can be using strings. first of all get the input from users in a string. Then add each element of string after converting it into int. It can be done using -> (str[i] - '0').

    #include<iostream>
    #include<string>
    using namespace std;
    int main()
    {   string str;
        cin>>str;
        long long int sum=0;
        for(long long int i=0;i<str.length();i++){
          sum =  sum + (str[i]-'0');
      }
        cout<<sum;
    }
    
          
  • Please could you edit your reply 1) not to use bits/stdc++.h as it is a bad example and 2) so that it is nicely indented? – Paul Floyd Jul 22 '21 at 09:29
0

Edit: It seems you want the the sum of the actual digits such that: 12345 = 1+2+3+4+5 not the count of digits, nor the sum of all numbers 1 to 12345 (inclusive);

As such the fastest you can get is:

long long sum_of_digits(long long input) {
    long long total = input % 10;
    while ((input /= 10) != 0)
        total += input % 10;
    return total;
}

Which is still going to be slow when you're running enough iterations. Your requirement of 1,000,000,000,000,000,000L iterations is One Million, Million, Million. Given 100 Million takes around 10,000ms on my computer, one can expect that it will take 100ms per 1 million records, and you want to do that another million million times. There are only 86400 seconds in a day, so at best we can compute around 86,400 Million records per day. It would take one computer

Lets suppose your method could be performed in a single float operation (somehow), suppose you are using the K computer which is currently the fastest (Rmax) supercomputer at over 10 petaflops, if you do the math that is = 10,000 Million Million floating operations per second. This means that your 1 Million, Million, Million loop will take the world's fastest non-distributed supercomputer 100 seconds to compute the sums (IF it took 1 float operation to calculate, which it can't), so you will need to wait around for quite some time for computers to become 100 so much more powerful for your solution to be runable in under one second.

What ever you're trying to do, you're either trying to do an unsolvable problem in near real-time (eg: graphics calculation related) or you misunderstand the question / task that was given you, or you are expected to perform something faster than any (non-distributed) computer system can do.

If your task is actually to sum all the digits of a range as you show and then output them, the answer is not to improve the for loop. for example:

1 = 0
10 = 46
100 = 901
1000 = 13501
10000 = 180001
100000 = 2250001
1000000 = 27000001
10000000 = 315000001
100000000 = 3600000001

From this you could work out a formula to actually compute the total sum of all digits for all numbers from 1 to N. But it's not clear what you really want, beyond a much faster computer.

Seph
  • 8,472
  • 10
  • 63
  • 94
  • 1
    That would be a count of digits, not a sum. – James Jan 23 '12 at 06:29
  • the OP needs to be more specific, almost every answer here comes the same solution.. "N*(N+1)/2" – Seph Jan 23 '12 at 07:32
  • answer updated to match what the question is 'actually' asking. – Seph Jan 23 '12 at 08:11
  • +1: The edited answer appears to be addressing what the question is 'actually' asking, which is to calculate the sum of the digits of all integers between 1 and N (inclusive). – David Hammen Jan 23 '12 at 10:02
  • @Seph to say "the fastest you can get is" means that you have a mathematical proof that your algorithm is the best possible or at least that any other will be only that fast and not faster. That's a heavy claim. And it turns you, a faulty one, too :) Oh and +1 for actually addressing the OP question – frnhr Nov 26 '13 at 13:50
  • @frnhr strictly speaking the question asked is "Fastest way to find the sum of decimal digits" not the "Fastest way to find the sum of **a range of** decimal digits". My solution presented is `O(N)` for calculating the sum of the digits of an a single number where `N` is the number of digits in the number; to sum up each unknown digit you must inspecting each one therefore it is "the fastest you can get". Optimizations exist for range of numbers, but I took the question text wording to imply that the `for` loop in the example code is indicative and not the actual problem being solved. – Seph Nov 27 '13 at 08:24
-2

The formula for finding the sum of the digits of numbers between 1 to N is:

(1 + N)*(N/2)

[http://mathforum.org/library/drmath/view/57919.html][1]

There is a class written in C# which supports a number with more than the supported max-limit of long. You can find it here. [Oyster.Math][2]

Using this class, I have generated a block of code in c#, may be its of some help to you.

using Oyster.Math;
class Program
{
    private static DateTime startDate;
    static void Main(string[] args)
    {
        startDate = DateTime.Now;
        Console.WriteLine("Finding Sum of digits from {0} to {1}", 1L, 1000000000000000000L);
        sum_of_digits(1000000000000000000L);
        Console.WriteLine("Time Taken for the process: {0},", DateTime.Now - startDate);
        Console.ReadLine();
    }

    private static void sum_of_digits(long input)
    {
       var answer = IntX.Multiply(IntX.Parse(Convert.ToString(1 + input)), IntX.Parse(Convert.ToString(input / 2)), MultiplyMode.Classic);
        Console.WriteLine("Sum: {0}", answer);
    }
}

Please ignore this comment if it is not relevant for your context. [1]: https://web.archive.org/web/20171225182632/http://mathforum.org/library/drmath/view/57919.html [2]: https://web.archive.org/web/20171223050751/http://intx.codeplex.com/

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Sunil Raj
  • 460
  • 5
  • 20
-3

If you want to find the sum for the range say 1 to N then simply do the following

long sum = N(N+1)/2;

it is the fastest way.

paper.plane
  • 1,201
  • 10
  • 17