我正在尝试使用numpy在球体表面上生成随机点。我查看了解释统一分发的帖子here。但是,需要关于如何仅在球体表面上生成点的想法。我有坐标(x,y,z)和每个球体的半径。
我不熟悉这个级别的数学,并试图理解蒙特卡罗模拟。
任何帮助将不胜感激。
谢谢, 帕林
答案 0 :(得分:24)
基于the last approach on this page,您可以简单地生成一个由三个标准正态分布的独立样本组成的向量,然后对该向量进行归一化,使其大小为1:
import numpy as np
def sample_spherical(npoints, ndim=3):
vec = np.random.randn(ndim, npoints)
vec /= np.linalg.norm(vec, axis=0)
return vec
例如:
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d
phi = np.linspace(0, np.pi, 20)
theta = np.linspace(0, 2 * np.pi, 40)
x = np.outer(np.sin(theta), np.cos(phi))
y = np.outer(np.sin(theta), np.sin(phi))
z = np.outer(np.cos(theta), np.ones_like(phi))
xi, yi, zi = sample_spherical(100)
fig, ax = plt.subplots(1, 1, subplot_kw={'projection':'3d', 'aspect':'equal'})
ax.plot_wireframe(x, y, z, color='k', rstride=1, cstride=1)
ax.scatter(xi, yi, zi, s=100, c='r', zorder=10)
同样的方法也可以推广在单位圆(ndim=2
)或高维单位超球面上拾取均匀分布的点。
答案 1 :(得分:7)
球体表面上的点可以使用两个球面坐标theta
和phi
,0 < theta < 2pi
和0 < phi < pi
来表示。
将公式转换为笛卡儿x, y, z
坐标:
x = r * cos(theta) * sin(phi)
y = r * sin(theta) * sin(phi)
z = r * cos(phi)
其中r
是球体的半径。
因此,程序可以在其范围内随机采样theta
和phi
,均匀分布,并从中生成笛卡尔坐标。
phi
选为phi = acos(a)
,其中-1 < a < 1
在均匀分布上选择。
对于Numpy代码,它与Sampling uniformly distributed random points inside a spherical volume中的代码相同,只是变量radius
具有固定值。
答案 2 :(得分:3)
在与@Soonts讨论后,我对答案中使用的三种方法的表现感到好奇:一种是生成随机角度,一种是使用正态分布坐标,一种是拒绝均匀分布的点。
这是我的尝试比较:
import numpy as np
def sample_trig(npoints):
theta = 2*np.pi*np.random.rand(npoints)
phi = np.arccos(2*np.random.rand(npoints)-1)
x = np.cos(theta) * np.sin(phi)
y = np.sin(theta) * np.sin(phi)
z = np.cos(phi)
return np.array([x,y,z])
def sample_normals(npoints):
vec = np.random.randn(3, npoints)
vec /= np.linalg.norm(vec, axis=0)
return vec
def sample_reject(npoints):
vec = np.zeros((3,npoints))
abc = 2*np.random.rand(3,npoints)-1
norms = np.linalg.norm(abc,axis=0)
mymask = norms<=1
abc = abc[:,mymask]/norms[mymask]
k = abc.shape[1]
vec[:,0:k] = abc
while k<npoints:
abc = 2*np.random.rand(3)-1
norm = np.linalg.norm(abc)
if 1e-5 <= norm <= 1:
vec[:,k] = abc/norm
k = k+1
return vec
然后是1000分
In [449]: timeit sample_trig(1000)
1000 loops, best of 3: 236 µs per loop
In [450]: timeit sample_normals(1000)
10000 loops, best of 3: 172 µs per loop
In [451]: timeit sample_reject(1000)
100 loops, best of 3: 13.7 ms per loop
请注意,在基于拒绝的实现中,我首先生成了npoints
个样本并丢弃了不好的样本,我只使用了一个循环来生成其余的点。似乎情况是直接的逐步拒绝需要更长的时间。我还删除了除零检查,以便与sample_normals
案例进行更清晰的比较。
从两个直接方法中删除矢量化将它们放在同一个球场:
def sample_trig_loop(npoints):
x = np.zeros(npoints)
y = np.zeros(npoints)
z = np.zeros(npoints)
for k in xrange(npoints):
theta = 2*np.pi*np.random.rand()
phi = np.arccos(2*np.random.rand()-1)
x[k] = np.cos(theta) * np.sin(phi)
y[k] = np.sin(theta) * np.sin(phi)
z[k] = np.cos(phi)
return np.array([x,y,z])
def sample_normals_loop(npoints):
vec = np.zeros((3,npoints))
for k in xrange(npoints):
tvec = np.random.randn(3)
vec[:,k] = tvec/np.linalg.norm(tvec)
return vec
In [464]: timeit sample_trig(1000)
1000 loops, best of 3: 236 µs per loop
In [465]: timeit sample_normals(1000)
10000 loops, best of 3: 173 µs per loop
In [466]: timeit sample_reject(1000)
100 loops, best of 3: 14 ms per loop
In [467]: timeit sample_trig_loop(1000)
100 loops, best of 3: 7.92 ms per loop
In [468]: timeit sample_normals_loop(1000)
100 loops, best of 3: 10.9 ms per loop
答案 3 :(得分:2)
取决于硬件的另一种方式可能会快得多。
选择a, b, c
为三个随机数,每个数字介于-1和1之间
计算r2 = a^2 + b^2 + c^2
如果r2> 1.0(=该点不在球体内)或r2&lt; 0.00001(=该点太靠近中心,我们将在投影到球体表面时除以零)你丢弃这些值,然后选择另一组随机a, b, c
否则,你得到了你的随机点(相对于球体的中心):
ir = R / sqrt(r2)
x = a * ir
y = b * ir
z = c * ir
答案 4 :(得分:0)
(编辑以反映评论中的更正)
我在2004年调查了一些常数方法解决这个问题。
假设您在球坐标中工作,theta
是垂直轴周围的角度(例如经度),phi
是从赤道抬起的角度(例如纬度),
然后在赤道以北的半球上获得均匀分布的随机点:
theta
= rand(0,360)。phi
= 90 *(1 - sqrt(rand(0,1)))。在球体而不是半球上获得点数,然后在50%的时间内简单地否定phi
。
对于好奇,类似的方法适用于在单位磁盘上生成均匀分布的点:
theta
= rand(0,360)。radius
= sqrt(rand(0,1))。我没有证明这些方法的正确性, 但在过去十年左右的时间里,我使用它们取得了很大的成功,并且确信它们的正确性。
各种方法的一些例证(从2004年开始)是here,包括在立方体表面上选择点并将它们标准化到球体上的方法的可视化。