Generate N random uniformly distributed points in a specific area

34 views (last 30 days)
I want to generate N random uniformly distributed points in the area between a circle of radius 1 and a square of side 2, both centered at origin like in the picture above.
How can I do that?

Accepted Answer

ytzhak goussha
ytzhak goussha on 4 May 2019
Here's a simple way (edited)
Randomly distributed:
N=100; %number of points
n=1; %Iterator
x_range=[-1 1]; %Range of width
mid_point=[mean(x_range),mean(x_range)]; %Center of box
radius=1; %Radius of circle
point_arr=zeros(N,2); %This will hold the point
while n<=N
temp_xy = (x_range(2)-x_range(1)).*rand(1,2) + x_range(1); %Generate 2 random numbers x and y
d = pdist([temp_xy;mid_point],'euclidean'); %Find distance between the point generated and the middle
if d>radius %If the distance is smaller than the radius, discard it, else accept it
point_arr(n,:)=temp_xy;
n=n+1;
end
end
scatter (point_arr(:,1),point_arr(:,2))

More Answers (2)

ytzhak goussha
ytzhak goussha on 4 May 2019
Edited: ytzhak goussha on 4 May 2019
Here's a different solution:
This one is evenly distributed and spaced. however it created 108 points or 92 point
x_range=[-1 1];
x=x_range(2)-x_range(1);
r=1;
N=100;
mid_point=[mean(x_range),mean(x_range)];
Ssq=x^2;
Sc=pi*1^2;
Sd=Ssq/Sc;
tot=100*Sd;
tot_x=round(sqrt(tot));
check=0;
while check<N
x_p=linspace(x_range(1),x_range(2),tot_x);
[X,Y] = meshgrid(x_p,x_p);
points_to_del=[];
for i1=1:tot_x
for j1=1:tot_x
temp_xy=[X(i1,j1),Y(i1,j1)];
d=pdist([temp_xy;mid_point],'euclidean');
if d<=r
points_to_del=[points_to_del;[i1,j1]];
end
end
end
for i1=1:size(points_to_del,1)
X(points_to_del(i1,1),points_to_del(i1,2))=nan;
Y(points_to_del(i1,1),points_to_del(i1,2))=nan;
end
X(isnan(X))=[];
Y(isnan(Y))=[];
check=size(X,2);
tot_x=tot_x+1;
end
scatter(X(:),Y(:))
xlim(x_range);
ylim(x_range);

John D'Errico
John D'Errico on 4 May 2019
Edited: John D'Errico on 4 May 2019
+1 to ytzhak goussha for the correct solution of course. A rejection method is really the logical way to solve it. What I'll point out are some issues that you can consider, and some alternative methods.
The rejection fraction here is moderately large.
(4 - pi)/4*100
ans =
21.4601836602552
So 78.5% of the points will be rejected. That is not massively bad. You might want to do it all in one large, essentially unlooped approach. So instead generate more points than you need, but do it all at once.
nreq = 1000;
% an extra 20% in case we reject too many.
rejectfrac = (4 - pi)/4;
oversample = 0.2;
xy = [];
nxy = 0;
center = [0, 0];
rad = 1;
while nxy < nreq
% assume we have a circle incribed in a square. So the square has edge length of 2*rad.
nsample = ceil((1 + oversample)*(nreq - nxy)/rejectfrac);
% this mext line uses a feature found in R2016b or later.
xypoints = (rand(nsample,2)*2 - 1)*rad + center;
% delete points inside the circle.
xypoints(sum((xypoints - center).^2,2) <= rad^2,:) = [];
% were there enough points
xy = [xy;xypoints];
nxy = size(xy,1);
if nxy > nreq
xy = xy(1:nreq,:);
end
end
plot(xy(:,1),xy(:,2),'.')
axis equal
grid on
yline(0);
xline(0);
But, still we rejected 78.5% of the points. Could I have done better? Perhaps. One idea is to generate points initially that are less likely to have been rejected. So don't bother to generate points that are known to be fully inside the circle. I can think of several such ways off the top of my head. I might choose a smaller polygonal region that describes the region(s) of interest, then generate points randomly and uniformly inside the indicated polygon.
However, the extra work might be more effort to code than the cost of the CPU cycles to reject too many points. Computers are fast, and programmer time is costly. Code that is more complex to write is also more complex to debug and to maintain.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!