Half a half: Part 2

Ten days ago we had a look at Java’s ability to deal with numbers which had alot of decimal places. Liam pointed out that having to loop through a variety of operations is longer than performing a more efficient operation once.

With Liams magical formula, we get the following Java, with no loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.math.BigDecimal;
 
public class The_proper_half {
 
    public static Integer times_to_loop;
 
    /**
     * @param args the number of times you want to run the loop
     */
    public static void main(String[] args) {
        calculate(Integer.valueOf(args[0]));
 
    }
 
    public static void calculate(int times_to_loop) {
        //The Number we want to half
        BigDecimal number_to_half = new BigDecimal("10");
        //The result of doing the half
        BigDecimal the_half = new BigDecimal("0");
        //The number we want to divide by
        BigDecimal divider = new BigDecimal("2");
 
        the_half = (number_to_half.divide(divider.pow(times_to_loop)));
 
        System.out.println("The last number has " +
                ((Integer) number_to_half.subtract(the_half).toString().length()
                - 2) + " decimal places: ");
 
        System.out.println(number_to_half.subtract(the_half));
    }
}

As you can see, we only perform the opertation once (line 23), but admitadly, to get the correct number at the end, we still do the subtraction at the end!

Regarding optimisation, there are huge benefits. It turns out that the old way if hugely costly for thousands of iterations. These are the results for 10000 iterations, the old way:

anton@anton-laptop classes $ time java Half_of_a_half 10000 > /dev/null

real    5m22.714s
user    5m21.320s
sys    0m1.052s

The results for the new way are much better:

anton@anton-laptop classes $ time java The_proper_half 10000 > /dev/null

real    0m1.019s
user    0m1.248s
sys    0m0.052s

So it went from over five minutes, down to just a second. That was just by changing from a loop a single statement. Thats pretty cool stuff!

I have to admit here, the difficult bit is probably not the code. Its the abillity to see how a problem may be translated from one way of expressing it to another. Its that bit of genius thats really done the work here. Credit to Liam for the genius!

3 Comments

  1. Liam McBrien says:

    I’d be interested to see how the loop version runs without the System.out calls. I’ll bet it makes a significant difference!

  2. Anton Parol says:

    Both these sets of results are with the I/O calls removed. The first results are non loop, the second are with the loop:

    anton@anton-laptop classes $ time java The_proper_half 4000

    real 0m0.292s
    user 0m0.384s
    sys 0m0.028s
    anton@anton-laptop classes $ time java Half_of_a_half 4000

    real 0m21.830s
    user 0m22.169s
    sys 0m0.108s

  3. Liam McBrien says:

    OK, so here’s how those two runs stack up against each other:

    With the I/O, the tests take 1.019s and 322.714s, meaning that the loop version is roughly 317 times slower (322.714 / 1.019).

    Without the I/O, the tests take 0.292s and 21.83s, meaning that the loop version is roughly 75 times slower (21.83/0.292).

    So, by removing the console output from the loop program, we get over four times better performance relative to the non-loop version (317/75). This means that roughly 80% of the loop version’s execution time must be the I/O – just goes to show it’s always worth looking at where the bottlenecks in a system are!

Leave a Reply