Random positioning and movement of a rabbit on a table I was inspired by this:


  If you would put a rabbit randomly on a circular table with radius r=
  1 meter and it moves 1 meter in a random direction, what is the
  chance it won't fall off?


I decided to develop a program to manually test this scenario. My code will run as many tests as you want with (hopefully) more tests resulting in a correct answer. I know a bit of C++, but have decided to learn Python recently.

My code does the following:


Initialises a random starting within a circle with radius 1
Moves 1 unit in a random direction
Updates the pass/fail rate depending on if the movement resulted in a position within the original circle




import math
import random

class RandPoint:
    x = 0
    y = 0

    def __init__(self):
        self.x = random.uniform(-1,1)
        self.y = random.uniform(-1,1)

    def move(self):
        angle = random.uniform(0, 2 * math.pi)
        self.x+=math.cos(angle)
        self.y+=math.sin(angle)

    def check(self):
        return math.sqrt(math.pow(self.x,2)+math.pow(self.y,2)) &lt; 1


passed = 0.0
num = int(raw_input("How many trials would you like? \n"))

for value in range (1,num):
    startingpoint = RandPoint()

    while not(startingpoint.check()):
        startingpoint = RandPoint()

    startingpoint.move()
    passed += int(startingpoint.check())

print (str(num) + " trys, " + str(passed) + " passes, " +      str((passed/num)*100) + "%")
Sampling

This is your approach to placing the rabbit randomly on a circular table:


startingpoint = RandPoint()

while not(startingpoint.check()):
    startingpoint = RandPoint()



How many tries does it take to place a rabbit?  Each attempt succeeds with a probability \$p = \dfrac{\pi}{4}\$, so the expected number of tries is

$$\frac{\pi}{4}\left(1 + 2\left(1 - \frac{\pi}{4}\right) + 3\left(1 - \frac{\pi}{4}\right)^2 + 4\left(1 - \frac{\pi}{4}\right)^3 + \ldots\right) \approx 1.273$$

Can it be done without retrying?  Yes.  You can use

$$\begin{array}{rl}
x =&amp; \sqrt{r}\ \cos \theta \\
y =&amp; \sqrt{r}\ \sin \theta
\end{array}$$

… where \$r\$ is sampled uniformly from the interval [0, 1] and \$\theta\$ is sampled uniformly on [0, 2π].

Which is better for performance: taking a chance with looping (and consuming 27% more random numbers), or performing a square root and trigonometric calculations?  It's hard to say in advance, but it turns out that the performance of my solution below is about the same.  I think that the \$\langle \sqrt{r}\ \cos \theta, \sqrt{r}\ \sin \theta\rangle\$ method is more elegant.

Critique

In Python 2, classes should explicitly derive from object.

In RandPoint, x = 0 and y = 0 are unnecessary, and in fact misleading.  Those variables are not the same as self.x and self.y.

In this case, math.pow() should be written more succinctly using the exponentiation operator, since you don't need the validation that math.pow() performs.  You also don't need math.sqrt() in check().

move() and check() are specific to the exercise at hand and feel out of place in a RandPoint class.  Furthermore, based on just the method name, it's not obvious what check() would check.  Perhaps is_in_unit_disc() would be clearer, but I would choose to reorganize that code altogether.

passed is a count, so it should be an int rather than a float.  A better way to count a bunch of boolean values is to use the built-in sum() function with a generator expression.

The idiom to repeat something num times is for _ in xrange(num): ….  By starting a 1, you skipped one iteration.

To print the result, don't do so many string concatenations.  You can use str.format() instead.

Suggested solution

import math
import random

class Point(object):
    @staticmethod
    def random_polar(r=None):
        """Pick a point in a random direction with some magnitude.
           If r=1, this samples a unit circle uniformly.
           If r=None, this samples a unit disc uniformly."""
        if r is None:
            r = random.uniform(0, 1)**0.5
        theta = random.uniform(0, 2 * math.pi)
        return Point(r * math.cos(theta), r * math.sin(theta))

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def add(self, other):
        self.x += other.x
        self.y += other.y
        return self

def rabbit_trial():
    """Rabbit at a random point in the unit disc moves one unit in
       a random direction.  Return True if the rabbit is still in
       the unit disc, or False if the rabbit is outside the disc."""
    p = Point.random_polar().add(Point.random_polar(r=1))
    return p.x**2 + p.y**2 &lt; 1**2

trials = int(raw_input("How many trials would you like? "))
inside = sum(rabbit_trial() for _ in xrange(trials))
print '{} tries, {} passes, {}%'.format(trials, inside, 100.0 * inside / trials)