我不确定这是否比拒绝抽样更好,但是这里有一个解决方案,用于均匀采样圆弧段(中心角度<= pi),涉及计算逆函数的数值。 (两个圆的交点的均匀采样可以由分段、扇形和三角形的采样组成-具体取决于如何将交点分割成更简单的图形。)
首先,我们需要知道如何生成具有给定分布F的随机值Z,即我们想要。
P(Z < x) = F(x) <=> (x = F^-1(y))
P(Z < F^-1(y)) = F(F^-1(y)) = y <=> (F is monotonous)
P(F(Z) < y) = y
这意味着:如果
Z
具有所请求的分布
F
,那么
F(Z)
是均匀分布的。反过来也是一样的:
Z = F^-1(Y),
其中Y
在区间[0,1]
上均匀分布,满足所需的分布。
如果F
的形式为
/ 0, x < a
F(x) = | (F0(x)-F0(a)) / (F0(b)-F0(a)), a <= x <= b
\ 1, b < x
然后,我们可以在区间
[F(a), F(b)]
中均匀选择一个
Y0
,并设置
Z = F0^-1(Y0)
。
我们选择用
(theta, r)
来参数化该线段,其中中心角度
theta
是从一个线段侧面测量的。当线段的中心角度为
alpha
时,与从线段开始的角度为
theta
的扇形相交的线段面积为(对于单位圆,
theta
在
[0, alpha/2]
).
F0_theta(theta) = 0.5*(theta - d*(s - d*tan(alpha/2-theta)))
其中 s = AB/2 = sin(alpha/2)
以及 d = dist(M,AB) = cos(alpha/2)
(圆心到线段的距离)。(本文不考虑alpha/2 <= theta <= alpha
的对称情况)。
我们需要一个随机变量theta
,满足 P(theta < x) = F_theta(x)
。无法通过符号计算得出 F_theta
的逆 - 必须通过优化算法(例如牛顿迭代)确定它。
一旦确定了theta
,我们需要在以下范围内随机生成半径r
:
[r_min, 1], r_min = d/cos(alpha/2-theta).
对于x
在[0, 1-r_min]
范围内,分布必须是
F0_r(x) = (x+r_min)^2 - r_min^2 = x^2 + 2*x*r_min.
这里可以以符号方式计算其反函数:
F0_r^-1(y) = -r_min + sqrt(r_min^2+y)
这里有一个Python的实现,用于概念证明:
from math import sin,cos,tan,sqrt
from scipy.optimize import newton
def segmentArea(alpha):
return 0.5*(alpha - sin(alpha))
def segmentAreaByAngle_gen(alpha):
alpha_2 = 0.5*alpha
s,d = sin(alpha_2),cos(alpha_2)
return lambda theta: 0.5*(theta - d*(s - d*tan(alpha_2-theta)))
def segmentAreaByAngleDeriv_gen(alpha):
alpha_2 = 0.5*alpha
d = cos(alpha_2)
return lambda theta: (lambda dr = d/cos(alpha_2-theta): 0.5*(1 - dr*dr))()
def segmentAreaByAngleInv_gen(alpha):
x0 = sqrt(0.5*segmentArea(alpha))
return lambda area: newton(lambda theta: segmentAreaByAngle_gen(alpha)(theta) - area, x0, segmentAreaByAngleDeriv_gen(alpha))
def randomPointInSegmentHalf(alpha):
FInv = segmentAreaByAngleInv_gen(alpha)
areaRandom = random.uniform(0,0.5*segmentArea(alpha))
thetaRandom = FInv(areaRandom)
alpha_2 = 0.5*alpha
d = cos(alpha_2)
rMin = d/cos(alpha_2-thetaRandom)
secAreaRandom = random.uniform(0, 1-rMin*rMin)
rRandom = sqrt(rMin*rMin + secAreaRandom)
return rRandom*cos(alpha_2-thetaRandom), rRandom*sin(alpha_2-thetaRandom)
这个可视化似乎验证了均匀分布(在中心角为pi/2
的线段的上半部分):
import matplotlib.pyplot as plot
segmentPoints = [randomPointInSegmentHalf(pi/2) for _ in range(500)]
plot.scatter(*zip(*segmentPoints))
plot.show()
![enter image description here](https://istack.dev59.com/woovr.webp)