原题链接
题目描述:
给定圆的半径和圆心的位置,实现函数 randPoint ,在圆中产生均匀随机点。
实现 Solution 类:
Solution(double radius, double x_center, double y_center) 用圆的半径 radius 和圆心的位置 (x_center, y_center) 初始化对象
randPoint() 返回圆内的一个随机点。圆周上的一点被认为在圆内。答案作为数组返回 [x, y] 。
方法一:拒绝采样
思路与算法
拒绝采样的意思是说:我们在一个更大的范围内生成随机数,并拒绝掉那些不在题目给定范围内的随机数,此时保留下来的随机数都是在范围内的。为了在一个半径为 R 的圆中生成均匀随机点,我们可以使用一个边长为 2R的正方形覆盖住圆,并在正方形内生成均匀随机点,此时就只需要对于横坐标和纵坐标分别生成一个随机数即可。若该点落在圆内,我们就返回这个点,否则我们拒绝这个点,重新生成,直到新的随机点落在圆内。
细节
由于正方形的面积为 (2R)^2 = 4R^2,圆的面积为 pi R^2,因此在正方形中随机生成的点,落在圆内的概率为
在正方形中生成点时(正方形中心的坐标简记为原点),如果我们在 [-R, R)的范围内生成随机数,那么是无法生成到横坐标或纵坐标恰好为 R 的点,对应到圆上时,会有圆周上与正方形边相切的两个点无法随机到。我们可以在生成时稍微提高右边界(例如 2R + ϵ,其中 ϵ 是一个很小的常数,例如 10^{-7}
),或者直接忽略这两个点,因为它们的勒贝格测度为零。
using System;
namespace ConsoleApplication1
{
//获取圆内随机的一点 没一点概率相等
//采用拒绝采样方法 用一个长为2r的正方形包围圆 若点不在圆内 则继续随机
//
public class getCirclePoint
{
double _radius; //圆半径
double _xcenter;
double _ycenter;
Random random;
public getCirclePoint(double radius, double x_center, double y_center)
{
_radius = radius;
_xcenter = x_center;
_ycenter = y_center;
random= new Random();
}
public double[] RandPoint()
{
while (true)
{
double x = random.NextDouble() * 2 * _radius - _radius;
double y = random.NextDouble() * 2 * _radius - _radius;
if ((x - _xcenter) * (x - _xcenter) + (y - _ycenter) * (y - _ycenter) < _radius * _radius)
{
return new double[] { x, y };
}
}
}
public double[] mathGetRandPoint()
{
double u = random.NextDouble();
double theta = random.NextDouble() * 2 * Math.PI;
double r = Math.Sqrt(u);
return new double[] { _xcenter + r * Math.Cos(theta) * _radius, _ycenter + r * Math.Sin(theta) * _radius };
}
}
}