14

I am wondering how i could generate random numbers that appear in a circular distribution.

I am able to generate random points in a rectangular distribution such that the points are generated within the square of (0 <= x < 1000, 0 <= y < 1000):

How would i go upon to generate the points within a circle such that:

(x−500)^2 + (y−500)^2 < 250000 ?

Jack8246
  • 153
  • 1
  • 3
  • 10
  • 2
    Have to be careful about how the calculation is done, however. If the intention is to have uniformly-distributed random (x,y) values within the circle, then many of the potential ways to do the calculation won't give that outcome. – Simon Jun 01 '15 at 00:28
  • 1
    http://mathworld.wolfram.com/DiskPointPicking.html – Veedrac Jun 01 '15 at 00:39
  • 2
    Read [this](http://stats.stackexchange.com/questions/120527/how-to-generate-random-points-uniformly-distributed-in-a-circle). – vsoftco Jun 01 '15 at 00:44
  • Possible duplicate of [Generate a random point within a circle (uniformly)](https://stackoverflow.com/questions/5837572/generate-a-random-point-within-a-circle-uniformly) – aioobe Sep 06 '19 at 21:01

7 Answers7

16
import random
import math

# radius of the circle
circle_r = 10
# center of the circle (x, y)
circle_x = 5
circle_y = 7

# random angle
alpha = 2 * math.pi * random.random()
# random radius
r = circle_r * math.sqrt(random.random())
# calculating coordinates
x = r * math.cos(alpha) + circle_x
y = r * math.sin(alpha) + circle_y

print("Random point", (x, y))

In your example circle_x is 500 as circle_y is. circle_r is 500.

Another version of calculating radius to get uniformly distributed points, based on this answer

u = random.random() + random.random()
r = circle_r * (2 - u if u > 1 else u)
Ulf Aslak
  • 7,876
  • 4
  • 34
  • 56
f43d65
  • 2,264
  • 11
  • 15
  • 8
    I don't know if OP needs it, but this will *not* be a uniform distribution. – andars Jun 01 '15 at 00:30
  • 2
    This approach produces non-uniformly distributed values of (x,y) - they will be more concentrated in the center than at the edge of the circle. If that is the desired result, that is well and good, but if a uniform distribution is required, this will not provide it. – Simon Jun 01 '15 at 00:30
  • 1
    This is a perfect opportunity to link to one of the greatest problems in probability theory: the [Bertrand paradox](https://en.wikipedia.org/wiki/Bertrand_paradox_(probability)). – naught101 May 25 '17 at 03:16
  • 2
    EDIT: I suggested taking the square root to the `random.random()` part of `r`. That way it will be a uniform distribution. – Ulf Aslak Jul 31 '18 at 15:51
8

FIRST ANSWER: An easy solution would be to do a check to see if the result satisfies your equation before proceeding.

Generate x, y (there are ways to randomize into a select range)

Check if ((x−500)^2 + (y−500)^2 < 250000) is true if not, regenerate.

The only downside would be inefficiency.

SECOND ANSWER:

OR, you could do something similar to riemann sums like for approximating integrals. Approximate your circle by dividing it up into many rectangles. (the more rectangles, the more accurate), and use your rectangle algorithm for each rectangle within your circle.

Fuad
  • 1,419
  • 1
  • 16
  • 31
  • 3
    The first answer has the dual merits of being simple and correctly providing a uniform distribution of (x,y) values. The inefficiency is relatively small because only a fraction 1-(0.25*PI) of the pairs will be rejected. – Simon Jun 01 '15 at 00:33
  • 2
    It's actually quite likely that this will be *faster* than doing it "properly" if using a fast random number generator since it avoids trigonometry, roots, large numbers and uses only one call to `random` on average. – Veedrac Jun 01 '15 at 00:44
5

What you need is to sample from (polar form):

r, theta = [math.sqrt(random.randint(0,500))*math.sqrt(500), 2*math.pi*random.random()]

You can then transform r and theta back to cartesian coordinates x and y via

x = 500 + r * math.cos(theta) 
y = 500 + r * math.sin(theta)

Related (although not Python), but gives the idea.

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • As noted on other answers, this won't result in a uniform distribution of points. – naught101 May 25 '17 at 03:18
  • @naught101 No, it will generate uniformly distributed points on the disc, note that I'm generating a uniform of the square root of the radius, not the radius itself (which indeed won't result in a uniform distribution over the disc). – vsoftco May 25 '17 at 03:24
  • Ah, true, I see what you're doing. I think it might be clearer if you just sampled from the unit circle, then multiplied and added afterwards, rather than sampling from an r=500 circle. – naught101 May 25 '17 at 05:34
2

You can use below the code and if want to learn more https://programming.guide/random-point-within-circle.html

import random
import math
circle_x = 500
circle_y = 500
a = random.randint(0,500) * 2 * math.pi
r = 1 * math.sqrt(random.randint(0,500))
x = r * math.cos(a) + circle_x
y = r * math.sin(a) + circle_y
Md Jewele Islam
  • 939
  • 8
  • 18
2

here's an example hope could help someone :).

randProba = lambda a: a/sum(a)
npoints = 5000 # points to chose from
r = 1 # radius of the circle

plt.figure(figsize=(5,5))
t = np.linspace(0, 2*np.pi, npoints, endpoint=False)
x = r * np.cos(t)
y = r * np.sin(t)
plt.scatter(x, y, c='0.8')

n = 2 # number of points to chose
t = np.linspace(0, 2*np.pi, npoints, endpoint=False)[np.random.choice(range(npoints), n, replace=False, p=randProba(np.random.random(npoints)))]
x = r * np.cos(t)
y = r * np.sin(t)

plt.scatter(x, y)

enter image description here

Walid Bousseta
  • 1,329
  • 2
  • 18
  • 33
1

You can use rejection sampling, generate a random point within the (2r)×(2r) square that covers the circle, repeat until get one point within the circle.

dlavila
  • 1,204
  • 11
  • 25
0

I would use polar coordinates:

r_squared, theta = [random.randint(0,250000), 2*math.pi*random.random()]

Then r is always less than or equal to the radius, and theta always between 0 and 2*pi radians.

Since r is not at the origin, you will always convert it to a vector centered at 500, 500, if I understand correctly

x = 500 + math.sqrt(r_squared)*math.cos(theta) y = 500 + math.sqrt(r_squared)*math.sin(theta)

Choose r_squared randomly because of this

fiacre
  • 1,150
  • 2
  • 9
  • 26
  • 3
    The distribution of (x,y) produced by this approach will not be uniform. – Simon Jun 01 '15 at 00:37
  • `x = 500 + r*math.cos(theta)` `y = 500 + r*math.sin(theta)` Yeah? – fiacre Jun 01 '15 at 00:44
  • @simon, I see the problem: Amend the code above to be: `r_sqared, theta = [random.randint(0,250000), 2*math.pi*random.random()]` Then apply a transform `x = 500 + math.sqrt(r_squared)*math.cos(theta)` `y = 500 + math.sqrt(r_squared)*math.sin(theta)` – fiacre Jun 01 '15 at 01:09