PI4fun (Part 2)
Estimating $\pi$ for fun (Part 2: illustrating the process)¶
In previous post we could see how to estimate $\pi$ value with a simple Monte Carlo algorithm.
Now we're going to illustrate the process with some simple plots.
Let's remember the code we prevously had:
import random, math
Inside = 0
Total = 1000
for i in range(Total):
x, y = random.random(), random.random()
if (x**2+y**2) < 1: # Target!
Inside += 1
my_pi = 4.*Inside/Total
print "my_pi = %s" % my_pi
Now we want to store all the generated shots so we can have a history of the entire process:
Total=1000
shots = []
for i in range(Total):
shots.append([random.random(), random.random()])
And this can be improved and simplified by using numpy library for working with N-dimensional arrays:
Total=1000
import numpy
shots = numpy.random.random((Total,2))
Now we can easily create a new boolean array for identifying hits hits hitting inside the target and count the total:
inside_hits = shots[:,0]**2+shots[:,1]**2 < 1
print 4.0*sum(inside_hits)/Total
Once we have the full list of points and we know how to identify if they're inside or outside the target we can represent them in a plot with different colors:
%matplotlib inline
import seaborn
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(1, 1, 1)
colors = map(lambda x: 'r' if x else 'g', inside_hits)
ax.scatter(shots[:,0],shots[:,1],c=colors)
ax.set_xlim((0,1))
ax.set_ylim((0,1))
ax.axis('off')
plt.show()
Additionally, we can also save partial $\pi$ values and see how our estimation grows with the number of shots:
partial_pi= map(lambda x: 4.0*sum(inside_hits[:x])/(x+1), range(len(inside_hits)))
fig = plt.figure(figsize=(10,5))
ax2 = fig.add_subplot(1, 1, 1)
ax2.plot(partial_pi)
ax2.plot([math.pi]*Total)
ax2.set_xscale('log')
plt.show()
print partial_pi[-1]
Obviously, by increasing the number of shots the value becomes more accurate
%%time
Total=1000
shots = numpy.random.random((Total,2))
inside_hits = shots[:,0]**2+shots[:,1]**2 < 1
colors = map(lambda x: 'r' if x else 'g', inside_hits)
partial_pi= map(lambda x: 4.0*sum(inside_hits[:x])/(x+1), range(len(inside_hits)))
fig = plt.figure(figsize=(10,5))
ax1 = fig.add_subplot(1, 2, 1)
ax1.scatter(shots[:,0],shots[:,1],c=colors)
ax1.set_xlim((0,1))
ax1.set_ylim((0,1))
ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(partial_pi)
ax2.plot([math.pi]*Total)
ax2.set_xscale('log')
plt.show()
print partial_pi[-1]
In next posts we'll encapsulate all the calculations with an object-oriented approach to make it more intelligible and usable, and later on we'll try to optimize more the process to get better $\pi$ approximations.
Share on: