3

I am trying to generate random x and y coordinates within a ring, which has an outer radius of 3.5 and an inner radius of 2. Therefor the following must be true for x and y:

x**2 + y**2 < 12.25 and x**2 + y**2 > 4

I wrote the following function:

def meteorites():
    circle = False
    while circle == False:        
        r = np.array([uniform(-6., 6.), uniform(-6., 6.)])
        # we will regenerate random numbers untill the coordinates
        # are within the ring x^2+y^2 < 3,5^2 and x^2+y^2 > 2^2
        if (r[0]**2+r[1]**2 < 12.25) and (r[0]**2+r[1]**2 > 4.):
            circle = True

       else :
            circle = False

    return r[0], r[1]

x = np.zeros(1000)
y = np.zeros(1000)
for i in range(1000):
    x[i] = meteorites()[0]
    y[i] = meteorites()[1]
plt.scatter(x,y)
plt.show()  

When I plot the resulting coordinates I get a square from -3.5 to 3.5. I can't seem to find the problem. I'm also not sure if it's a coding error, or some dum math problem. Since you guys are usually good at both, can you see what I'm doing wrong here?

user90465
  • 187
  • 2
  • 13

7 Answers7

8

To get uniform distribution of random point in the ring, one should take relative areas of thin circular regions into account. How it works for the circle enter image description here

For your case generate uniform distribution of SquaredR in range of squared inner and outer radii. Pseudocode:

 Fi  = RandomUniform(0, 2 * Pi)
 SquaredR  = RandomUniform(inner*inner, outer*outer)
 R = Sqrt(SquaredR)
 x,y = R * Cos(Fi), R * Sin(Fi)
MBo
  • 77,366
  • 5
  • 53
  • 86
6

Take a random angle and a random distance between the two constraints; you'll need to produce a uniform distribution in a circle:

from math import sin, cos, radians, pi, sqrt

def meteorites():
    angle = uniform(0, 2 * pi)  # in radians
    distance = sqrt(uniform(4, 12.25))
    return distance * cos(angle), distance * sin(angle)
Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
5

You're getting random points that don't fall on your ring because these two lines don't do what you want:

x[i] = meteorites()[0]
y[i] = meteorites()[1]

These assign an x value from one point on the ring to x[i], and the y value from a different point on the ring to y[i]. You get coordinates from different points because you're calling meteorites() twice.

Instead, you probably want to call the function once, and then assign to each coordinate, or do an assignment with iterable-unpacking where both targets are on the left side of the equals sign:

x[i], y[i] = meteorites()
Blckknght
  • 100,903
  • 11
  • 120
  • 169
3

Your implementation will also work if you correct one line: insstead of calling meteorites() twice, call just once.

x = np.zeros(1000)
y = np.zeros(1000)
for i in range(1000):
    x[i], y[i] = meteorites()
plt.scatter(x,y)
plt.show()  

enter image description here

Sandipan Dey
  • 21,482
  • 2
  • 51
  • 63
2

as @Martijn Pieters suggested, simply draw the polar coordinates uniformly in the range you require.

theta = uniform(0,2*np.pi)
r = uniform(2.,3.5)
x = r*np.cos(theta)
y = r*np.sin(theta)

EDIT: There will be equal probability for every point in the ring to occur.

But practically there will be less pixels for a given theta the closer r is to the lower limit. So "meteorites" with smaller r will occur with larger probability.

I believe this effect is negligeble.

EDIT 2: MBo's answer is better. Code:

theta = uniform(0, 2 * np.pi)
r = np.sqrt(uniform(2.0 ** 2, 3.5 ** 2)) # draw from sqrt distribution
x = r * np.cos(theta)
y = r * np.sin(theta)
Nimrod Morag
  • 938
  • 9
  • 20
2

I would also rather run through a loop that picks a random angle and a random distance within your ring range. Then calculate the coords from that.

But in your code the first problem is see is that should write:

x[i],y[i] = meteorites()

instead of

x[i] = meteorites()[0] 
y[i] = meteorites()[1]

In your example, you're called meteorites() twice resulting in the x and y two different meteorites.

Edward van Kuik
  • 1,357
  • 1
  • 9
  • 9
1

You could try the following to generate 1000 samples using numpy:

import numpy 
n = 1000
phi = numpy.random.uniform(0, 2*numpy.pi, n)
r = numpy.random.uniform(2, 3.5, n)

Then x, y coordinates can be constructed as follows using the transformation from radial to cartesian coordinates:

x = r * numpy.cos(phi)
y = r * numpy.sin(phi)

This shows the power of numpy, as x and y are now arrays without needing to iterate over n.

keepitwiel
  • 122
  • 7