1

I'm currently working my way through a book on simple optimizations. One of the algorithms shown was:

Print all solutions to a3 + b3 = c3 + d3 (where a, b, c, d are less than 1000)

(I went with 100 so it would run quicker on my slow netbook)

I programmed up their solution (Their solution and their first offered optimisation are included), however the break optimisation offered actually made the algorithm a fair bit slower.

public static void doUnoptimised() {
    for(int a = 1; a < 100; a++) 
    {
        for(int b = 1; b < 100; b++)
        {
            for(int c = 1; c < 100; c++)
            {
                for(int d = 1; d < 100; d++) {
                    if(Math.pow(a, 3) + Math.pow(b, 3) == Math.pow(c, 3) + Math.pow(d, 3))
                    {
                        System.out.println("A:" + a + " B:" + b + " C:" + c + " D:" + d);
                    }
                }
            }
        }
    }
}

public static void doFirstOptimised() {
    for(int a = 1; a < 100; a++) 
    {
        for(int b = 1; b < 100; b++)
        {
            for(int c = 1; c < 100; c++)
            {
                for(int d = 1; d < 100; d++) {
                    if(Math.pow(a, 3) + Math.pow(b, 3) == Math.pow(c, 3) + Math.pow(d, 3))
                    {
                        System.out.println("A:" + a + " B:" + b + " C:" + c + " D:" + d);
                        break;
                    }
                }
            }
        }
    }
}

Why is that? Note that this isn't my solution, this is the one shown in the book, and they go on with more optimizations later but I'm interested in why this optimization was such an epic fail.

Edit - Perhaps I'm not being clear enough here. I know this is a barely noticeable change and I understand why this is an improvement and have already gone through the better optimizations offered in the book. (Thanks for those giving better optimisations though, appreciate the effort) However this particular step one does not work at all and im trying to figure out why. But at this stage it seems like my jvm is just weird.

  • 1
    Please post relevant code directly, not as link. – Fildor Aug 03 '16 at 10:08
  • 7
    Note that microbenchmarking in Java is hard because of all the complicated heuristics that the JIT does, so it's easy to be misled by the results. See [How do I write a correct micro-benchmark in Java?](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java/) – Jesper Aug 03 '16 at 10:10
  • When I tried this out, the optimization actually made it a fair bit faster, not slower :D – Mark Aug 03 '16 at 10:13
  • You can remove one of the loops by calculating `d` rather than trying to discover it by brute force. This will make it 100x faster. I would also use `x*x*x` instead of `Math.pow` and I would only calculate it once per outer iteration not inside the loop. – Peter Lawrey Aug 03 '16 at 10:20
  • @Peter and I'm sure the book will get to that, but it's kind of besides the point of the question ;) – Mark Aug 03 '16 at 10:20
  • @Plonker: Have you tried running the solution from the book instead of your own? Does the optimized algorithm still run slower for you if you do? – Mark Aug 03 '16 at 10:22
  • @Mark True, but it's such a tiny optimisation by comparison which rarely occurs. It's not clear why the OP can't work this out themselves. – Peter Lawrey Aug 03 '16 at 10:22
  • @Plonker: I just tried this and I repeated it 100 times in a loop and the optimisation works quicker to be honest. – Vishal Jumani Aug 03 '16 at 10:29
  • @Jesper Yes, I get that heuristics are imperfect when done in a hack way like me, however I'm talking about a 30 second difference (10 seconds to complete vs 38 seconds) which I am still getting now. It's not even close and you can easily tell by just eyeballing it. Apparently my java is being weird for some reason. – PlonkerThePenguin Aug 03 '16 at 12:05
  • @Mark Yes, exactly. The book itself even says this is a barely noticeable change, but on my laptop its nuking my results completely. – PlonkerThePenguin Aug 03 '16 at 12:13
  • Okay that seems really odd. I just tried it again (copied the code and ran it again). 29 seconds without optimization, 7.7 seconds with optimization. So... yeah... it's noticeable after all, but kind of the opposite of your test results :D – Mark Aug 05 '16 at 10:52

1 Answers1

0

There is an assumption in this optimisation that there is only one possible d for any combination of a, b and c.

What the break; does is stop the inner loop and so once we have a solution, we can give up looking for another one and this saves work, thus reducing the time.

There is many, much better optimisation, however using a break; like this, or a return is a common optimisation used in less contrived situations.

For example, how might you implement ArrayList.indexOf naively ignoring null values.

public int indexOf(Object o) {
    int index = -1;
    for (int i = 0; i < size; i++)
        if (o.equals(elementData[i]))
            if (index == -1)
                index = i;
    return index;
}

Note: this has to scan every entry even if it is the first entry to be found. A faster way is to use break to say; stop iterating if I have a solution.

public int indexOf(Object o) {
    int index = -1;
    for (int i = 0; i < size; i++)
        if (o.equals(elementData[i]))
            index = i;
            break;
        }
    return index;
}

However, break is not needed here because the next thing it does is return

public int indexOf(Object o) {
    for (int i = 0; i < size; i++)
        if (o.equals(elementData[i]))
            return i;
    return -1;
}

taking into account null values, the actual implementation is

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130