8
8
from statistics import mean
9
9
10
10
11
- def pi_estimator ( iterations : int ) :
11
+ def is_in_unit_circle ( x : float , y : float ) -> bool :
12
12
"""
13
- An implementation of the Monte Carlo method used to find pi.
13
+ Checks whether the point (x, y) lies inside or on the boundary
14
+ of a unit circle centered at the origin.
15
+
16
+ >>> is_in_unit_circle(0.0, 0.0)
17
+ True
18
+ >>> is_in_unit_circle(1.0, 0.0)
19
+ True
20
+ >>> is_in_unit_circle(1.0, 1.0)
21
+ False
22
+ """
23
+ return sqrt (x ** 2 + y ** 2 ) <= 1
24
+
25
+
26
+ def pi_estimator (iterations : int ) -> float :
27
+ """
28
+ Estimate the value of pi using the Monte Carlo method.
29
+
30
+ The method simulates random points in the square and checks how
31
+ many fall within the inscribed unit circle.
32
+
33
+ Intuition:
14
34
1. Draw a 2x2 square centred at (0,0).
15
- 2. Inscribe a circle within the square.
16
- 3. For each iteration, place a dot anywhere in the square.
17
- a. Record the number of dots within the circle.
18
- 4. After all the dots are placed, divide the dots in the circle by the total.
19
- 5. Multiply this value by 4 to get your estimate of pi.
20
- 6. Print the estimated and numpy value of pi
35
+ 2. Inscribe a unit circle within the square.
36
+ 3. For each iteration, place a dot randomly within the square.
37
+ 4. Divide the number of dots within the circle by the total number of dots.
38
+ 5. Multiply this ratio by 4 to estimate pi.
39
+
40
+ Args:
41
+ iterations: Number of random points to generate.
42
+
43
+ Returns:
44
+ Estimated value of pi.
45
+
46
+ >>> round(pi_estimator(100000), 2) in [3.13, 3.14, 3.15]
47
+ True
21
48
"""
22
49
23
- # A local function to see if a dot lands in the circle.
24
- def is_in_circle (x : float , y : float ) -> bool :
25
- distance_from_centre = sqrt ((x ** 2 ) + (y ** 2 ))
26
- # Our circle has a radius of 1, so a distance
27
- # greater than 1 would land outside the circle.
28
- return distance_from_centre <= 1
50
+ if iterations <= 0 :
51
+ raise ValueError ("Number of iterations must be positive" )
29
52
30
- # The proportion of guesses that landed in the circle
31
- proportion = mean (
32
- int (is_in_circle (uniform (- 1.0 , 1.0 ), uniform (- 1.0 , 1.0 )))
53
+ inside_circle = sum (
54
+ is_in_unit_circle (uniform (- 1.0 , 1.0 ), uniform (- 1.0 , 1.0 ))
33
55
for _ in range (iterations )
34
56
)
35
- # The ratio of the area for circle to square is pi/4.
36
- pi_estimate = proportion * 4
37
- print (f"The estimated value of pi is { pi_estimate } " )
38
- print (f"The numpy value of pi is { pi } " )
39
- print (f"The total error is { abs (pi - pi_estimate )} " )
57
+ return (inside_circle / iterations ) * 4
40
58
41
59
42
60
def area_under_curve_estimator (
@@ -46,83 +64,113 @@ def area_under_curve_estimator(
46
64
max_value : float = 1.0 ,
47
65
) -> float :
48
66
"""
49
- An implementation of the Monte Carlo method to find area under
50
- a single variable non-negative real-valued continuous function,
51
- say f(x), where x lies within a continuous bounded interval,
52
- say [min_value, max_value], where min_value and max_value are
53
- finite numbers
54
- 1. Let x be a uniformly distributed random variable between min_value to
55
- max_value
56
- 2. Expected value of f(x) =
57
- (integrate f(x) from min_value to max_value)/(max_value - min_value)
58
- 3. Finding expected value of f(x):
59
- a. Repeatedly draw x from uniform distribution
60
- b. Evaluate f(x) at each of the drawn x values
61
- c. Expected value = average of the function evaluations
62
- 4. Estimated value of integral = Expected value * (max_value - min_value)
63
- 5. Returns estimated value
67
+ Estimate the definite integral of a continuous, non-negative function f(x)
68
+ over a bounded interval [min_value, max_value] using the Monte Carlo method.
69
+
70
+ Intuition:
71
+ 1. Randomly sample x values uniformly from [min_value, max_value].
72
+ 2. Compute the average of f(x) at the sample points.
73
+ This estimates the expected value of f(x).
74
+ 3. Multiply this average by the interval width to estimate the integral.
75
+
76
+ Args:
77
+ iterations: Number of random samples to draw.
78
+ function_to_integrate: A function f(x) to integrate over [min_value, max_value].
79
+ min_value: Lower bound of the interval.
80
+ max value: Upper bound of the interval.
81
+
82
+ Returns:
83
+ Estimated area under the curve (integral of f from min_value to max_value).
84
+
85
+ Raises:
86
+ ValueError: If iterations <= 0 or min_value >= max_value.
87
+
88
+ Example:
89
+ >>> def f(x): return x
90
+ >>> round(area_under_curve_estimator(100000, f), 2) in [0.49, 0.5, 0.51]
91
+ True
64
92
"""
93
+ if iterations <= 0 :
94
+ raise ValueError ("Number of iterations must be positive" )
95
+ if min_value >= max_value :
96
+ raise ValueError ("min_value must be less than max_value" )
65
97
66
- return mean (
98
+ samples = (
67
99
function_to_integrate (uniform (min_value , max_value )) for _ in range (iterations )
68
- ) * (max_value - min_value )
100
+ )
101
+ return mean (samples ) * (max_value - min_value )
69
102
70
103
71
104
def area_under_line_estimator_check (
72
105
iterations : int , min_value : float = 0.0 , max_value : float = 1.0
73
- ) -> None :
106
+ ) -> tuple [ float , float , float ] :
74
107
"""
75
- Checks estimation error for area_under_curve_estimator function
76
- for f(x) = x where x lies within min_value to max_value
77
- 1. Calls "area_under_curve_estimator" function
78
- 2. Compares with the expected value
79
- 3. Prints estimated, expected and error value
108
+ Demonstrate the Monte Carlo integration method by estimating
109
+ the area under the line y = x over [min_value, max_value].
110
+
111
+ The exact integral of f(x) = x over [a, b] is:
112
+ (b**2 - a**2) / 2
113
+
114
+ This helps illustrate that the Monte Carlo method produces
115
+ a reasonable estimate for a simple, well-understood function.
116
+
117
+ Args:
118
+ iterations: Number of samples to use in estimation.
119
+ min_value: Lower bound of the integration interval.
120
+ max_value: Upper bound of the integration interval.
121
+
122
+ Returns:
123
+ A tuple of (estimated_value, expected_value, absolute_error).
124
+
125
+ >>> result = area_under_line_estimator_check(100000)
126
+ >>> round(result[0], 2) in [0.49, 0.5, 0.51]
127
+ True
80
128
"""
81
129
82
130
def identity_function (x : float ) -> float :
83
- """
84
- Represents identity function
85
- >>> [function_to_integrate(x) for x in [-2.0, -1.0, 0.0, 1.0, 2.0]]
86
- [-2.0, -1.0, 0.0, 1.0, 2.0]
87
- """
88
131
return x
89
132
90
133
estimated_value = area_under_curve_estimator (
91
134
iterations , identity_function , min_value , max_value
92
135
)
93
- expected_value = (max_value * max_value - min_value * min_value ) / 2
136
+ expected_value = (max_value ** 2 - min_value ** 2 ) / 2
137
+ error = abs (estimated_value - expected_value )
138
+ return estimated_value , expected_value , error
94
139
95
- print ("******************" )
96
- print (f"Estimating area under y=x where x varies from { min_value } to { max_value } " )
97
- print (f"Estimated value is { estimated_value } " )
98
- print (f"Expected value is { expected_value } " )
99
- print (f"Total error is { abs (estimated_value - expected_value )} " )
100
- print ("******************" )
101
140
102
-
103
- def pi_estimator_using_area_under_curve (iterations : int ) -> None :
141
+ def pi_estimator_using_area_under_curve (iterations : int ) -> tuple [float , float , float ]:
104
142
"""
105
- Area under curve y = sqrt(4 - x^2) where x lies in 0 to 2 is equal to pi
143
+ Estimate the value of pi using Monte Carlo integration.
144
+
145
+ The area under the curve y = sqrt(4 - x**2) from x = 0 to 2 is equal to pi.
146
+ This function uses that fact to estimate pi.
147
+
148
+ Args:
149
+ iterations: Number of random samples to use for estimation.
150
+
151
+ Returns:
152
+ A tuple of (estimated_value, expected_value, absolute_error).
153
+
154
+ >>> result = pi_estimator_using_area_under_curve(100000)
155
+ >>> round(result[0], 2) in [3.13, 3.14, 3.15]
156
+ True
106
157
"""
107
158
108
- def function_to_integrate (x : float ) -> float :
159
+ def semicircle_function (x : float ) -> float :
109
160
"""
110
161
Represents semi-circle with radius 2
111
- >>> [function_to_integrate (x) for x in [-2.0, 0.0, 2.0]]
162
+ >>> [semicircle_function (x) for x in [-2.0, 0.0, 2.0]]
112
163
[0.0, 2.0, 0.0]
113
164
"""
114
165
return sqrt (4.0 - x * x )
115
166
116
167
estimated_value = area_under_curve_estimator (
117
- iterations , function_to_integrate , 0.0 , 2.0
168
+ iterations , semicircle_function , 0.0 , 2.0
118
169
)
170
+ expected_value = pi
171
+ error = abs (estimated_value - expected_value )
119
172
120
- print ("******************" )
121
- print ("Estimating pi using area_under_curve_estimator" )
122
- print (f"Estimated value is { estimated_value } " )
123
- print (f"Expected value is { pi } " )
124
- print (f"Total error is { abs (estimated_value - pi )} " )
125
- print ("******************" )
173
+ return estimated_value , expected_value , error
126
174
127
175
128
176
if __name__ == "__main__" :
0 commit comments