Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to increase the performance for estimating `Pi`in Python

I have written the following code in Python, in order to estimate the value of Pi. It is called Monte Carlo method. Obviously by increasing the number of samples the code becomes slower and I assume that the slowest part of the code is in the sampling part. How can I make it faster?

from __future__ import division
import numpy as np

a = 1
n = 1000000

s1 = np.random.uniform(0,a,n)
s2 = np.random.uniform(0,a,n)

ii=0
jj=0

for item in range(n):
    if ((s1[item])**2 + (s2[item])**2) < 1:
        ii = ii + 1

print float(ii*4/(n))

Do you suggest other (presumably faster) codes?

like image 320
NKN Avatar asked Jun 12 '15 15:06

NKN


People also ask

How can Monte Carlo method be used to estimate pi?

To compute Monte Carlo estimates of pi, you can use the function f(x) = sqrt(1 – x2). The graph of the function on the interval [0,1] is shown in the plot. The graph of the function forms a quarter circle of unit radius. The exact area under the curve is π / 4.

How can you estimate the value of π numerically via the area evaluation concept of an ellipse with the help of Monte Carlo simulation?

One method to estimate the value of (3.141592...) is by using a Monte Carlo method. In the demo above, we have a circle of radius 0.5, enclosed by a 1 × 1 square. The area of the circle is π r 2 = π / 4 , the area of the square is 1. If we divide the area of the circle, by the area of the square we get .


2 Answers

The bottleneck here is actually your for loop. Python for loops are relatively slow, so if you need to iterate over a million items, you can gain a lot of speed by avoiding them altogether. In this case, it's quite easy. Instead of this:

for item in range(n):
    if ((s1[item])**2 + (s2[item])**2) < 1:
        ii = ii + 1

do this:

ii = ((s1 ** 2 + s2 ** 2) < 1).sum()

This works because numpy has built-in support for optimized array operations. The looping occurs in c instead of python, so it's much faster. I did a quick test so you can see the difference:

>>> def estimate_pi_loop(x, y):
...     total = 0
...     for i in xrange(len(x)):
...         if x[i] ** 2 + y[i] ** 2 < 1:
...             total += 1
...     return total * 4.0 / len(x)
... 
>>> def estimate_pi_numpy(x, y):
...     return ((x ** 2 + y ** 2) < 1).sum()
... 
>>> %timeit estimate_pi_loop(x, y)
1 loops, best of 3: 3.33 s per loop
>>> %timeit estimate_pi_numpy(x, y)
100 loops, best of 3: 10.4 ms per loop

Here are a few examples of the kinds of operations that are possible, just so you have a sense of how this works.

Squaring an array:

>>> a = numpy.arange(5)
>>> a ** 2
array([ 0,  1,  4,  9, 16])

Adding arrays:

>>> a + a
array([0, 2, 4, 6, 8])

Comparing arrays:

>>> a > 2
array([False, False, False,  True,  True], dtype=bool)

Summing boolean values:

>>> (a > 2).sum()
2

As you probably realize, there are faster ways to estimate Pi, but I will admit that I've always admired the simplicity and effectiveness of this method.

like image 175
senderle Avatar answered Oct 04 '22 22:10

senderle


You have assigned numpy arrays, so you should use those to your advantage.

for item in range(n):
    if ((s1[item])**2 + (s2[item])**2) < 1:
        ii = ii + 1

becomes

s1sqr = s1*s1
s2sqr = s2*s2
s_sum = s1sqr + s2sqr
s_sum_bool = s_sum < 1
ii = s_sum_bool.sum()
print float(ii*4/(n))

Where you are squaring the arrays, summing them, checking if the sum is less than 1 and then summing the boolean values (false = 0, true = 1) to get the total number that meet the criteria.

like image 41
James Avatar answered Oct 04 '22 20:10

James