Approximating Pi
Sunday, September 25, 2011
A few days ago, it was
announced on
the Wolfram Blog that a 13-year-old had made a record calculating 458
million terms for the continued fraction of pi. In the spirit of that,
I thought I would show how to solve a question that sometimes gets asked
at interviews:
Given that Pi can be estimated using the function 4 * (1 - 1/3 + 1/5 - 1/7 + …) with more terms giving greater accuracy, write a function that calculates Pi to an accuracy of 5 decimal places.
Using Factor, we can calculate the nth
approximation of pi using vector
arithmetic:
: approximate-pi ( n -- approx )
    [1..b] 2 v*n 1 v-n 1 swap n/v
    [ odd? [ neg ] when ] map-index sum 4 * ;
This isn’t ideal if we want to try an increasing number of terms (looking for a particularly accuracy), since a lot of the work would be redone unnecessarily. Instead, we can write a word that adds successive terms until the difference between the previous approximation and the current approximation is less than our requested accuracy.
: next-term ( approx i -- approx' )
    [ 2 * 1 + ] [ odd? [ neg ] when ] bi 4.0 swap / + ; inline
:: find-pi-to ( accuracy -- n approx )
    1 4.0 [
        dup pick next-term [ - ] keep
        swap abs accuracy >= [ 1 + ] 2dip
    ] loop ;
To show its performance, we can time it:
IN: scratchpad [ 0.00001 find-pi-to ] time .
Running time: 0.026030341 seconds
3.141597653564762
An equivalent function in Python might look like this:
def find_pi_to(accuracy):
    i = 1
    approx = 4.0
    while 1:
        term = (2 * i) + 1
        if i % 2 == 1:
            term = -term
        new = approx + 4.0/term
        if abs(new - approx) < accuracy:
            approx = new
            break
        i += 1
        approx = new
    return approx
But, if we time this version (not counting startup or compile time), it takes 0.134 seconds. Doing the math shows that Factor is 5 times faster than Python in this case. Not bad.