持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
题目链接:478. 在圆内随机生成点
题目描述
给定圆的半径和圆心的位置,实现函数 randPoint ,在圆中产生均匀随机点。
实现 Solution 类:
Solution(double radius, double x_center, double y_center)用圆的半径radius和圆心的位置(x_center, y_center)初始化对象randPoint()返回圆内的一个随机点。圆周上的一点被认为在圆内。答案作为数组返回[x, y]。
提示:
randPoint最多被调用 次
示例 1:
输入:
["Solution","randPoint","randPoint","randPoint"]
[[1.0, 0.0, 0.0], [], [], []]
输出: [null, [-0.02493, -0.38077], [0.82314, 0.38945], [0.36572, 0.17248]]
解释:
Solution solution = new Solution(1.0, 0.0, 0.0);
solution.randPoint ();//返回[-0.02493,-0.38077]
solution.randPoint ();//返回[0.82314,0.38945]
solution.randPoint ();//返回[0.36572,0.17248]
整理题意
题目给定圆心坐标和圆的半径长度,要求每次 随机 返回一个坐标,返回的坐标要求在圆内,且圆周上的一点也被认为在圆内。
解题思路分析
观察题目描述得知该题与随机性有关,有很大可能会使用到随机函数 rand() 以及初始化随机函数的 srand((unsigned)time(NULL)) 。
首先观察题目数据范围,根据题目输入数据类型和返回类型可知,输入和输出的坐标值类型是浮点数,且根据题目示例可知是需要保留小数点后 5 位,数据范围在 以内,可以使用 double 进行存储。
- 我们可以先随机选择角度
[0, 360],再随机长度[0, r],从而就可以确定一个随机点在圆上。 - 但是考虑到按照以上操作求得的随机坐标比较麻烦,我们可以换一种方法,扩大采样范围,再根据采样坐标是否在圆上来拒绝采样。
拒绝采样的意思是说:我们在一个更大的范围内生成随机数,并拒绝掉那些不在题目给定范围内的随机数,此时保留下来的随机数都是在范围内的。
- 想要使得采样点在半径为
r的圆内,我们可以使用一个边长为2r的正方形覆盖住圆,并在正方形内生成均匀随机点,这样就可以只对于横、纵坐标分别生成一个范围在[-r, r]内的随机数即可。 - 不断随机选取该范围内的点,直至点落在圆内。通过计算圆和正方形的面积比为 ,也就是随机选取点落在圆上的概率约为 ,期望的生成次数为 ,也就是平均随机选取 次即可落在圆上。
具体实现
- 首先我们把给定的圆心坐标看成偏移量,最后进行操作即可,首先以
(0, 0)作为圆心,r为半径进行随机取点操作。随机点的横、纵坐标rx和ry都在[-r, r]上随机选取。 - 判断如果当前坐标距离
(0, 0)小于等于r,表示随机点在圆内,对rx和ry进行偏移后输出即可,否则重复随机选取。
- 浮点数的生成:采用随机生成
[0, 1]之间的浮点数,表示百分比,利用百分比乘以需要的区间长度,再加上区间左端点即可。- 需要注意的是,该题需要一定常数优化,否则会
TLE超时,在随机取点以及相关计算操作时需要注意优化,避免卡常。
复杂度分析
- 时间复杂度:期望时间复杂度为 。
- 空间复杂度:。
代码实现
class Solution {
private:
double r, x, y, r2;
public:
Solution(double radius, double x_center, double y_center) {
//初始化随机函数
srand((unsigned)time(NULL));
r = radius;
r2 = r * r;
x = x_center;
y = y_center;
}
vector<double> randPoint() {
double rx, ry;
//直到输出答案为止
while(true){
//使得随机rx 和随机ry 在 [-r, r]之间
rx = -r + (double)(rand() / (double)RAND_MAX) * 2 * r;
ry = -r + (double)(rand() / (double)RAND_MAX) * 2 * r;
//如果在以(0,0)为圆心r为半径的圆内就套用到点(x, y)上即可
if(rx * rx + ry * ry <= r2) return {x + rx, y + ry};
}
return {0, 0};
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(radius, x_center, y_center);
* vector<double> param_1 = obj->randPoint();
*/
总结
- 需要注意 随机浮点数 是如何生成,
(rand() / (double)RAND_MAX) * (r - l) + l,表示选取随机百分比长度区间,加上左区间即可。 - 拒绝采样 的思想和用法,扩大采样范围使得采样更为方便,同时选取符合要求的样本,其目的是为了方便采样操作,同时保证采样概率不变。
- 该题还需要注意题目卡常的优化操作。
- 测试结果:
结束语
生活千种,人生百态,你怎样看待世界,世界就会怎样对待你。心有阳光的人,不但能给身边的人带去温暖,自己也会被这阳光滋养着。愿你带着一颗赤诚的心,朝着明亮的地方,用力生长。