Consider a square with side length 2, centered at the origin, with a circle of radius 1 inscribed within it.
Key points:
Square area: 4 square units
Circle area: π square units
The ratio of these areas is π/4.
The Monte Carlo Approach
Imagine randomly throwing darts at this square. The proportion of darts landing inside the circle should approximate the ratio of the circle’s area to the square’s area: (Darts inside circle) / (Total darts) ≈ π/4 Multiply both sides by 4, and we have our π estimate.
We use random number generators to create (x, y) coordinates within the square, then check if each point falls within the circle using x^2 + y^2 ≤ 1.
Show the code
def generate_data(NUM_SAMPLES: int, min_: int, max_: int): min_, max_ =-1, 1 circle =0 Xs = [] Ys = []for i inrange(NUM_SAMPLES): X = np.random.uniform(min_, max_) Y = np.random.uniform(min_, max_) Xs.append(X) Ys.append(Y)if X **2+ Y **2<1**2: circle +=1 df = pd.DataFrame({"x": Xs, "y": Ys}) df['in_circle'] = np.where(df['x']**2+ df['y']**2<1**2, 1, 0) rolling_est =4* np.cumsum(df['in_circle'][1:]) / np.arange(1, len(df)) rolling_est = np.insert(rolling_est, 0, 0) df['rolling_est'] = rolling_estreturn dfdf = generate_data(NUM_SAMPLES=2500, min_=-1, max_=1)
We add a column in_circle to indicate whether the point falls inside the circle or outside. Additionally, we create another column rolling_est which is a rolling estimate of π. This is what the dataset looks like.
Show the code
df.head()
x
y
in_circle
rolling_est
0
0.455318
0.499045
1
0.0
1
-0.326996
0.797670
1
4.0
2
-0.672441
-0.206805
1
4.0
3
0.630730
-0.050815
1
4.0
4
-0.890021
0.124752
1
4.0
Let’s run the simulation.
Show the code
import plotly.express as pximport pandas as pdimport numpy as np# Generate sample datan_frames =int(len(df) /10)N =10# Create a DataFrame with cumulative datadf_list = []for i inrange(1, n_frames +1): df_temp = pd.DataFrame({'x': df['x'][:i*N], 'y': df['y'][:i*N],'in_circle': df['in_circle'][:i*N],'rolling_est': df['rolling_est'][:i*N],'Iteration': i }) df_list.append(df_temp)df_with_iterations_for_viz = pd.concat(df_list, ignore_index=True)# Create the animated scatter plotfig = px.scatter( df_with_iterations_for_viz, x='x', y='y', animation_frame='Iteration', range_x=[-2, 2], range_y=[-1, 1] ) # Add a circlefig = fig.add_shape(type="circle", xref="x", yref="y", x0=-1, y0=-1, x1=1, y1=1, line_color="red",)# Add a squarefig = fig.add_shape(type="rect", xref="x", yref="y", x0=-1, y0=-1, x1=1, y1=1, line_color="Black")# Fix axesfig = fig.update_yaxes( scaleanchor ="x", scaleratio =1,)# Update layout for better appearancefig = fig.update_layout( title='Cumulative Scatter Plot Animation', xaxis_title='X Axis', yaxis_title='Y Axis', showlegend=False)fig.show()