What is the most performative way to convert an int into the sum of its digits?

Asked

Viewed 126 times

4

I have a certain int and would like to turn it into another that is the result of the sum of your digits in the best possible way. For example:

int n = 2601;

Should result in 9 since this is the result of 2+6+0+1. How to best perform to achieve this behavior?

Based in this Stack Overflow response I’m going to list two ways, but I didn’t think of a way to test which has better performance and I don’t know if there’s any other possibility that stands out from those as well:


Form 1:

public int somarDigitos(int numero) {
  int resultado = 0;

  while (numero != 0) {
    int digito = numero % 10;
    numero = numero / 10;
    resultado = resultado + digito;
  }

  return resultado;
}

Form 2:

public int somarDigitos2(int numero) {
  String texto = String.valueOf(numero);
  int resultado = 0;

  for (char digito : texto.toCharArray()) {
    resultado = resultado + Character.getNumericValue(digito);
  }

  return resultado;
}
  • 2

    The first alternative avoids the creation of two objects (a String creates the first object, there the toCharArray creates p second), so I’m inclined to think that the first will be faster

  • Following the line of the above comment, I think avoid Strings is the best option...

  • 1

    Almost sure that working only with numbers is faster than with strings, but maybe still do some tests and put as answer

  • Smell of XY problem?

  • @Sorack Performance depends a lot on the problem you want to solve: is it more "performatic" in relation to the running time? in relation to memory usage?... I like the OS system of objective questions+answers, for real problems, but ok, to each his Own.

1 answer

3


The best way to know which is faster is by running tests running the expected algorithm many times and compare.

Of course the test needs care not to give biased results. I answered something facing C#, but almost everything is worth leave Java.

Number handling is a native processor operation and has high performance.

The algorithm with number will do 3 mathematical operations, maximum 3 data transports (it is likely that there is some optimization that keeps something in the register and gets much faster and will have a branch, which may be more time-consuming, but I think the processor bypass prediction should work well in this case.

I’ll dispose of the charge needed to convert the string for array of char imagining that the language does some optimization and does not need to do anything heavy, but if you really have to do a physical work here you should already have a performance commitment. The question does not want to know details :)

Has by the one branch of for, maybe more because what I saw from getNumericValue() it does checks on the provenance of the content. At least it has a mathematical operation and a transport within the function. If it cannot linearize the function it gets much worse because it will not be able to optimize for use of register, there will be more 2 transports and more overhead to manage the function call.

It has a mathematical operation and a visible transport in the code. But a lot of invisible things. And it seems that this part is much heavier than it looks.

I took a test and the difference is two orders of magnitude. But it can vary in certain circumstances. I expect a good difference, but not that big, which is why testing is important.

I tested the algorithm, because even knowing the difference in performance is what should be analyzed.

I tested in different environments and the results were consistent.

But in real use there are a lot and other things to solve. And it seems to me that Java is not good at optimizing this. If you call a function with the algorithm the function call will weigh a lot and puts the two algorithm closer to parity.

If you don’t have something I don’t know how to do in Java it makes a lot of difference to call a function or to execute directly. Then it is for the Javeiros with extensive experience to talk about it. I admit that I may have made a mistake in the evaluation, but nothing easy to understand.

class Main{
    public static void main(String []args) {
        int numero = 123456;
        long time = System.nanoTime();
        for (int i = 0; i < 10000000; i++) {
            int resultado = 0;
            while (numero != 0) {
                numero /= 10;
                resultado += numero % 10;
            }
        }
        System.out.println((System.nanoTime() - time) + "ns");
        time = System.nanoTime();
        for (int i = 0; i < 10000000; i++) {
            String texto = String.valueOf(numero);
            int resultado = 0;
            for (char digito : texto.toCharArray()) {
                resultado = resultado + Character.getNumericValue(digito);
            }
        }
        System.out.println((System.nanoTime() - time) + "ns");
    }
}

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

  • About your methodology, I redo the observation I made to @Isac. I also question what you did with a single integer size. Different sizes might offer an analysis to detect the change of branch.

  • I tested what he created, if I test with something else there is not what he is proposing.

  • 1

    My criticism is not in relation to what has been tested :-) but rather that in its measurement comes the maintenance time of the loop. 1,000,000 (or more than 6 zeroes? ) of increments and comparisons and deviations that are not part of what is actually being studied end up entering the measurement. Details of methodology, but still a flea behind my ear keeps poking me. Just as the invocation of Ambdas in my measurements also bothers me...

  • 1

    @Jeffersonquesado I agree, how would you eliminate this?

  • I thought of two alternatives: (a) measuring what is being measured, granularly, within the for and not outside it; this measurement I believe is unreliable because perhaps the granular return is unreliable. (b) measure a control by making the loop and noop, then discount the time of the control of the measured times; also do not know if it is reliable, if the jitter will try to do some optimization with my noop...

  • 1

    I’ve never done it in Java, but in all the tests I’ve done on other platforms it doesn’t work.

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.