Reputation: 111
Reference Image:
The red circle has a radius of r
The blue square has a side length of s
The goal is to generate a random point within the blue zone but not in the red zone I already have a solution, but it involves trial and error and that is not my prefered method, is there some sort of mathematical solution to this problem?
Here is my method
Let rx and ry be the random variables
rx = random number between 0 and s
ry = random number between 0 and s
while (distance(rx,ry,0,0) < r)
{
rx = random number between 0 and s
ry = random number between 0 and s
}
return rx ry;
Upvotes: 5
Views: 4034
Reputation: 336
Ok, why to reject both rx and ry if they are not the ones? Any rx in [-s,s] can be our choice. Therefore;
rx = -s + 2*s*rand();
if(rx<-R || rx>R) % if rx is outside the critical area, you are free on ry
ry = -s + 2*s*rand();
else % if rx is critical, we are restricted about choosing ry not within [-sqrt(R^2-rx^2),sqrt(R^2-rx^2)]
{
rand_inst = rand();
if(rand_inst<0.5) % for the first possible interval [-s, -sqrt(R^2-rx^2)]
ry = -s + 2*(s-sqrt(R^2-rx^2))*rand_inst;
else % for the second possible interval [sqrt(R^2-rx^2), s]
ry = (sqrt(R^2-rx^2) - (s-sqrt(R^2-rx^2)) ) + 2*(s-sqrt(R^2-rx^2))*rand_inst;
}
Upvotes: -1
Reputation: 5821
I'd suggest you stay with the idea you have now, which is formally called rejection sampling and is a relatively common technique for sampling from arbitrary probability distribution using only a uniform random number generator.
The problem of slowdown with an increased number of dimensions is simply unavoidable--this is often called the curse of dimensionality.
While some people have suggested "pushing" points that end up in the circle to points in the acceptable/blue region, it is hard to do this without sacrificing a completely uniform probability distribution. (For example, I could push all points in the circle to the nearest point on the circle's boundary, but this would make the distribution non-uniform as the circle's boundary would be sampled much more often.)
To make your code as efficient as possible, you should be calculating (distance(rx,ry,0,0)
without calling any functions if possible, and using primative operators likes +
and *
, rather than any library functions like exp(x,2)
. In other words, use x*x + y*y < r2
directly in the if
statement (with r2 = r*r;
predefined somewhere).
Upvotes: 6
Reputation: 1498
I'll not enter on the discussion math vs. programming. For me this question has both. This is my take.
You can try to map a point generated in the [0,1] interval to the blue area. This should give a normal distribution if both shapes were concentric circles. When you have a square, the points will be sparser the closer you get to the diagonal.
The idea is to work in polar coordinates:
(x,y)
position (0,0)
.a
in the [0,1]
interval.w = 2 * pi * a
. This will be your polar angle. The equation y = x * tan(w)
defines a straight line going through the center of the picture.r
and smaller than the point where the line defined in step 3 intercepts the square.w
has values from pi/4
to 3pi/4
; the "left side" is for w
ranging from 3pi/4
to 5pi/4
; the "bottom side" has w
between 5pi/4
and 7pi/4
; and the "right" side has w
from 7pi/4
to 2pi
and from 0
to pi/4
.y = s / 2
. The interception of the line defined in 3 with the top side is then at position s/2 = xi tan(w)
.(xi, s/2)
defines the largest value for rho: R = sqrt(xi^2 + (s/2)^2)
b
in the [0,1]
intervalrho = r + b * R
X = rho * cos(w)
and Y = rho * sin(w)
which should be in the blue area.Note that from step 5 on, you have to check which side of the square is the one you should take into account (which value of w
).
As mentioned above, the problem is that the diagonals are longer than the directions along the main axes producing a sparser distribution. It is up to you to see if that is a problem or not. Note that the mapping does not pile up points close to the circle edge. Also ask yourself if the checks on directions are practical or not (I am assuming that the square and circle shapes are crude approximations to what you really want).
Upvotes: 2
Reputation: 15693
I can see two possibilities. If the circle is a lot smaller than the square, then generate a point inside the square and check if it is inside or outside the circle. If the circle is a large proportion of the square, then find the largest possible radius of the corner of the square furthest from the centre of the circle. Generate a radius that is outside the given circle, but not greater than the distance to the furthest corner. Also generate a theta. If the resulting (r, theta) point is inside the square, then accept it. The method of construction ensures that it is outside the circle.
Upvotes: 3
Reputation: 1717
You could generate two numbers on the range [0..1], and then define a function that maps x for a certain y to a point outside of the circle.
Upvotes: 0
Reputation: 506
Still, as @Marc stated, technically it's math, not programming. However the answer is to generate a random point and then determine if the length of the line drawn from the center of the square to that point is less than or equal to the radius of the circle. If so, reject it and generate another.
Upvotes: 0