88 views (last 30 days)

Show older comments

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?

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))

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
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.

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

Start Hunting!