patternpythonMajor
Monte Carlo pi calculation
Viewed 0 times
montecalculationcarlo
Problem
In order to learn the basics of Monte Carlo I calculated pi with it.
I also wrote an explanation of the reasoning behind the code.
Down here you can see the circle with random points that I simulated in my code.
I also wrote an explanation of the reasoning behind the code.
Down here you can see the circle with random points that I simulated in my code.
"""
This programme calculates pi with Monte Carlo
Given a square and a circle inside it.
We have
Area_of_the_square = LENGTH ** 2
Area_of_the_circle = radius ** 2 * pi => (LENGTH ** 2) / 4 * pi
The circle is obviously smaller than the square.
We have the equation:
Area_of_the_square * number_less_than_one == Area_of_the_circle
This programme is going to put a big number of points inside the square
(I suggest TIMES_TO_REPEAT = 10**5).
It will then count how many of them are inside the circle,
number_less_than_one = points_inside_the_circle / total_points
After doing some simple math with this formula:
Area_of_the_square * number_less_than_one == Area_of_the_circle
we get that
pi = number_less_than_one * 4
NOTE: This method is deadly slow and quite complicated,
I made this programme just in order to learn.
"""
import random
TIMES_TO_REPEAT = 10**5
LENGTH = 10**5
CENTER = [LENGTH/2,LENGTH/2]
def in_circle(point):
x = point[0]
y = point[1]
center_x = CENTER[0]
center_y = CENTER[1]
radius = LENGTH/2
return (x - center_x)**2 + (y - center_y)**2 < radius**2
count = inside_count = 0
for i in range(TIMES_TO_REPEAT):
point = random.randint(1,LENGTH),random.randint(1,LENGTH)
if in_circle(point):
inside_count += 1
count += 1
pi = (inside_count / count) * 4
print(pi)Solution
Your code seems to be working. Here are a few things to make it better.
Tuple unpacking
can be written :
Remove what is not required
You maintain a
Variable name
Removing the counting logic
Using iterators and
Underscore for throwaway values
In your iteration, you don't really care about the value of
Move things to a function
It makes things easier to test
Add an
It is quite common to define functions/values/classes/whatever from your python file but to use them only behind an
Oops
Currently, changing the value of LENGTH seems to be changing the result. This is probably wrong.
At this point, my code looks like :
Using the right type and the right values
In order to solve the problem above and make things easier, you could change a bit the referential you are using.
Instead of using a circle of radius R (equal to length/2) and with center (R, R) and picking integer values in square [1, R] [1, R] maybe you could consider a circle of radius R and with center 0 and pick floating values in [-R, +R]. Even better, you could focus on the corner of the circle by considering points in [0, R][0, R]. Finally, R is not really important here, you could pick 1.
Then the code becomes :
And then, this can be simplified.
Tuple unpacking
x = point[0]
y = point[1]can be written :
x, y = pointRemove what is not required
You maintain a
count variable but you already know the count : it is TIMES_TO_REPEAT.Variable name
TIMES_TO_REPEAT does not seem to be such a good name. NB_POINTS would probably be better.Removing the counting logic
Using iterators and
sum, you can remove the logic to count points :inside_count = sum(1 for i in range(NB_POINTS) if in_circle((random.randint(1,LENGTH),random.randint(1,LENGTH))))Underscore for throwaway values
In your iteration, you don't really care about the value of
i. It is quite a common thing to call it _ (more about this).Move things to a function
It makes things easier to test
def compute_pi(nb_it):
inside_count = sum(1 for _ in range(nb_it) if in_circle((random.randint(1,LENGTH),random.randint(1,LENGTH))))
return (inside_count / nb_it) * 4Add an
if main guardIt is quite common to define functions/values/classes/whatever from your python file but to use them only behind an
if main guard to make things easier to reuse via module imports for instance.Oops
Currently, changing the value of LENGTH seems to be changing the result. This is probably wrong.
At this point, my code looks like :
import random
NB_POINTS = 10**5
LENGTH = 10**5
CENTER = [LENGTH/2,LENGTH/2]
def in_circle(point):
x,y = point
center_x, center_y = CENTER
radius = LENGTH/2
return (x - center_x)**2 + (y - center_y)**2 < radius**2
def compute_pi(nb_it):
inside_count = sum(1 for _ in range(nb_it) if in_circle((random.randint(1,LENGTH),random.randint(1,LENGTH))))
return (inside_count / nb_it) * 4
if __name__ == "__main__":
print(compute_pi(NB_POINTS))Using the right type and the right values
In order to solve the problem above and make things easier, you could change a bit the referential you are using.
Instead of using a circle of radius R (equal to length/2) and with center (R, R) and picking integer values in square [1, R] [1, R] maybe you could consider a circle of radius R and with center 0 and pick floating values in [-R, +R]. Even better, you could focus on the corner of the circle by considering points in [0, R][0, R]. Finally, R is not really important here, you could pick 1.
Then the code becomes :
import random
NB_POINTS = 10**5
def in_circle(x, y):
return x**2 + y**2 < 1
def compute_pi(nb_it):
inside_count = sum(1 for _ in range(nb_it) if in_circle(random.random(),random.random()))
return (inside_count / nb_it) * 4
if __name__ == "__main__":
print(compute_pi(NB_POINTS))And then, this can be simplified.
import random
NB_POINTS = 10**5
def compute_pi(nb_it):
return 4 * sum(1 for _ in range(nb_it) if random.random()**2 + random.random()**2 < 1) / nb_it
if __name__ == "__main__":
print(compute_pi(NB_POINTS))Code Snippets
x = point[0]
y = point[1]x, y = pointinside_count = sum(1 for i in range(NB_POINTS) if in_circle((random.randint(1,LENGTH),random.randint(1,LENGTH))))def compute_pi(nb_it):
inside_count = sum(1 for _ in range(nb_it) if in_circle((random.randint(1,LENGTH),random.randint(1,LENGTH))))
return (inside_count / nb_it) * 4import random
NB_POINTS = 10**5
LENGTH = 10**5
CENTER = [LENGTH/2,LENGTH/2]
def in_circle(point):
x,y = point
center_x, center_y = CENTER
radius = LENGTH/2
return (x - center_x)**2 + (y - center_y)**2 < radius**2
def compute_pi(nb_it):
inside_count = sum(1 for _ in range(nb_it) if in_circle((random.randint(1,LENGTH),random.randint(1,LENGTH))))
return (inside_count / nb_it) * 4
if __name__ == "__main__":
print(compute_pi(NB_POINTS))Context
StackExchange Code Review Q#69370, answer score: 30
Revisions (0)
No revisions yet.