1. Introduction
In this tutorial, we’ll show how to sample a point from a uniform distribution defined over a circle.
We’ll explain how to derive the sampling distribution explicitly and how to avoid math using rejection sampling.
2. Problem Statement
In the polar coordinate system, we define a point by its distance to the origin
and the angle
from the polar axis:
Let be the circle’s radius. Since we want to sample uniformly from the circle, the sampling density
should have constant values for all
:
3. Incorrect Naive Solution
The first approach we might try is to sample uniformly from
and
from
:
algorithm NaiveSampling(R):
// INPUT
// R = the radius of the corcle
// OUTPUT
// theta, r = the polar coordinates of the sampled point
theta <- uniform(0, 2 * pi)
r <- uniform(0, R)
return theta, r
However, this doesn’t result in a uniform distribution. To show that, let’s generate 10,000 points from a unit circle ():
The area near the origin is more densely populated. Why?
Let’s observe that infinitely many concentric circles are inside our target circle. Since is uniform over
, all the angles are equally likely for any radius
. However, the concentric circles don’t have the same circumference, as the closer ones to the origin are smaller:
Informally, we’re covering all these concentric circles with the same number of points, but since the outer ones are larger, they have a lower sampling density, which is why this approach doesn’t work.
4. Correct Density for Radius
We must sample so that greater
is more likely to be sampled.
In particular, the probability of sampling a point from an area corresponding to the point’s distance to the origin must be proportional to that area, i.e., to the area
of the circle whose radius is
. In other words, the probability of sampling from a circle with radius
is the ratio of
and the total area
:
Intuitively, sampling from any two concentric circles (defined by their radii) should be equally likely, so the probabilities (also defined by the radius
) should follow a uniform distribution over
. More formally, since sampling from a circle with radius
means that the sampled point can be at any distance
from the origin, the probability
is a cumulative distribution function of a distribution over
. By the probability integral transform, it’s a uniform random variable over
.
From there, if is a random number from
, we get
by setting
and solving for
:
So, sampling stays the same as in the naive approach, but not for
:
algorithm CorrectExplicitSampling(R):
// INPUT
// R = the radius of the corcle
// OUTPUT
// theta, r = the polar coordinates of the sampled point
theta <- uniform(0, 2 * pi)
r <- sqrt(uniform(0, 1)) * R
return theta, r
Now, the density is truly uniform over our entire circle:
5. Less Math: Rejection Sampling
If we want to sample uniformly from any geometric shape, rejection sampling offers a simple and intuitive approach that avoids explicitly dealing with probabilities and densities.
The first step is to place a shape from which we can easily sample uniform points over our target shape. In most cases, this reference shape will be a square. So, we inscribe our target circle with radius into a square whose sides have length
:
Then, we sample a point from the square. If the sampled point is in the circle, we return it (after transforming from the Cartesian to polar coordinates). Otherwise, we repeat the sampling:
algorithm RejectionSampling(x0, y0, R):
// INPUT
// x0, y0 = the coordinates of the origin of the circle
// R = the radius of the circle
// OUTPUT
// theta, r = the polar coordinates of a point inside the circle
R2 <- R * R
while true:
// uniformly sample a point in the square
x <- uniform(x0 - R, x0 + R)
y <- uniform(y0 - R, y0 + R)
// compute its squared distance to the center
r2 <- (x0 - x)**2 + (y0-y)**2
// check if the point is in the circle
if r2 <= R2:
// convert to polar coordinates
theta <- atan2(y, x)
r <- sqrt(r2)
return theta, r
For efficiency, we compute the polar coordinates only if the point is in the circle and compare to
instead of
to
to avoid calculating the square root if the point isn’t in the circle.
Why does this approach work? Since we cover our circle with a square of constant density, the density over the circle will also be uniform:
A drawback of rejection sampling is that some points fall outside the circle. The probability of sampling such a point is:
However, this approach is fast and straightforward to implement.
6. Conclusion
In this article, we showed how to randomly generate a point from a circle.
We can derive the sampling distribution mathematically and draw from it explicitly or use rejection sampling. Both approaches work, but the latter involves less math.