[随机函数、拒绝采样] 478. 在圆内随机生成点

171 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

每日刷题 2022.06.11

题目

  • 给定圆的半径和圆心的位置,实现函数 randPoint ,在圆中产生均匀随机点。
  • 实现 Solution 类:
    • Solution(double radius, double x_center, double y_center) 用圆的半径 radius 和圆心的位置 (x_center, y_center) 初始化对象
    • randPoint() 返回圆内的一个随机点。圆周上的一点被认为在圆内。答案作为数组返回 [x, y] 。

示例

输入: 
["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]

提示

  • 0 < radius <= 10^8
  • -10^7 <= x_center, y_center <= 10^7
  • randPoint 最多被调用 3 * 10^4 次

解题思路

相似的题目

  • 黑名单中的随机数
  • 也是同样的需要需要对于要求下的所有点都是同样的概率被取到,更巧妙的在于将符合要求的点放在一个连续的区间内,这样再使用随机函数,就可以使其都是相同的概率被取到。

重点

  • Math.random()函数的理解:Math.random() * ?乘以的这个数,扩大的是整个区间的长度,也就是右边界的值。而Math.random() + ?加上的这个数,是整体移动区间的左边界和右边界,区间的长度是不会改变的。
    • 也可以将乘法理解成:扩大区间的长度
    • 加法理解成:整体移动区间
  • 位移量的理解

分析题意

  • 本题会给出半径和圆心的位置,需要我们在圆中随机的取点。
  • 可以想到在这个圆的外面接一个正方形,也就是限制🚫x、y的取值范围,然后再去判断当前获得的这个点是否在圆上。如果在圆上的话,就可以直接返回🔙。
    • 判断当前的点是否在圆上的办法:随机点到圆心的距离,是否小于等于半径。
    • 两点之间的距离公式:dir * dir = x * x + y * y
  • 最后这种做法超时了。

优化

  • 可以将区间计算出来,只将区间的长度固定下来,偏移量最后再添加上去。
  • 也就是先计算出x、y的裸值,再添加偏移量。
    • 裸值:计算范围大小Math.random() * this.r * 2 - this.r
    • 偏移量:x + this.xc, y + this.yc

AC代码

/**
 * @param {number} radius
 * @param {number} x_center
 * @param {number} y_center
 */
var Solution = function(radius, x_center, y_center) {
  // 判断x的范围、y的范围
  // 点到点之间的距离
  this.r = radius;
  this.xc = x_center;
  this.yc = y_center;
  // 查找个两个的范围,从圆心到四边封起来
  this.r2 = this.r * this.r;
};

/**
 * @return {number[]}
 */
Solution.prototype.randPoint = function() {
  // 随机取一个,在正方型的范围内
  // 因为是一个正方形,因此可能不在圆内
  let flag = false, res = [];
  // 固定的比较
  while(!flag) {
    // 判断是否在圆内
    // res = this.rand();
    // 计算距离
    let x = Math.random() * this.r * 2 - this.r;
    let y = Math.random() * this.r * 2 - this.r;
    let dir = x * x + y * y;
    if(dir <= this.r2) {
      res = [x + this.xc, y + this.yc];
      flag = true;
    }
  }
  return res;
};

/**
 * Your Solution object will be instantiated and called as such:
 * var obj = new Solution(radius, x_center, y_center)
 * var param_1 = obj.randPoint()
 */

总结

  • 偏移的变量可以最后再添加上。