I am new member here and I'm gonna drive straight into this as I've spent my whole Sunday trying to get my head around it.
I'm new to Python, having previously learned coding on C++ to a basic-intermediate level (it was a 10-week university module).
I'm trying a couple of iterative techniques to calculate Pi but both are coming up slightly inaccurate and I'm not sure why.
The first method I was taught at university - I'm sure some of you have seen it done before.
x=0.0
y=0.0
incircle = 0.0
outcircle = 0.0
pi = 0.0
i = 0
while (i<100000):
x = random.uniform(-1,1)
y = random.uniform(-1,1)
if (x*x+y*y<=1):
incircle=incircle+1
else:
outcircle=outcircle+1
i=i+1
pi = (incircle/outcircle)
print pi
It's essentially a generator for random (x,y) co-ordinates on a plane from -1 to +1 on both axes. Then if x^2+y^2 <= 1, we know the point rests inside a circle of radius 1 within the box formed by the co-ordinate axes.
Depending on the position of the point, a counter increases for incircle or outcircle.
The value for pi is then the ratio of values inside and outside the circle. The co-ordinates are randomly generated so it should be an even spread.
However, even at very high iteration values, my result for Pi is always around the 3.65 mark.
The second method is another iteration which calculates the circumference of a polygon with increasing number of sides until the polygon is almost a circle, then, Pi=Circumference/diameter. (I sort of cheated because the coding has a math.cos(Pi) term so it looks like I'm using Pi to find Pi, but this is only because you can't easily use degrees to represent angles on Python). But even for high iterations the final result seems to end around 3.20, which again is wrong. The code is here:
S = 0.0
C = 0.0
L = 1.0
n = 2.0
k = 3.0
while (n<2000):
S = 2.0**k
L = L/(2.0*math.cos((math.pi)/(4.0*n)))
C = S*L
n=n+2.0
k=k+1.0
pi = C/math.sqrt(2.0)
print pi
I remember, when doing my C++ course, being told that the problem is a common one and it isn't due to the maths but because of something within the coding, however I can't remember exactly. It may be to do with the random number generation, or the limitations of using floating point numbers, or... anything really. It could even just be my mathematics...
Can anyone think what the issue is?
TL;DR: Trying to calculate Pi, I can get close to it but never very accurately, no matter how many iterations I do.
(Oh and another point - in the second code there's a line saying S=2.0**k. If I set 'n' to anything higher than 2000, the value for S becomes too big to handle and the code crashes. How can I fix this?)
Thanks!
The algorithm for your first version should look more like this:
from __future__ import division, print_function
import sys
if sys.version_info.major < 3:
range = xrange
import random
incircle = 0
n = 100000
for n in range(n):
x = random.random()
y = random.random()
if (x*x + y*y <= 1):
incircle += 1
pi = (incircle / n) * 4
print(pi)
Prints:
3.14699146991
This is closer. Increase n to get even closer to pi.
The algorithm takes into account only one quarter of the unit circle, i.e. with a radius of 1.
The formula for the area of a quarter circle is:
area_c = (pi * r **2) / 4
That for the area of the square containing this circle:
area_s = r **2
where r is the radius of the circle.
Now the ratio is:
area_c / area_s
substitute the equations above, re-arange and you get:
pi = 4 * (area_c / area_s)
Going Monte Carlo, just replace both areas by a very high number that represents them. Typically, the analogy of darts thrown randomly is used here.
For the first one, your calculation should be
pi = incircle/1000000*4 # 3.145376..
This is the number of points that landed inside of the circle over the number of total points (approximately 0.785671 on my run).
With a radius of 1 (random.uniform(-1,1)), the total area is 4, so if you multiple 4 by the ratio of points that landed inside of the circle, you get the correct answer.
Related
I have a question on the algorithm below. What confused me is why x = random.random()*2 -1 and y = random.random()*2 -1 rather than just simply x = random.random() and y = random.random()? The complete code is as following:
import random
NUMBER_OF_TRIALS= 1000000
numberOfHits = 0
for i in range(NUMBER_OF_TRIALS):
x = random.random()*2 -1
y = random.random()*2 -1
if x * x + y * y <=1:
numberOfHits +=1
pi = 4* numberOfHits / NUMBER_OF_TRIALS
print("PI is", pi)
The circle in this simulation is centered at (0, 0) with a radius of 1, so
x = random.random() * 2 - 1
y = random.random() * 2 - 1
will make the range for each -1 to 1.
The interesting thing about this question is that the implementation works just as well, and gives you the same expected answer whether you use random.random() or random.random()*2-1... so the reason why the author chose to use random.random()*2-1 has nothing to do with what the program does.
The author of this code understands the algorithm as follows:
Imagine a circle inscribe in a square. Use the unit circle because it's simplest
Choose random points within the square, and see how many are also inside the circle
The circle has area pi and the square has area 4, so the proportion of points that fall in the circle will approach pi/4. Calculate the measured ratio and solve for pi.
Now, the square in which the unit circle is inscribed goes from (-1,-1) to (1,1). Since random() only gives you a number in [0,1), it needs to be multiplied by two and shifted to select a random number in [-1,1), which chooses random points within the square.
If the author had used random(), then he would be selecting point within the first quadrant only. All the quadrants look exactly the same, so the ratio of hits to misses would be the same and the program would still work just fine, but then the program would not be implementing the above-described procedure, and would be more difficult to understand.
One of the most important properties of good code is that it clearly communicates the author's intent.
random() gives you a random float between 0 and 1.
random()*2 -1 gives you a random float between -1 and +1.
The algorithm, as usually explained, is in terms of the proportion of points in the unit square that are in the unit circle being pi/4, which is obvious after a moment's thought, and the second one gives you that directly.
It doesn't take much additional thought to see that using only the upper-right quadrant of the unit square and the unit circle will still give you pi/4 (although it is possible to confuse yourself and get it wrong, as I embarrassingly did in the first version of this answer). But it's not as blindingly obvious. And that might be a good enough reason for a tutorial to not do things that way.
If you were interested in calculating pi as efficiently as possible, it would probably make more sense to just use random(), and add a comment about how you're diving both the unit square and the unit circle by the same value so the odds are still pi/4. But if you're interested in showing novice programmers how to design and implement randomized algorithms? Probably better to write it the way it's written.
For a project, I am trying to determine the time it would take for a photon to leave the Sun. However, I am having trouble with my code (found below).
More specifically, I set up a for loop with an if statement, and if some randomly generated probability is less than the probability of collision, that means the photon collides and it changes direction.
What I am having trouble with is setting up a condition where the for loop stops if the photon escapes (when distance > Sun radius). The one I have set up already doesn't appear to work.
I use a very scaled down measurement of the Sun's radius because if I didn't it would take a long time for the photon to escape in my simulation.
from numpy.random import random as rng # we want them random numbers
import numpy as np # for the math functions
import matplotlib.pyplot as plt # to make pretty pretty class
mass_proton = 1.67e-27
mass_electron = 9.11e-31
Thompson_cross = 6.65e-29
Sun_density = 150000
Sun_radius = .005
Mean_Free = (mass_proton + mass_electron)/(Thompson_cross*Sun_density*np.sqrt(2))
time_step= 10**-13 # Used this specifically in order for the path length to be < Mean free Path
path_length = (3e8)*time_step
Probability = 1-np.exp(-path_length/Mean_Free) # Probability of the photon colliding
def Random_walk():
x = 0 # Start at origin (0,0)
y = 0
N = 1000
m=0 # This is a counter I have set up for the number of collisions
for i in range(1,N+1):
prand = rng(N+1) # Randomly generated probability
if prand[i] < Probability: # If my prand is less than the probability
# of collision, the photon collides and changes
# direction
x += Mean_Free*np.cos(2*np.pi*prand)
y += Mean_Free*np.sin(2*np.pi*prand)
m += 1 # Everytime a collision occurs 1 is added to my collision counter
distance = np.sqrt(x**2 + y**2) # Final distance the photon covers
if np.all(distance) > Sun_radius: # if the distance the photon travels
break # is greater than the Radius of the Sun,
# the for loop stops, meaning the
#photon escaped
print(m)
return x,y,distance
x,y,d = Random_walk()
plt.plot(x,y, '--')
plt.plot(x[-1], y[-1], 'ro')
Any criticisms of my code are welcome, this is for a grade and I do want to learn how to do this correctly, so please tell me if you notice any other errors.
I don't understand the motivation for the formulas you've implemented. I'll explain my own motivation here, but if your instructor told you to do something else, I guess you should listen to them instead.
If I were going to do this, I would generate a sequence of movements of a photon, stopping when distance of the photon to the center of the sun is greater than the solar radius. Each movement is a sample from a distribution which has two components: one for the distance, and one for the direction. I will assume that these are independent (this may be questioned in a more careful simulation).
It seems plausible that the distribution of distance is an exponential distribution with parameter 1/(mean free path). Then the density is p(d) = (1/MFP) exp(-d/MFP). Its cdf is 1 - exp(-d/MFP) and the inverse of the cdf is -MFP log(1 - p) where p = cdf(d). Now you can sample from the distribution of distances: let p = rand(0, 1) where rand = uniform random and plug it into the inverse cdf to get d. This is called the inverse cdf method of sampling; a web search will find more info about it.
As for the direction, you can let angle = rand(0, 2*pi) and then (x, y) = (cos(angle), sin(angle)).
Now you can construct the series of positions. From an initial location, let the new location = previous + d*(x, y). Stop when distance of location to center is greater than radius.
Looks like a great problem! Good luck and have fun. Let me know if you have any questions.
Here is a way of thinking about the problem that you may find helpful. At each moment, the photon has a position (x, y) and a direction (dx, dy). The (dx, dy) variables are coefficients of the unit vector, so sqrt(dx**2 + dy**2) = 1.0. The distance traveled by the photon during one step is path_length * direction.
At each step you do 4 things:
calculate the photon's new position
figure out if the photon has left the sun by computing its distance from the center point
determine, with a single random number, whether or not the photon collides. If it does you randomly generate a new direction.
Append the photon's current position to a list. You might want to do this as a function of distance from the center rather than x,y.
At the end, you plot the list you have built up.
You should also choose a random direction at the very start.
I don't know how you will terminate the loop, for the photon isn't ever guaranteed to leave the sun - just like in the real world. In principle the program might run forever (or until the sun burns out).
There is a slight inaccuracy in that the photon can collide at any instant, not just at the end of one step. But since the steps are small, so is the error introduced by this simplification.
I will point out that you do not need numpy for any of this except perhaps the final plot. The standard Python library has all the math functions you need. Numpy is of course great for manipulating arrays of data, but the only array you will have here is the one you build, a step at a time, of photon position versus time.
As I pointed out in one of my comments, you are modeling the sun as a 2-dimensional object. If you want to do this calculation in three dimensions, you don't need to change this basic approach.
Consider that following program.
import math
import random
def inside_unit_circle(point):
"""
Compute distance of point from origin
"""
distance = math.sqrt(point[0] ** 2 + point[1] ** 2)
return distance < 1
def estimate_mystery(num_trials):
"""
Main function
"""
num_inside = 0
for dumm_idx in range(num_trials):
new_point = [2 * random.random() - 1, 2 * random.random() - 1]
if inside_unit_circle(new_point):
num_inside += 1
return float(num_inside) / num_trials
print estimate_mystery(10000)
This program uses random.random() to generates a random set of points that are uniformly distributed over the square with corners at
(1, 1) (−1, 1)
(1,−1) (−1,−1)
Here, being uniformly distribution means that each point in the square has an equal chance of being generated. The method then tests whether these points lie inside a unit circle.
As one increases the number of trials, the value returned by estimate_mystery tends towards a specific value that has a simple expression involving a well-known constant. Enter this value as a math expression below. (Do not enter a floating point number.)
So you need to run estimate_mystery with increasingly higher numbers of trials. As you do so, it will become clear that the value increases to the following simple expression:
(\sum_{k=1}^{\infty} \frac{e^{i\pi(k+1)}}{2k-1})
It should be noted, however, that this is not the only correct answer. The following would have been valid too, where \zeta is the Riemann zeta function:
However, this does not include the well-known constant e.
I'm not sure why this is confusing. It's quite clear that the sum expression is correct, and it's written quite clearly: the code below the image is very standard LaTeX formatting for mathematical expressions. But to illustrate its correctness, here's a plot showing the convergence when taking the sum to n, and running estimate_mystery up to n as well:
Hrmm... maybe this wasn't what your question wanted? It should also converge to the following, where \gamma is a unit circle around z=0 on the complex plane:
(-i\oint_\gamma z^{-3}e^{\frac{z}{2}}dz)
If you try estimate_mystery() method with different inputs such as with, 100, 1000, 10000, 100000), you will see that the result will be 0.81, 0.781 0.7807 0.7855, accordingly.
It means, the more you increase the trial number, the result is getting closer ( converges ) to 0.7855. This number can be defined with Pi.
You can find it just by simple calculation. Pi * x = 0.7855. From this equation we can find that x ~ 0.25. Therefore, 0.7855 can be described with Pi/4.
As per advice given to me in this answer, I have implemented a Runge-Kutta integrator in my gravity simulator.
However, after I simulate one year of the solar system, the positions are still off by cca 110 000 kilometers, which isn't acceptable.
My initial data was provided by NASA's HORIZONS system. Through it, I obtained position and velocity vectors of the planets, Pluto, the Moon, Deimos and Phobos at a specific point in time.
These vectors were 3D, however, some people told me that I could ignore the third dimension as the planets aligned themselves in a plate around the Sun, and so I did. I merely copied the x-y coordinates into my files.
This is the code of my improved update method:
"""
Measurement units:
[time] = s
[distance] = m
[mass] = kg
[velocity] = ms^-1
[acceleration] = ms^-2
"""
class Uni:
def Fg(self, b1, b2):
"""Returns the gravitational force acting between two bodies as a Vector2."""
a = abs(b1.position.x - b2.position.x) #Distance on the x axis
b = abs(b1.position.y - b2.position.y) #Distance on the y axis
r = math.sqrt(a*a + b*b)
fg = (self.G * b1.m * b2.m) / pow(r, 2)
return Vector2(a/r * fg, b/r * fg)
#After this is ran, all bodies have the correct accelerations:
def updateAccel(self):
#For every combination of two bodies (b1 and b2) out of all bodies:
for b1, b2 in combinations(self.bodies.values(), 2):
fg = self.Fg(b1, b2) #Calculate the gravitational force between them
#Add this force to the current force vector of the body:
if b1.position.x > b2.position.x:
b1.force.x -= fg.x
b2.force.x += fg.x
else:
b1.force.x += fg.x
b2.force.x -= fg.x
if b1.position.y > b2.position.y:
b1.force.y -= fg.y
b2.force.y += fg.y
else:
b1.force.y += fg.y
b2.force.y -= fg.y
#For body (b) in all bodies (self.bodies.itervalues()):
for b in self.bodies.itervalues():
b.acceleration.x = b.force.x/b.m
b.acceleration.y = b.force.y/b.m
b.force.null() #Reset the force as it's not needed anymore.
def RK4(self, dt, stage):
#For body (b) in all bodies (self.bodies.itervalues()):
for b in self.bodies.itervalues():
rd = b.rk4data #rk4data is an object where the integrator stores its intermediate data
if stage == 1:
rd.px[0] = b.position.x
rd.py[0] = b.position.y
rd.vx[0] = b.velocity.x
rd.vy[0] = b.velocity.y
rd.ax[0] = b.acceleration.x
rd.ay[0] = b.acceleration.y
if stage == 2:
rd.px[1] = rd.px[0] + 0.5*rd.vx[0]*dt
rd.py[1] = rd.py[0] + 0.5*rd.vy[0]*dt
rd.vx[1] = rd.vx[0] + 0.5*rd.ax[0]*dt
rd.vy[1] = rd.vy[0] + 0.5*rd.ay[0]*dt
rd.ax[1] = b.acceleration.x
rd.ay[1] = b.acceleration.y
if stage == 3:
rd.px[2] = rd.px[0] + 0.5*rd.vx[1]*dt
rd.py[2] = rd.py[0] + 0.5*rd.vy[1]*dt
rd.vx[2] = rd.vx[0] + 0.5*rd.ax[1]*dt
rd.vy[2] = rd.vy[0] + 0.5*rd.ay[1]*dt
rd.ax[2] = b.acceleration.x
rd.ay[2] = b.acceleration.y
if stage == 4:
rd.px[3] = rd.px[0] + rd.vx[2]*dt
rd.py[3] = rd.py[0] + rd.vy[2]*dt
rd.vx[3] = rd.vx[0] + rd.ax[2]*dt
rd.vy[3] = rd.vy[0] + rd.ay[2]*dt
rd.ax[3] = b.acceleration.x
rd.ay[3] = b.acceleration.y
b.position.x = rd.px[stage-1]
b.position.y = rd.py[stage-1]
def update (self, dt):
"""Pushes the uni 'dt' seconds forward in time."""
#Repeat four times:
for i in range(1, 5, 1):
self.updateAccel() #Calculate the current acceleration of all bodies
self.RK4(dt, i) #ith Runge-Kutta step
#Set the results of the Runge-Kutta algorithm to the bodies:
for b in self.bodies.itervalues():
rd = b.rk4data
b.position.x = b.rk4data.px[0] + (dt/6.0)*(rd.vx[0] + 2*rd.vx[1] + 2*rd.vx[2] + rd.vx[3]) #original_x + delta_x
b.position.y = b.rk4data.py[0] + (dt/6.0)*(rd.vy[0] + 2*rd.vy[1] + 2*rd.vy[2] + rd.vy[3])
b.velocity.x = b.rk4data.vx[0] + (dt/6.0)*(rd.ax[0] + 2*rd.ax[1] + 2*rd.ax[2] + rd.ax[3])
b.velocity.y = b.rk4data.vy[0] + (dt/6.0)*(rd.ay[0] + 2*rd.ay[1] + 2*rd.ay[2] + rd.ay[3])
self.time += dt #Internal time variable
The algorithm is as follows:
Update the accelerations of all bodies in the system
RK4(first step)
goto 1
RK4(second)
goto 1
RK4(third)
goto 1
RK4(fourth)
Did I mess something up with my RK4 implementation? Or did I just start with corrupted data (too few important bodies and ignoring the 3rd dimension)?
How can this be fixed?
Explanation of my data etc...
All of my coordinates are relative to the Sun (i.e. the Sun is at (0, 0)).
./my_simulator 1yr
Earth position: (-1.47589927462e+11, 18668756050.4)
HORIZONS (NASA):
Earth position: (-1.474760457316177E+11, 1.900200786726017E+10)
I got the 110 000 km error by subtracting the Earth's x coordinate given by NASA from the one predicted by my simulator.
relative error = (my_x_coordinate - nasa_x_coordinate) / nasa_x_coordinate * 100
= (-1.47589927462e+11 + 1.474760457316177E+11) / -1.474760457316177E+11 * 100
= 0.077%
The relative error seems miniscule, but that's simply because Earth is really far away from the Sun both in my simulation and in NASA's. The distance is still huge and renders my simulator useless.
110 000 km absolute error means what relative error?
I got the 110 000 km value by subtracting my predicted Earth's x
coordinate with NASA's Earth x coordinate.
I'm not sure what you're calculating here or what you mean by "NASA's Earth x coordinate". That's a distance from what origin, in what coordinate system, at what time? (As far as I know, the earth moves in orbit around the sun, so its x-coordinate w.r.t. a coordinate system centered at the sun is changing all the time.)
In any case, you calculated an absolute error of 110,000 km by subtracting your calculated value from "NASA's Earth x coordinate". You seem to think this is a bad answer. What's your expectation? To hit it spot on? To be within a meter? One km? What's acceptable to you and why?
You get a relative error by dividing your error difference by "NASA's Earth x coordinate". Think of it as a percentage. What value do you get? If it's 1% or less, congratulate yourself. That would be quite good.
You should know that floating point numbers aren't exact on computers. (You can't represent 0.1 exactly in binary any more than you can represent 1/3 exactly in decimal.) There are going to be errors. Your job as a simulator is to understand the errors and minimize them as best you can.
You could have a stepsize problem. Try reducing your time step size by half and see if you do better. If you do, it says that your results have not converged. Reduce by half again until you achieve acceptable error.
Your equations might be poorly conditioned. Small initial errors will be amplified over time if that's true.
I'd suggest that you non-dimensionalize your equations and calculate the stability limit step size. Your intuition about what a "small enough" step size should be might surprise you.
I'd also read a bit more about the many body problem. It's subtle.
You might also try a numerical integration library instead of your integration scheme. You'll program your equations and give them to an industrial strength integrator. It could give some insight into whether or not it's your implementation or the physics that causes the problem.
Personally, I don't like your implementation. It'd be a better solution if you'd done it with mathematical vectors in mind. The "if" test for the relative positions leaves me cold. Vector mechanics would make the signs come out naturally.
UPDATE:
OK, your relative errors are pretty small.
Of course the absolute error does matter - depending on your requirements. If you're landing a vehicle on a planet you don't want to be off by that much.
So you need to stop making assumptions about what constitutes too small a step size and do what you must to drive the errors to an acceptable level.
Are all the quantities in your calculation 64-bit IEEE floating point numbers? If not, you'll never get there.
A 64 bit floating point number has about 16 digits of accuracy. If you need more than that, you'll have to use an infinite precision object like Java's BigDecimal or - wait for it - rescale your equations to use a length unit other than kilometers. If you scale all your distances by something meaningful for your problem (e.g., the diameter of the earth or the length of the major/minor axis of the earth's orbit) you might do better.
To do a RK4 integration of the solar system you need a very good precision or your solution will diverge quickly. Assuming you have implemented everything correctly, you may be seeing the drawbacks with RK for this sort of simulation.
To verify if this is the case: try a different integration algorithm. I found that using Verlet integration a solar system simulation will be much less chaotic. Verlet is much simpler to implement than RK4 as well.
The reason Verlet (and derived methods) are often better than RK4 for long term prediction (like full orbits) is that they are symplectic, that is, conserve momentum which RK4 does not. Thus Verlet will give a better behavior even after it diverges (a realistic simulation but with an error in it) whereas RK will give a non-physical behavior once it diverges.
Also: make sure you are using floating point as good as you can. Don't use distances in meters in the solar system, since the precision of floating point numbers is much better in the 0..1 interval. Using AU or some other normalized scale is much better than using meters. As suggested on the other topic, ensure you use an epoch for the time to avoid accumulating errors when adding time steps.
Such simulations are notoriously unreliable. Rounding errors accumulate and introduce instability. Increasing precision doesn't help much; the problem is that you (have to) use a finite step size and nature uses a zero step size.
You can reduce the problem by reducing the step size, so it takes longer for the errors to become apparent. If you are not doing this in real time, you can use a dynamic step size which reduces if two or more bodies are very close.
One thing I do with these kinds of simulations is "re-normalise" after each step to make the total energy the same. The sum of gravitational plus kinetic energy for the system as a whole should be a constant (conservation of energy). Work out the total energy after each step, and then scale all the object speeds by a constant amount to keep the total energy a constant. This at least keeps the output looking more plausible. Without this scaling, a tiny amount of energy is either added to or removed from the system after each step, and orbits tend to blow up to infinity or spiral into the sun.
Very simple changes that will improve things (proper usage of floating point values)
Change the unit system, to use as much mantissa bits as possible. Using meters, you're doing it wrong... Use AU, as suggested above. Even better, scale things so that the solar system fits in a 1x1x1 box
Already said in an other post : your time, compute it as time = epoch_count * time_step, not by adding ! This way, you avoid accumulating errors.
When doing a summation of several values, use a high precision sum algorithm, like Kahan summmation. In python, math.fsum does it for you.
Shouldn't the force decomposition be
th = atan(b, a);
return Vector2(cos(th) * fg, sin(th) * fg)
(see http://www.physicsclassroom.com/class/vectors/Lesson-3/Resolution-of-Forces or https://fiftyexamples.readthedocs.org/en/latest/gravity.html#solution)
BTW: you take the square root to calculate the distance, but you actually need the squared distance...
Why not simplify
r = math.sqrt(a*a + b*b)
fg = (self.G * b1.m * b2.m) / pow(r, 2)
to
r2 = a*a + b*b
fg = (self.G * b1.m * b2.m) / r2
I am not sure about python, but in some cases you get more precise calculations for intermediate results (Intel CPUs support 80 bit floats, but when assigning to variables, they get truncated to 64 bit):
Floating point computation changes if stored in intermediate "double" variable
It is not quite clear in what frame of reference you have your planet coordinates and velocities. If it is in heliocentric frame (the frame of reference is tied to the Sun), then you have two options: (i) perform calculations in non-inertial frame of reference (the Sun is not motionless), (ii) convert positions and velocities into the inertial barycentric frame of reference. If your coordinates and velocities are in barycentric frame of reference, you must have coordinates and velocities for the Sun as well.
I am just starting out with programming and this assignment is giving me a lot of trouble. How do I change the Monte Carlo code below (used for area under a curve) so that it estimates pi?
from random import uniform
from math import exp
def estimate_area(f, a, b, m, n=1000):
hits = 0
total = m * (b - a)
for i in range(n):
x = uniform(a, b)
y = uniform(0, m)
if y <= f(x):
hits += 1
frac = hits / n
return frac * total
def f(x):
return exp(-x**2)
def main():
print(estimate_area(f, 0, 2, 1))
main()
Any help would be greatly appreciated. Thank you.
I won't solve this for you, but I will give you a hint. Think about embedding a unit circle within a 2x2 square, and about how that might help you to estimate π. Once you figure that out, make use of the inherent symmetries to work with just one of the four quadrants.
This is a common example for Monte Carlo methods, see for example the Wikipedia page on Monte Carlo Method.
Think about this problem in only one quadrant, so a quarter of a circle centered at 0,0 and the radius of 1 and a square from (0,0) to (1,1).
If you randomly put a point in the square at x=uniform(0,1) and y=uniform(0,1), you can check if the point falls within the quarter circle, (x^2.+y^2.)^0.5 <= 1.0.
The chances this happens is related to the ratio of the volumes of the two objects.