在环形区域内生成随机数
我想生成一个在环形区域内的随机数,也就是说,我们有一个最大半径和一个最小半径。我试着这样做:
while True:
x=random.uniform(-maxR, maxR)
y=random.uniform(-maxR, maxR)
R=math.sqrt(x**2 + y**2)
if R <= maxRadius and R >= minRadius:
if x>= -maxRadius and x <= maxRadius and x<=-minRadius and x>= minRadius:
print "passed x"
if y>= -maxRadius and y <= maxRadius and y<=-minRadius and y>= minRadius:
break
但是这样做速度很慢。有没有办法给random.uniform
添加更多的限制条件,或者有没有其他的方法?
1 个回答
24
一般来说,你可以直接绘制正确的分布,或者使用拒绝采样的方法。
直接绘制的方法
- 在区间 [0,2pi) 上均匀地生成一个角度 theta:
theta = random.uniform(0,2*pi)
从幂律分布 r^1中生成 r。
与在圆上进行这项操作相比,唯一的复杂之处在于你的概率密度函数(PDF)是从 [r_min,r_max] 而不是 [0,r_max]。这导致了:
累积分布函数(CDF) = A \int_{r_min}^{r} r' dr' = A (r^2 - r_min^2)/2
这里的 A 是归一化常数。
A = 2/(r_max*r_max - r_min*r_min)
这意味着:
r = sqrt(2*random.uniform(0,1)/A + r_min*r_min)
你可以稍微简化一下。
然后通过常规的极坐标转换计算 (x,y):
x = r * cos(theta)
y = r * sin(theta)
这种积分 PDF、归一化 CDF 和反转的方法是通用的,有时被称为“采样的基本定理”。
拒绝采样
在一个足够大的框中绘制 (x,y),这个框要能包含环形区域,然后拒绝所有那些 `r = sqrt(xx + yy)` 超过 r_max 或小于 r_min 的情况。
如果中间的空洞很小,这种方法效率还算合理;但如果空洞很大,那就非常低效了。