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.
Related
At the origin of the coordinate system we place a circle with radius 1 and a square with radius 1, that is, with page 2.
The plane of the circle is πr2, so π, and the square of the square is a2, that is, 4. The square of the square covered with the circle is π / 4.
Choose two random coordinates within the square, i.e. two random numbers between -1 and 1. The probability that this point lies within the circle is equal to π / 4.
We do this a thousand times. There will be around n = 1000 × π / 4 points within the circle. If they did not know how much it was, they could actually do this experiment; with them we get the upper n, and from this n, if we turn the formula around, we calculate the value of π.
Write a program that draws out 1000 random coordinates within a square, (quietly) counts how much it is calculated in this way within the circle and in the end.
This will help you: if you write from random random * at the beginning of the program, the random () function returns a random number between 0 and 1. How to convert it into numbers between -1 and 1, consider it yourself. You also deal with mathematics yourself.
enter image description here
Rather than giving you a complete answer... as this is a school project, that would deprive you of some learning. I'll give you some pointers:
If you need random numbers between -1 and 1, but random.random() only gives values between 0 and 1. What do you need to do to the random numbers to get the range you need? (One simple way would be to multiply by 2 - resulting in a number between 0 and 2, then subtract by 1, to bring it to -1 to 1)
Try pyplot for plotting the graph so you can visualise the answer to check to see if your doing the right thing.
Hi recently I was writing a program with a bunch dots that the player had to avoid. The dots changed direction, but can't turn too much in a given timeframe.
I had a method of doing this but was inefficient as I had to arcsine, knowing the cosed angle and sined angle, then sine and cosine that angle. I thought of just returning the cosine and sined angle, but theres one problem. Once I received the cosine and sine, I need too know if it is too different from my current state.
With the angle this would be easy as I would just have too see the difference, here's a model program,(the code I currently have uses the angle, and isn't very helpful). I have tried graphing sine and cosine and trying to observe any patterns, none obvious showed up.
import math
def sendTargRot():
#I actually use a fairly long method to find it's current rotation, but a random.random() is a fair replacement, so in my real thing there would be no angle calculation
pretendAngle = math.pi*random.random()-math.pi
pretendCosedX = math.cos(pretendAngle)
pretendSinedX = math.sin(pretendAngle)
def changeDotAngle():
targRot = sendTargRot
#make sure targRot is not too much from current rotation
#do stuff with the angle, change dot's rotation
If you want to just give me an algorithm without code that is just as acceptable!
EDIT: I can't really change sendTargRot so it gives a proper rotation, because that would require me knowing the current angle, and really it's just moving the problem.
To get angle between two vectors you can use atan2 function
angle = Math.atan2(a.x * b.y - a.y * b.x, a.x * b.x + a.y * b.y)
if you already have got cosines and sines (the same as coordinates on unit circle):
angle = Math.atan2(cos(a) * sin(b) - sin(a) * cos(b), cos(a) * cos(b) + sin(a) * sin(b))
This approach gives angle needed to rotate a until it coincides with b accounting for direction (in range -Pi..Pi)
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.
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.
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.