0

I am trying to estimate the value of pi using a monte carlo simulation. I need to use two unit circles that are a user input distance from the origin. This is what i have:

import random
import math
import sys

def main():
    numDarts=int(sys.argv[1])
    distance=float(sys.argv[2])
    print(montePi(numDarts,distance))

def montePi(numDarts,distance):
    width=2*(1-distance)
    if distance>=1:
        return(0)
    inCircle=0
    for i in range(numDarts):
        x=(width*(random.random()))-width  
        y=(random.random())
        d=(x-distance)**2+(y-0)**2
        d2=(x-(distance*-1))**2+(y-0)**2
        if d<=1 and d2>=-1:
            inCircle=inCircle+1
    pi=(inCircle/numDarts)*(width*2)
    return pi

main()

This is what i should get-

when distance = 0, about 3.14 when distance = .5, about 1.288 i am getting about 1.6 and .6 respectively, why?

These are my instructions-

Write a program called mcintersection.py that uses the Monte Carlo method to estimate the area of this shape (and prints the result). Your program should take two command-line parameters: distance and numDarts. The distance parameter specifies how far away the circles are from the origin on the x-axis. So if distance is 0, then both circles are centered on the origin, and completely overlap. If distance is 0.5 then one circle is centered at (-0.5, 0) and the other at (0.5, 0). If distance is 1 or greater, then the circles do not overlap at all! In that last case, your program can simply output 0. The numDarts parameter should specify the number of random points to pick in the Monte Carlo process.

In this case, the rectangle should be 2 units tall (with the top at y = 1 and the bottom at y = -1). You could also safely make the rectangle 2 units wide, but this will generally be much bigger than necessary. Instead, you should figure out exactly how wide the shape is, based on the distance parameter. That way you can use as skinny a rectangle as possible.

  • Presumably numDarts matters, not just distance… otherwise, why would you be asking for it? So, what values are you testing for that? – abarnert Sep 25 '13 at 00:29
  • @LegoStormtroopr: No he's not, he's asking us to help him with his homework, after he's tried and gotten pretty far but then gotten stuck, which is perfectly valid for SO. (As long as his teacher doesn't mind.) The question could definitely be improved, but it shouldn't be closed (unless he refuses to improve it). – abarnert Sep 25 '13 at 00:30
  • 3
    Hint: `d2>=-1` is **always** true. Why? Because `d2` is the sum of two squares. Square are always non-negative. So, in fact, `d2 >= 0` is always true. Look for errors ;-) – Tim Peters Sep 25 '13 at 00:37
  • i'm sorry, i am new here and i don't really know what i did wrong. I am just using 1000 darts because that doesn't really change the answer. i'm sorry if my question is bad, my teacher is not the most helpful and moves really fast in class:( – user2807779 Sep 25 '13 at 00:38
  • What version of Python are you using? If it's 2.7 or earlier, that `inCircle/numDarts` is always going to be 0, because they're both integers. If it's 3.0 or later, this isn't a problem. – abarnert Sep 25 '13 at 00:39
  • i am using python 3.2 – user2807779 Sep 25 '13 at 00:42
  • so this part d2=(x-(distance*-1))**2+(y-0)**2 is wrong because the square of a negative is always positive, like tim peters said. i tried multiplying by -1 after the square, but is still gives me similar answers? – user2807779 Sep 25 '13 at 00:44
  • It would probably help you to see what's going on if you print out each dart hit, like `print(x, y, d, d2)`. (Obviously you'll want a smaller `numDarts` value while doing that.) Do the `x` and `y` values look like they're distributed properly? Do `d` and `d2` make sense? – abarnert Sep 25 '13 at 00:50
  • OP, you should check this out:http://stackoverflow.com/questions/982381/finding-pi-digits-using-monte-carlo?rq=1 – Patrick the Cat Sep 25 '13 at 01:24
  • and OP, you're warned for if you're one of the student in my class, I will find out if you copy and paste any of these. – Patrick the Cat Sep 25 '13 at 01:25
  • @Mai: The only changes needed to fix his attempt are adding `/2` and changing `>=-1` to `<=1`. I hope you're not going to penalize him for copying and pasting the `/2` and `<=1` and force him to find some other way to write them. :) – abarnert Sep 25 '13 at 01:34

1 Answers1

4

Notice that your results are almost exactly half what they should be.

If you try printing out the dart locations for distance 0, it should be obvious where the problem arises: The x values are all negative, and they're as low as -2, when clearly they're supposed to be in the range from -1 to 1. So, you're only getting half as many hits as you should. And the problem is the same with any other distance—a bit less obvious to nail down, but obvious enough that something similar is wrong.

So, look at the formula for each x:

x=(width*(random.random()))-width

width*(random.random()) gives you a random number from 0 to width. Subtracting width gives you a random number from -width to 0.

And now, the fix should be obvious:

x=(width*(random.random()))-width/2

Meanwhile, d2 is always going to be positive, because it's the sum of two squares, so d2>=-1 is always true. You wanted to check d2<=1. (If you're working from a description that says you wanted dy>=-1, you probably needed to negate the squares, rather than the expressions inside the squares. But that's going to have the same effect anyway.)

For the 0 distance case, this makes no difference, because the d2 values are between 0 and 1 exactly when the d1 values are. But as distance increases, you will overestimate the counts quadratically more.


Finally, the way the problem is usually described—and the way you specifically described it—the circles are supposed to be centered about points on the X axis. But you're not doing that; you're picking numbers from 0.0 to 1.0. You wanted y=random.random()-.5.

But you're also calculating the distances wrong; you need y-0.5, not y-0 in the d and d2 equations.

By getting both of those wrong, you've effectively just shifted the X axis down by 0.5, which obviously has no effect on the results. So, this could be a useful optimization/simplification, if done intentionally. Or it could be two bugs that just happen to cancel out.


While we're at it, you could simplify most of your expressions to make them more readable. You've got parentheses all over the place around single values, like the (0) in the return statement, or the (random.random()) in the y assignment. Obviously -0 has no useful effect. x-(distance*-1) is the same as x-(-distance) which is the same as x+distance (which also makes the relationship between d and d2 much more obvious). And so on.

abarnert
  • 354,177
  • 51
  • 601
  • 671