In [1]:
from graphics import GraphWin, Point, Circle, Rectangle, Text
from random import randrange
from math import pi, sqrt

In [2]:
height, width = 1044, 1044

In [3]:
def intro():
s = """
Calculating π using a Monte Carlo method

Source: https://en.wikipedia.org/wiki/Monte_Carlo_method#Introduction

For example, consider a circle inscribed in a unit square. Given that the
circle and the square have a ratio of areas that is π/4, the value of π can
be approximated using a Monte Carlo method:

Draw a square on the ground, then inscribe a circle within it.  Uniformly
scatter some objects of uniform size (grains of rice or sand) over the
square.  Count the number of objects inside the circle and the total number
of objects.  The ratio of the two counts is an estimate of the ratio of the
two areas, which is π/4.  Multiply the result by 4 to estimate π.

In this procedure the domain of inputs is the square that circumscribes our
circle. We generate random inputs by scattering grains over the square then
perform a computation on each input (test whether it falls within the
circle).  Finally, we aggregate the results to obtain our final result, the
approximation of π.

There are two important points to consider here: Firstly, if the grains are
not uniformly distributed, then our approximation will be poor. Secondly,
there should be a large  number of inputs. The approximation is generally
poor if only a few grains are randomly dropped into the whole square. On
average, the approximation improves as more grains are dropped.

"""

print(s)

In [4]:
def prepare_window():
global height, width

# Set our window dimensions and display it.
win = GraphWin("Monte Carlo Calculation of π", height, height)

# Prepare our circle for drawing.
center = Point(height/2, width/2)
unit_circle = Circle(center,
(height - 20)/2 if height < width else (width - 20)/2)
unit_circle.setFill("red1")
unit_circle.setOutline("red1")

p1, p2 = Point((height - 10), 10), Point(10, (width - 10))
unit_square = Rectangle(p1, p2)
unit_square.setFill("blue")
unit_square.setOutline("blue")

# The order of drawing matters, the square first, then the circle.
# Otherwise, the circle may be obscured.
unit_square.draw(win)
unit_circle.draw(win)

return unit_circle, unit_square, win

In [5]:
def get_input():
num_points = int(float(input("How many points to draw? ")))
return num_points

In [6]:
def in_circle(point, circ):

# get the distance between pt1 and circ using the
# distance formula
dx = point.getX() - circ.getCenter().getX()
dy = point.getY() - circ.getCenter().getY()
dist = sqrt(dx*dx + dy*dy)

# check whether the distance is less than the radius

In [7]:
def calc_pi(points, circle):
global height, width
total = len(points)
total_in_circle = 0

for p in points:
if in_circle(p, circle):
total_in_circle += 1

return (total_in_circle/total) * 4

In [8]:
def scatter(win, circle, n):
global height, width
points = []

# White backing for stats
white_box = Rectangle(Point(10,10), Point(190, 90))
white_box.setFill("white")
white_box.draw(win)

# Create Text object to show us current stats.
status = Text(Point(80, 50), "")
status.setSize(16)
status.draw(win)

for i in range(n):
# Random position in side square
x = randrange(10, width - 10 + 1)
y = randrange(10, height - 10 + 1)

# Create and save point in a list
point = Point(x, y)
points.append(point)

# Set the points properties, including drawing.
point.setFill("white")
point.setOutline("white")
point.draw(win)

# Update the stats text label with new info.
pi_sim = calc_pi(points, circle)
status.setText("Points drawn: {0}\n\
π     = {1:0.6f}\n\
π_sim = {2:0.6f}\n\
diff = {3:0.6f}%".format(len(points),
pi,
pi_sim,
(1 - (pi_sim/pi))*100))

In [9]:
def main():
# Print an introduction
intro()

# Get input from user on how many points to draw.
n = get_input()

# Prepare our window for simulation
u_circle, u_square, window = prepare_window()

# Scatter points all over the unit square.
scatter(window, u_circle, n)

# Get mouse click to exit (useful when run from a command line terminal)
window.getMouse()

In [10]:
main()

    Calculating π using a Monte Carlo method

Source: https://en.wikipedia.org/wiki/Monte_Carlo_method#Introduction

For example, consider a circle inscribed in a unit square. Given that the
circle and the square have a ratio of areas that is π/4, the value of π can
be approximated using a Monte Carlo method:

Draw a square on the ground, then inscribe a circle within it.  Uniformly
scatter some objects of uniform size (grains of rice or sand) over the
square.  Count the number of objects inside the circle and the total number
of objects.  The ratio of the two counts is an estimate of the ratio of the
two areas, which is π/4.  Multiply the result by 4 to estimate π.

In this procedure the domain of inputs is the square that circumscribes our
circle. We generate random inputs by scattering grains over the square then
perform a computation on each input (test whether it falls within the
circle).  Finally, we aggregate the results to obtain our final result, the
approximation of π.

There are two important points to consider here: Firstly, if the grains are
not uniformly distributed, then our approximation will be poor. Secondly,
there should be a large  number of inputs. The approximation is generally
poor if only a few grains are randomly dropped into the whole square. On
average, the approximation improves as more grains are dropped.

How many points to draw? 10000

---------------------------------------------------------------------------
GraphicsError                             Traceback (most recent call last)
<ipython-input-10-263240bbee7e> in <module>()
----> 1 main()

<ipython-input-9-7d40f7241740> in main()
10
11     # Scatter points all over the unit square.
---> 12     scatter(window, u_circle, n)
13
14     # Get mouse click to exit (useful when run from a command line terminal)

<ipython-input-8-a88486962e41> in scatter(win, circle, n)
25         point.setFill("white")
26         point.setOutline("white")
---> 27         point.draw(win)
28
29         # Update the stats text label with new info.

~/Library/Python/3.7/lib/python/site-packages/graphics.py in draw(self, graphwin)
480
481         if self.canvas and not self.canvas.isClosed(): raise GraphicsError(OBJ_ALREADY_DRAWN)
--> 482         if graphwin.isClosed(): raise GraphicsError("Can't draw to closed window")
483         self.canvas = graphwin
484         self.id = self._draw(graphwin, self.config)

GraphicsError: Can't draw to closed window