Random points without rejection
May 10, 2025 at 9:20 AM by Dr. Drang
A few days ago, John D. Cook wrote a post about generating random points on the surface of a sphere. His approach was:
- Generate three independent uniformly distributed random variables in the range from to , where is the radius of the sphere. These will represent a point in space.
- Calculate such that
- If , which means the point is outside the sphere, reject the point and go back to Step 1.
Otherwise, return the point
which is a projection of the point out onto the surface of the sphere.
This works, of course, but you reject nearly half of the points you generate. All the points are in a cube of volume , but you’re keeping only those in a sphere of volume
I wanted to come up with a way to generate points directly on the sphere without any rejections or projections. To start, I decided to reduce the problem by one dimension and think about circles.
Generating random points around the circumference of a circle is pretty easy. Just generate a series of uniformly distributed random variables from to and treat them as angles, . Then the points will be distributed around the circumference. Like this:
This is 500 points, and there are some gaps and clumps because that’s the nature of random number generation. But there’s no bias to the distribution of points.
Now let’s generate points within the circle. A naive way to go about this would be to generate values uniformly distributed between and for the angle, as before, and to generate values uniformly distributed between and for the radius. But if we do that, we’ll get a point cloud of pairs that looks like this:
Definitely biased toward the center of the circle.
Why did this happen? Consider this polar grid:
If we generate uniformly distributed and , all the sectors in the grid will have about the same number of points. But because the sectors near the center are smaller, the points will be denser in those sectors and sparser in the sectors further out. Which is exactly what we see in the naive point cloud.
The size of the sectors grows linearly with , so we need to generate the using a distribution that grows linearly with . That would be a triangular distribution with a probability density function that looks like this (for ):
The peak value of the PDF is because the area under the PDF must be one.
Many math software packages have functions for generating random variables with a triangular distribution. Using that distribution instead of a uniform distribution for generating the will give us a set of that’s uniformly spread over the interior area of the circle:
How did I know to use a triangular distribution for ? The polar grid certainly shows that we need to generate more large values than small ones, but how did I know that a PDF with linear growth was the one to use? There are, I suppose, many ways to think about the problem, but I thought of it in terms of the Jacobian, a topic you typically learn about in a multivariable calculus class.
We know that in Cartesian coordinates, a differential area element is the product of the two differential linear elements:
But in polar coordinates, the expression for differential area is a little more complicated:
We can get this expression by considering the equations that convert between polar and Cartesian coordinates:
The differentials are
which can also be expressed in matrix form:
The determinant of the matrix (called the Jacobian) represents the ratio of the differential areas expressed in the two coordinate systems:
That in the expression tells us that we need to scale linearly with . Hence the triangular PDF with the shape given above.
OK, now it’s time to move from circles to spheres. Here’s our coordinate system:
There are different ways to define the two angles. I’ve chosen a system where is like longitude and is like latitude. The relationship between the Cartesian and spherical coordinates is this:
Which means the differential relationship is
The determinant has lots of trig cancelation, and we end up with
as the relationship between differential volumes in the two coordinate systems.
Of course, we’re interested in surface area, not volume, but this analysis is still helpful. To get the differential surface area in spherical coordinates, we drop the and set :
So to get points on a sphere’s surface that are uniformly distributed over the area, is drawn from a uniform distribution from to and is drawn from a distribution whose PDF follows a cosine curve from to :
To make this a legitimate PDF, the cosine has been scaled so the integral under the curve is one:
Unfortunately, this kind of distribution isn’t common. I don’t know of any math packages that include functions for a cosine distribution. No matter. There’s a procedure for generating random numbers from any distribution. We start by integrating the PDF to get the cumulative distribution function:
The constant of integration, , is determined by noting that at . That means and
which looks like this:
Here’s the trick: CDFs always have a range from zero to one. So if we generate random numbers that are uniformly distributed from zero to one—which is the most common random number generator—and run them through the inverse of the CDF, we’ll get random numbers with the distribution of interest. In this case,
Here’s the Mathematica code for generating a list of 5000 random points on the surface of a sphere of radius 5:
n = 5000;
R = 5;
\[Phi] = ArcSin[2*RandomReal[1, n] - 1];
\[Theta] = RandomReal[{-Pi, Pi}, n];
And here’s the conversion to Cartesian coordinates:
x = R Cos[\[Phi]] Cos[\[Theta]] ;
y = R Cos[\[Phi]] Sin[\[Theta]];
z = R Sin[\[Phi]];
It actually looks nicer in a Mathematica notebook because the Greek letters are rendered properly:
I did this in Mathematica to give me practice. Python code would be similar using NumPy.
Here’s the plot of the randomly generated points:
It may not be obvious that the points are on the sphere’s surface. Here’s a video of the point cloud rotating slowly. If you look carefully, you’ll see clumps of points moving from the front surface to the rear and vice versa. There are no points within the sphere to block your view.
Is this a better approach than Cook’s rejection/projection method? I guess that depends on your definition of better. To me, this is a cleaner approach because we’re generating the points on the sphere directly. But random number generation is an inherently numerical procedure, which means efficiency can’t be overlooked. Even though Cook is generating three random numbers for every two I generate—and then thows away nearly half of them—I suspect the simpler numerics of his method will win out. My technique uses a handful of trig functions, whereas his most complicated function is the square root.
But it was a fun problem to think about, and it’s the first time I’ve animated a graph in Mathematica.