Uniformly Sample Points in a Disk
twitter.com
twitter.com
edit: The video is about 'Bertrand's Paradox' that another post references.
I have very often found myself wishing for an extensive reference table of such F functions when coding, as in:
- draw one or many samples from the language's PRNG
- apply F to it
- get a random number that "does what I want"
Unfortunately: 1. The math for F, while not hard in theory, rarely yields closed form solutions (you've got to, IIRC, find Distro^-1 and integrate it, or something like that, neither steps being easy for some distros).
2. There are very interesting "methods" to produce a similar results (such as the box-müller transform [1]), even if they involve rejection sampling.
What I've never seen though is an overall theoretical + practical treatment of the problem: how do I get the distribution I want by applying a function or maybe an algorithm to the output of a uniform PRNG).If anyone is aware of such work, would love to learn about it.
Keenan Crane's solution is really neat in that regard, btw.
[1] https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
I quite like the normalized Gaussian method.
Sample from the triangle that has (0,0), (1,0) and (cos 2pi/N, sin 2pi/N) as its vertices. Then uniformly pick an integer M from 0 to (N-1) and [by table look-up of sin/cos] rotate this point by the angle M*2pi/N. Even for modest values like N=128, 256 this is very close to a circle (around .1% error).
However, I'm not familiar enough with GPU compute to know if this maps well onto the available operations. It seems like a table look-up is a lot like a texture look-up so I'm tempted to assert it should be fast.
I think the only citation there is of a philosophy guy, which is a field well-known for raising fundamentally irrelevant points.
i would assume someone didnt even know what uniform meant if they did that.
and graphics people are so insane about perf, they arent going to just start doing sqrt over and over. they are going to rejection sample anyway because it just 2 fmas
Assmuning this is a graphics operation running on a GPU. Taking the sqrt is pretty optimized, and having a branch for every point to be sampled is not fun.
What costs more, generating a random number and maybe a second or a random number and a sin/cos? This sounds very platform dependent.
Note that when generating random points in the 4-ball (i.e. a quaternion)? The ball takes up a much smaller percentage of the space in the hypercube compared to the disk in a square, so rejections sampling can take many iterations.
You’re adding a very unpredictable loop branch and a bunch of data dependencies doing that. For GPUs (and maybe even CPUs), the sqrt way is almost certainly faster.
I want to get a random point in a circle.
What parameters make up a point in a circle?
Radius and angle can be one, so let's randomise those.
This is something that's done a lot in game development.
Btw, here's a way to sample a uniform random point on the edge of a circle without trigonometric functions:
Sample x and y coordinates from a standard normal distribution. Then project the result on the circle, by normalising the distance.
(You can approximate normal distributions fairly easily. Or your favourite computing environment might have specialised optimized machinery built-in.)
I wonder if that's uniform...
I expect that you would have more points mapped to the corners.
This says otherwise
var a = Math.max(Math.random(), Math.random());
var b = Math.sqrt(Math.random());I came across that one in the other direction, when trying to work out how to make weighted rendezvous hashing work. See https://en.wikipedia.org/wiki/Rendezvous_hashing
First, the cost is 2 FMAs * the cost of generating 2 random numbers * the number of rejection sampling iterations. On average, you reject (4-pi)/4 ~= 0.25 of the time. However, if you're running on a GPU in a warp (or the equivalent) of 32 threads, then you pay the cost of the maximum number of rejections over all the threads.
The bigger issue is that a direct mapping from [0,1]^2 to the disk, as is described in this tweet, if you have well-distributed uniform samples in [0,1]^2, you get well-distributed samples on the disk. Thus, stratified or low discrepancy samples in [0,1]^2 end up being well distributed on the disk. In turn, this generally gives benefits in terms of error with Monte Carlo integration of functions over the disk.
They don't realize/they lack intuition about the nonlinearity of going from rectangular to polar coordinates.
Things like this aren't necessarily immediately obvious, depending on one's academic background.
If you can re-jig the rest of your algorithm to consume the square of the radius (instead of the radius itself), you don't need to take a square-root here.
var radius1 = Math.max(Math.random(), Math.random());
var radius2 = Math.sqrt(Math.random());
Basically taking the maximum of two uniformly distributed random variables is the same as taking the square root of one of them in terms of their distributions.Whether taking the max of two variables is cheaper than the square-root of one depends on implementation details, I guess?
You can also re-use the random variable that the max 'tossed' away by scaling it.