Wednesday, March 11, 2015

Groovy closure to find Pi and sqrt(2)

I want to start with very simple math fact - if we draw right triangle and two of edges of this triangle have length equal to sqrt(2). Area of this triangle is of course 1 (because: 1/2*a*h).

Another fact is that area under function sin(x) from 0 to pi/2 is also equal to 1.

Let's use those facts to exploit groovy closures to estimate pi and sqrt(2).

Clone repo: git clone https://github.com/piotrpietrzak/gatchaman.git
git checkout origin/groovypi

We should start with tests - this time we will use spock with "where:" block.

def "should converge to square root of two"(scale, sumToLimit, expectedResult) {
    expect:
    new DefiniteIntegral()
            .compute(scale, sumToLimit , { it }) == expectedResult

    where:
    scale | sumToLimit | expectedResult
    1     | 1          | 1.4
    2     | 1          | 1.41
    3     | 1          | 1.414
    4     | 1          | 1.4142
    5     | 1          | 1.41421
    6     | 1          | 1.414214
}

For pi this is also very simple test:

def "should converge to pi"(scale, sumToLimit, expectedResult) {
    expect:
    new DefiniteIntegral()
            .compute(scale, sumToLimit, { Math.sin(it) }) * 2 == expectedResult

    where:
    scale | sumToLimit | expectedResult
    1     | 1          | 3.2    
    2     | 1          | 3.14
    3     | 1          | 3.142
    4     | 1          | 3.1416
    5     | 1          | 3.14160
    6     | 1          | 3.141592
}

Definite integral is quite simple (from the numerical recipes point of view we can achieve much better results even with this naive approach but I want to keep this simple).

while (sum < integrateTo) {
    x += step
    sum += function(x) * step
}

After this loop we should return x which is our result.

What else can we do better?

Every math lover will tell us other things but IT guys will focus on poor API and unnecessary method. Lets replace this simple solution with this:
class IntegrationClosure {
    final Closure<BigInteger> compute = {
        Integer scale, BigDecimal integrateTo, Closure<BigDecimal> function ->
            BigDecimal sum = 0;
            BigDecimal x = 0;
            BigDecimal step = BigDecimal.valueOf(1, scale)

            while (sum < integrateTo) {
                x += step
                sum += function(x) * step
            }
            x
    }
}


No comments:

Post a Comment