题目描述

✅ 478. 在圆内随机生成点

image-20260928224218140

题意分析

每次返回圆内的一个随机坐标,要求任意区域被选中的概率与它的面积成正比。可以先在以原点为圆心的圆内采样,再加上给定圆心的坐标完成平移。

解法:极坐标采样

核心思路

[!blue]

用极坐标表示点:方向角为 $\theta$,到圆心的距离为 $d$。所有方向对称,所以让 $\theta$ 在 $[0,2\pi)$ 上均匀分布;距离的分布则要由面积推导。

设圆半径为 $R$,距离圆心不超过 $r$ 的区域占总面积的比例为 $\pi r^2/(\pi R^2)=(r/R)^2$,因此必须满足 $P(d\le r)=(r/R)^2$。取均匀随机数 $u\in[0,1)$,令 $d=R\sqrt{u}$,就有 $P(d\le r)=P(u\le(r/R)^2)=(r/R)^2$。

方向角和距离分别使用独立抽样。这样每个圆环获得的概率与面积成正比,同一圆环内各个方向也均匀,得到的点便在整个圆内均匀分布。若直接令 $d=Ru$,相同宽度的内外圆环会获得相同概率,但内圈面积更小,点就会过度集中在圆心附近。

最后转换为直角坐标:x = xCenter + d * cos(theta)、y = yCenter + d * sin(theta)。平移只改变圆的位置,不改变分布。

解题步骤

  1. 构造时保存半径 radius 和圆心坐标。
  2. 每次调用分别取得两个均匀随机数,一个用于生成方向角,另一个作为面积比例。
  3. 用 radius * sqrt(u) 计算到圆心的距离。
  4. 用正弦、余弦计算相对坐标,再加上圆心并返回两个坐标。

代码实现

class Solution {
    // 角度可以在 [0, 2π) 上均匀采样,方向天然均匀。
    private final double radius;
    private final double xCenter;
    private final double yCenter;

    public Solution(double radius, double x_center, double y_center) {
        this.radius = radius;
        this.xCenter = x_center;
        this.yCenter = y_center;
    }

    public double[] randPoint() {
        java.util.concurrent.ThreadLocalRandom random =
                java.util.concurrent.ThreadLocalRandom.current();
        // 方向角与下面的面积比例使用两次独立抽样
        double angle = random.nextDouble(2.0 * Math.PI);
        // 均匀的是面积比例,半径需要开平方恢复
        double distance = radius * Math.sqrt(random.nextDouble());
        // 相对圆心的坐标转换后,再加上圆心偏移
        double x = xCenter + distance * Math.cos(angle);
        double y = yCenter + distance * Math.sin(angle);

        return new double[] {
            x,
            y
        };
    }
}
import (
    "math"
    "math/rand"
)

type Solution struct {
    // 角度可以在 [0, 2π) 上均匀采样,方向天然均匀。
    radius float64
    x      float64
    y      float64
}

func Constructor(radius float64, xCenter float64, yCenter float64) Solution {
    return Solution{radius: radius, x: xCenter, y: yCenter}
}

func (s *Solution) RandPoint() []float64 {
    // 方向角与下面的面积比例使用两次独立抽样
    angle := rand.Float64() * 2.0 * math.Pi
    // 均匀的是面积比例,半径需要开平方恢复
    distance := s.radius * math.Sqrt(rand.Float64())
    // 相对圆心的坐标转换后,再加上圆心偏移
    x := s.x + distance*math.Cos(angle)
    y := s.y + distance*math.Sin(angle)
    return []float64{
        x,
        y,
    }
}

复杂度分析

  • 时间复杂度:$O(1)$,固定次数的随机取样及数学运算。
  • 空间复杂度:$O(1)$,对象状态与返回坐标长度固定。

关键点总结

[!green]

  • 随机半径的平方与面积成正比。
  • 两个独立随机样本也可能恰好相等,独立不等于数值必须不同。

易错点总结

[!yellow]

  • 半径直接乘均匀随机数,会过度采样内圈。
  • 角度与距离复用同一个随机量,会让点集中在一条曲线上。
  • 漏加圆心偏移,会围绕错误的圆心采样。

相似题目

题目 难度 关联与区别
497. 非重叠矩形中的随机点 中等 同样要求几何区域内均匀采样,原题在离散矩形格点间按点数加权,本题在连续圆面积上均匀。
470. 用 Rand7() 实现 Rand10() 中等 拒绝采样的均匀性证明相同:保留区域中的每个原等概率位置仍等概率。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2022/72593011
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!