Calculate PI using Random Numbers - python

Having trouble with the following question:
In geometry the ratio of the circumference of a circle to its diameter is known as π. The value of π can be estimated from an infinite series of the form:
π / 4 = 1 - (1/3) + (1/5) - (1/7) + (1/9) - (1/11) + ...
There is another novel approach to calculate π. Imagine that you have a dart board that is 2 units square. It inscribes a circle of unit radius. The center of the circle coincides with the center of the square. Now imagine that you throw darts at that dart board randomly. Then the ratio of the number of darts that fall within the circle to the total number of darts thrown is the same as the ratio of the area of the circle to the area of the square dart board. The area of a circle with unit radius is just π square unit. The area of the dart board is 4 square units. The ratio of the area of the circle to the area of the square is π / 4.
To simuluate the throwing of darts we will use a random number generator. The Random module has several random number generating functions that can be used. For example, the function uniform(a, b) returns a floating point random number in the range a (inclusive) and b (exclusive).
Imagine that the square dart board has a coordinate system attached to it. The upper right corner has coordinates ( 1.0, 1.0) and the lower left corner has coordinates ( -1.0, -1.0 ). It has sides that are 2 units long and its center (as well as the center of the inscribed circle) is at the origin.
A random point inside the dart board can be specified by its x and y coordinates. These values are generated using the random number generator. The way we achieve that is:
xPos = random.uniform (-1.0, 1.0)
yPos = random.uniform (-1.0, 1.0)
To determine if a point is inside the circle its distance from the center of the circle must be strictly less than the radius of the circle. The distance of a point with coordinates ( xPos, yPos ) from the center is math.hypot (xPos, yPos). The radius of the circle is 1 unit.
The program that you will be writing will be called CalculatePI. It will have the following structure:
import math
import random
def computePI ( numThrows ):
...
def main ():
...
main()
Your function main() will call the function computePI() for a given number of throws. The function computePI() will simulate the throw of a dart by generating random numbers for the x and y coordinates. You will determine if that randomly generated point is inside the circle or not. You will do this as many times as specified by the number of throws. You will keep a count of the number of times a dart lands within the circle. That count divided by the total number of throws is the ratio π/4. The function computePI() will then return the computed value of PI.
In your function main() you want to experiment and see if the accuracy of PI increases with the number of throws on the dartboard. You will compare your result with the value given by math.pi. The quantity Difference in the output is your calculated value of PI minus math.pi. Use the following number of throws to run your experiment - 100, 1000, 10,000, 100,000, 1,000,000, and 10,000,000. You will call the function computePI() with these numbers as input parameters. Your output will be similar to the following, i.e. the actual values of your Calculated PI and Difference will be different but close to the ones shown:
Computation of PI using Random Numbers
num = 100 Calculated PI = 3.320000 Difference = +0.178407
num = 1000 Calculated PI = 3.080000 Difference = -0.061593
num = 10000 Calculated PI = 3.120400 Difference = -0.021193
num = 100000 Calculated PI = 3.144720 Difference = +0.003127
num = 1000000 Calculated PI = 3.142588 Difference = +0.000995
num = 10000000 Calculated PI = 3.141796 Difference = +0.000204
Difference = Calculated PI - math.pi
Your output must be in the above format. The number of throws must be left justified. The calculated value of π and the difference must be expressed correct to six places of decimal. There should be plus or minus sign on the difference. Read the relevant sections in the book on formatting.
Till now I have done:
import math
import random
def computePI (numThrows):
xPos = random.uniform (-1.0, 1.0)
yPos = random.uniform (-1.0, 1.0)
in_circle = 0
throws = 0
while (throws < numThrows):
if math.hypot (xPos, yPos) <= 1:
in_circle += 1
throws += 1
pi = (4 * in_circle) / numThrows
return pi
def main ():
throws = (100, 1000, 10000, 100000, 1000000, 10000000)
for numThrows in throws[0:7]:
main ()
I am having trouble calling the ComputePI function in the Main function. Also how do I print num with left indentation and ensure that all numbers have the required decimal space? Thank you!

Your program has three main issues:
Generating random numbers in the wrong place
xPos = random.uniform (-1.0, 1.0)
yPos = random.uniform (-1.0, 1.0)
These lines are executed only once when you enter the computePI() function. You then proceed to calculate the exact same value of hypot for hundreds or even thousands of iterations. Put these lines inside the while loop.
Integer arithmetic
pi = (4 * in_circle) / numThrows
Since in_circle and numThrows are both integers, this calculation will be performed using integer arithmetic (in Python 2, at least). Changing the constant from 4 to 4.0 will change this to a floating point calculation:
pi = (4.0 * in_circle) / numThrows
Incomplete main() function:
There's no need to use a subset of your throws tuple, and you haven't added a body to your for loop. Try this:
for numThrows in (100, 1000, 10000, 100000, 1000000, 10000000):
randpi = computePI(numThrows)
diff = randpi - math.pi
print "num = %-8d Calculated PI = %8.6f Difference = %+9.6f" % \
(numThrows, randpi, diff)

This is how I find it easy.
import random
import math
def approximate_pi():
total_points = 0
within_circle = 0
for i in range (10000):
x = random.random()
y = random.random()
total_points += 1
distance = math.sqrt(x**2+y**2)
if distance < 1:
within_circle += 1
if total_points % 1000 == 0:
pi_estimate = 4 * within_circle / total_points
yield pi_estimate
set total point generated and points withing the circle to zero
total_points = 0
within_circle = 0
generate the random values of x and y for multiple times. Calculate the distance of the point from the center of the circle or (0,0). Then if the distance is less than one it means that it's within the circle so it is incremented.
distance = math.sqrt(x**2+y**2)
if distance < 1:
within_circle += 1
Now if you have generated let's say multiple of 1000(1000 because we have taken the range for 10,000 so 1000 to get 10 values of pi), calculate the estimated value of pi using this formula which you know already.and the tied the estimate value(pi_estmate)
if total_points % 1000 == 0:
pi_estimate = 4 * within_circle / total_points
yield pi_estimate
pi_estimates = list(es for es in approximate_pi())
errors = list(estimate-math.pi for estimate in approximate_pi())
print(pi_estimates)
print(errors)
OUTPUT:
Estimates
[3.096, 3.142, 3.1253333333333333, 3.121, 3.1384, 3.136, 3.1314285714285712, 3.133, 3.1342222222222222]
Errors
[0.04240734641020705, 0.02240734641020703, 0.03307401307687341, 0.020407346410206806, 0.02320734641020694, 0.0017406797435404187, -0.009021225018364554, -0.011592653589793223, -0.016703764700904067]
Hope you understood, I hope my explanation was easy to understand, I am a beginner and learning stuff if there is anything wrong please feel free to notify.
Thank you

Essentially what the statement you've written above says:
import math
def find_pi(iterations):
return sum(
1 for _ in range(iterations) if math.hypot(
random.random(), random.random()) <= 1) * 4.0/iterations

Related

How to generate random geocoordinates using random module

How to generate random latitude and longitude using Python 3 random module? I already googled and read documentation and not found a way to do this.
The problem when using uniform distributions for both the latitude and the longitude
is that physically, the latitude is NOT uniformly distributed.
So if you plan to use these random points for something like some statiscal averaging computation,
or a physics Monte-Carlo simulation, the results will risk being incorrect.
And if you plot a graphical representation of the “uniform” random points, they will seem to cluster in the polar regions.
To picture that, consider on planet Earth the zone that lies between 89 and 90 degrees of latitude (North).
The length of a degree of latitude is 10,000/90 = 111 km. That zone is a circle of radius 111 km,
centered around the North Pole. Its area is about 3.14 * 111 * 111 ≈ 39,000 km2
On the other hand, consider the zone that lies between 0 and 1 degree of latitude.
This is a strip whose length is 40,000 km (the Equator) and whose width is 111 km,
so its area is 4.44 millions km2. Much larger than the polar zone.
A simple algorithm:
A possibility is to use Gaussian-distributed random variables, as provided by the Python library.
If we build a 3D vector whose 3 components have Gaussian distributions, the overall
probability distribution is like
exp(-x2) * exp(-y2) * exp(-z2)
but this is the same thing as exp(-(x2 + y2 + z2))
or exp(-r2), where r is the distance from the origin.
So these vectors have no privileged direction. Once normalized to unit length, they are uniformly
distributed on the unit sphere. They solve our problem with the latitude distribution.
The idea is implemented by the following Python code:
import math
import random
def randlatlon1():
pi = math.pi
cf = 180.0 / pi # radians to degrees Correction Factor
# get a random Gaussian 3D vector:
gx = random.gauss(0.0, 1.0)
gy = random.gauss(0.0, 1.0)
gz = random.gauss(0.0, 1.0)
# normalize to an equidistributed (x,y,z) point on the unit sphere:
norm2 = gx*gx + gy*gy + gz*gz
norm1 = 1.0 / math.sqrt(norm2)
x = gx * norm1
y = gy * norm1
z = gz * norm1
radLat = math.asin(z) # latitude in radians
radLon = math.atan2(y,x) # longitude in radians
return (round(cf*radLat, 5), round(cf*radLon, 5))
A sanity check:
Euclidean geometry provides a formula for the probability of a spherical zone
defined by minimal/maximal latitude and longitude. The corresponding Python code is like this:
def computeProbaG(minLat, maxLat, minLon, maxLon):
pi = math.pi
rcf = pi / 180.0 # degrees to radians Correction Factor
lonProba = (maxLon - minLon) / 360.0
minLatR = rcf * minLat
maxLatR = rcf * maxLat
latProba = (1.0/2.0) * (math.sin(maxLatR) - math.sin(minLatR))
return (lonProba * latProba)
And we can also compute an approximation of that same probability by random sampling, using
the random points provided by a function such as randlatlon1, and counting what
percentage of them happen to fall within the selected zone:
def computeProbaR(randlatlon, ranCount, minLat, maxLat, minLon, maxLon):
norm = 1.0 / ranCount
pairs = [randlatlon() for i in range(ranCount)]
acceptor = lambda p: ( (p[0] > minLat) and (p[0] < maxLat) and
(p[1] > minLon) and (p[1] < maxLon) )
selCount = sum(1 for p in filter(acceptor, pairs))
return (norm * selCount)
Equipped with these two functions, we can check for various geometric parameter sets
that the geometric and probabilistic results are in good agreement, with ranCount set to one million random points:
ranCount = 1000*1000
print (" ")
probaG1 = computeProbaG( 30, 60, 45, 90)
probaR1 = computeProbaR(randlatlon1, ranCount, 30, 60, 45, 90)
print ("probaG1 = %f" % probaG1)
print ("probaR1 = %f" % probaR1)
print (" ")
probaG2 = computeProbaG( 10, 55, -40, 160)
probaR2 = computeProbaR(randlatlon1, ranCount, 10, 55, -40, 160)
print ("probaG2 = %f" % probaG2)
print ("probaR2 = %f" % probaR2)
print (" ")
Execution output:
$ python3 georandom.py
probaG1 = 0.022877
probaR1 = 0.022852
probaG2 = 0.179307
probaR2 = 0.179644
$
So the two sort of numbers appears to agree reasonably here.
Addendum:
For the sake of completeness, we can add a second algorithm which is less intuitive but derives from a wider statistical principle.
To solve the problem of the latitude distribution, we can use the Inverse Transform Sampling theorem. In order to do so, we need some formula for the probability of the latitude to be less than an arbitrary prescribed value, φ.
The region of the unit 3D sphere whose latitude is less than a given φ is known as a spherical cap. Its area can be obtained thru elementary calculus, as described here for example.
The spherical cap area is given by formula: A = 2π * (1 + sin(φ))
The corresponding probability can be obtained by dividing this area by the overall area of the unit 3D sphere, that is 4π, corresponding to φ = φmax = π/2. Hence:
p = Proba{latitude < φ} = (1/2) * (1 + sin(φ))
Or, conversely:
φ = arcsin (2*p - 1)
From the Inverse Transform Sampling theorem, a fair sampling of the latitude (in radians) is obtained by replacing the probability p by a random variable uniformly distributed between 0 and 1. In Python, this gives:
lat = math.asin(2*random.uniform(0.0, 1.0) - 1.0)
As for the longitude, this is an independent random variable that is still uniformly distributed between -π and +π (in radians). So the overall Python sampler code is:
def randlatlon2r():
pi = math.pi
cf = 180.0 / pi # radians to degrees Correction Factor
u0 = random.uniform(0.0, 1.0)
u1 = random.uniform(0.0, 1.0)
radLat = math.asin(2*u0 - 1.0) # angle with Equator - from +pi/2 to -pi/2
radLon = (2*u1 - 1) * pi # longitude in radians - from -pi to +pi
return (round(radLat*cf,5), round(radLon*cf,5))
This code has been found to pass successfully the sanity check as described above.
Generate a random number between
Latitude: -85 to +85 (actually -85.05115 for some reason)
Longitude: -180 to +180
As #tandem wrote in his answer, the range for latitude is almost -90 to +90 (it is cut on maps) and for longitude it is -180 to +180. To generate random float numbers in this range use random.uniform function:
import random
# returns (lat, lon)
def randlatlon():
return (round(random.uniform( -90, 90), 5),
round(random.uniform(-180, 180), 5))
It is rounded to 5 digits after comma because that extra accuracy is unnecessary.

How do I program a spatially dependend random number distribution?

I wrote a routine that distributes circles randomly (uniformly) with an arbitrary diameter in my study area.
def no_nearby_dots(new_dot, dots_sim, min_distance):
for dot in dots_sim:
if np.sqrt((dot[0] - new_dot[0]) ** 2 + (dot[1] - new_dot[1]) ** 2) <= min_distance:
return False
return True
while realizations < simulations:
dots_sim = []
new_dot = True
dots_sim.append((np.random.uniform(xmin, xmax), np.random.uniform(ymin, ymax)))
failed_attempts = 0
while new_dot:
xp = np.random.uniform(xmin, xmax)
yp = np.random.uniform(ymin, ymax)
if no_nearby_dots((xp, yp), dots_sim, diameter):
dots_sim.append((xp, yp))
failed_attempts = 0
else:
failed_attempts += 1
if len(dots_sim) == n_sim:
new_dot = False
if failed_attempts > 2000:
new_dot = False
print('ERROR...exit loop')
break
x_sim = [dot[0] for dot in dots_sim]
y_sim = [dot[1] for dot in dots_sim]
I want to introduce a second circle around the initial ones where the possibility of distributing points reduces exponentially towards the inner border -> I want to prevent a "hard" border, the points are allowed to occur anywhere on the plane but not closer than diameter, additionally they can only occur to a certain degree between diameter and diameter2.
Are there any ideas how to do that?
Here is an idea.
Choose a random radius between diameter/2 and diameter2/2, then generate a random point in the circle formed by that radius. There are many ways to choose a radius that meets your requirements. For example, the following chooses a radius such that radii very close to diameter2/2 are much more likely to be chosen:
radius = (diameter1/2) + ((diameter2/2) - (diameter1/2)) * random.random()**(1/20)
Note that 1/20 is the 20th root of a uniform (0, 1) random number. Try changing 1/20 to a different value and see what happens.
There are other ways to choose a radius this way, and they can all be described by a probability density function (for more information, see the following answer: Generate a random point within a circle (uniformly), which shows how a linear density function leads to a uniform distribution of points in a circle).
I solved it, and this is what I did:
while realizations < simulations:
dots_sim = []
new_dot = True
dots_sim.append((np.random.uniform(x_min, x_max), np.random.uniform(y_min, y_max)))
failed_attempts = 0
while new_dot:
x = np.random.uniform(x_min, x_max)
y = np.random.uniform(y_min, y_max)
diameter_simulation = np.random.uniform(min_diameter, max_diameter)
if no_nearby_dots((x, y), dots_sim, diameter_simulation):
dots_sim.append((x, y))
failed_attempts = 0
else:
failed_attempts += 1
if len(dots_sim) == len(x_coordinate):
new_dot = False
if failed_attempts > 1000:
new_dot = False
print('ERROR... -> no more space to place QDs! -> exit loop!')
break
What I did was creating diameters for my circles also using uniformly distributed numbers in an arbitrary interval, which smoothes my cumulative distribution function. This is the solution I needed, but it might not fit the initial question very well (or the question was formulated inaccurately in the first place :p)

Improving a Monte Carlo code to find the volume of sphere in Higher dimension. (PYTHON)

Considering Gaussian shape we can find the volume of n dimensional volume of a sphere. My intention is to find the volume using Monte Carlo method.
Using the gaussian integral I have found the formula
What I understand that the ratio of the points inside the n-dimensional sphere to the total number of points will then be roughly the same as the ratio of the volume of the ball to that of the cube. I mean the mass density would never change , whatever dimension I use.
Therefore I assume I should follow the same technique what I used to find the value of pi using Monte Carlo method.
I don't understand how to follow the code which I evaluated to find the value of pi.
import random
TIMES_TO_REPEAT = 10**5
LENGTH = 10**5
def in_circle(x, y):
return x**2 + y**2 < LENGTH**2
inside_count = 0
for _ in range(TIMES_TO_REPEAT):
point = random.randint(0,LENGTH), random.randint(0,LENGTH)
if in_circle(*point):
inside_count += 1
pi = (inside_count / TIMES_TO_REPEAT) * 4
print(pi)
How can I apply the inequality condition in the code I have mentioned so the mass density would be same and I can find the value of volume in Higher dimension.?
import random
N = 10**5 # number of trials (ie, number of points to sample)
R = 10**5 # circle radius
def in_sphere(x, y, z):
return x**2 + y**2 + z**2 < R**2
c = 0
for _ in range(N):
p = random.randint(0,R), random.randint(0,R), random.randint(0,R)
if in_sphere(*p):
c += 1
pi = 6 * c / N
print(pi)
The basic idea: if a circle (radius R) is inscribed inside a square (then, its edge must be 2R), then the ratio (the area of circle over the area of square) will be π/4. So, if you pick N points at random inside the square, approximately N * π/4 of those points should fall inside the circle.
hope the annotated/revised code help you understand the MC logic
import random
N = 10**5 # number of trials (ie, number of points to sample)
R = 10**5 # circle radius
# whether p(x,y) is inside a circle
def in_circle(x, y):
return x**2 + y**2 < R**2
# use integer ops as much as possible for speed
c = 0
for i in range(N):
x, y = random.randint(0,R), random.randint(0,R)
if in_circle(x, y):
c += 1
pi = 4 * c / N
print(pi) # pi-> 3.14

Trigonometry: sin(x) getting negative value

I am trying to solve a homework: I am required to write a program which will calculate the length of a ladder based on two inputs, that is the desired height to be reached and the angle created by leaning the ladder toward the wall.
I used the following formula to convert degrees to radians :
radians = (math.pi / 180) * x # x is the given angle by the user.
I imported the math library as well to use its functions.
def main():
import math
print("this program calculates the length of a ladder after you give the height and the angle")
h = eval(input("enter the height you want to reach using the ladder"))
x = eval(input("enter the angle which will be created be leaning the ladder to the wall"))
radians = ( math.pi / 180 ) * x
length = h / math.sin(x)
print("the length is:", length)
main()
What exactly am I doing wrong?
I know the code is missing something and would appreciate it if someone could help me fill the gap.
You never used radians after you calculate it.
i.e. length = h / math.sin(radians)
To make crickt_007's right answer absolutely clear: radians which you did not use after you calculate it should be the argument of the sine:
length = h / math.sin(radians)
you calculate radians,thats ok,but problem is you never used that radians value. i think your code must be changed as follows :)
def main():
import math
print("this program calculates the length of a ladder after you give the height and the angle")
h = eval(input("enter the height you want to reach using the ladder"))
x = eval(input("enter the angle which will be created be leaning the ladder to the wall"))
radians = ( math.pi / 180 ) * x
length = h / math.sin(radians)
print("the length is:", length)
main()
if your both input will be 5,output is the length is: 57.36856622834928

Random generator function in Python

I have been learning Java since September, but have been given an assignment for a science course by a professor to create a program in Python that will generate a random diameter using a max and minimum value, and 15 random points within this sphere (x, y, z).
I need to make a random number generator that generates a number between 0.0 and 1.0 so I can plug it into my formula to find a random diameter. If the random number is RN, it would be: [(RN*(max-min))+min]
At first I used this random function:
from random import*
RN=random():
The problem is, this random function is [0.0, 1.0). In other words, it does not include 1.0.
How can I create a function that includes 1.0?
Also, if you don't mind, can you help me with finding the y and z coordinates? I know how to find x.
The formula for the y value is y=+ or - sqrt(r^2-x^2) (to be randomly generated too).
I would have the x value, which is the result from the random function [0.0-1.0], and the radius which would be half my diameter. I am a complete beginner at Python, how do I initialize my x and y and put the above formula in?
The formula for z is similar, z=+ or - sqrt(-x^2-y^2+r^2) (to be randomly generated as well)
These are using the formulas for a circle:
radius: r=sqrt(x^2+y^2)
sphere:r=sqrt(x^2+y^2+z^2)
I would be incredibly grateful if you could answer any part of my question, thank you so much for taking the time to read this!!
**by the way, I am usinHi! I have been learning Java since September, but have been given an assignment for a science course by a professor to create a program in Python that will generate a random diameter using a max and minimum value, and 15 random points within this sphere (x, y, z).
I need to make a random number generator that generates a number between 0.0 and 1.0 so I can plug it into my formula to find a random diameter. If the random number is RN, it would be: [(RN*(max-min))+min]
At first I used this random function:
from random import*
RN=random():
The problem is, this random function is [0.0, 1.0). In other words, it does not include 1.0.
How can I create a function that includes 1.0?
Also, if you don't mind, can you help me with finding the y and z coordinates? I know how to find x.
The formula for the y value is y=+ or - sqrt(r^2-x^2) (to be randomly generated too).
I would have the x value, which is the result from the random function [0.0-1.0], and the radius which would be half my diameter. I am a complete beginner at Python, how do I initialize my x and y and put the above formula in?
The formula for z is similar, z=+ or - sqrt(-x^2-y^2+r^2) (to be randomly generated as well)
These are using the formulas for a circle:
radius: r=sqrt(x^2+y^2)
sphere:r=sqrt(x^2+y^2+z^2)
I would be incredibly grateful if you could answer any part of my question, thank you so much for taking the time to read this!!!
**by the way, I am using python x,y spyder!
random.uniform will give you a uniformly distributed random number between a given minimum and maximum.
Just generate random points and check if they are within the sphere. If they are not, discard them and try again.
import math
import random
def generate_points(n_points, min_diameter=0, max_diameter=1):
diameter = random.uniform(min_diameter, max_diameter)
radius = diameter / 2
count = 0
while count < n_points:
x, y, z = [random.uniform(-radius, radius) for _ in range(3)]
distance = math.sqrt(x * x + y * y + z * z)
if distance <= radius:
yield (x, y, z)
count += 1
for x, y, z in generate_points(10):
print x, y, z
Note: this may bias what points are generated. (I'm not sure, it's probably okay actually.) Another approach might be to use polar coordinates, choosing two random angles and a random radius (offset from center).
Here is the math for that approach:
theta = random.uniform(0, 2 * math.pi)
phi = random.uniform(-math.pi / 2, math.pi / 2)
x = r * cos(theta) * cos(phi)
y = r * sin(phi)
z = r * sin(theta) * cos(phi)
See here for more on distribution:
https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability
http://mathworld.wolfram.com/SpherePointPicking.html (this one is about points on the surface of the sphere, so might not be as useful)
I used this for a random generator and it works, i'm not sure if it's the right one you're looking for but I hope it helps
import random
maths_operator_list=['+','-','*']
maths_operator = random.choice(maths_operator_list)
number_one = random.randint(0,20)
number_two = random.randint(0,20)
correct_answer = 0
print(str(number_one), str(maths_operator), number_two)
if maths_operator == '+':
correct_answer = number_one + number_two
elif maths_operator == '-':
correct_answer = number_one - number_two
elif maths_operator == '*':
correct_answer = number_one * number_two
print(correct_answer)
You can use random.range
random.randrange(-15,15,.1)
That will find a number between -15 and 15 that is divisible by 0.1.
As answer to the random number generator :
RN = random.uniform(a, b)
Return a random integer N such that a <= N <= b.
As how to initiate x and y, y will be initialize when you set y = to something, it does it automatically.

Categories