生成分布在单位sph表面上的点的随机样本

2024-04-29 06:55:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我试着用numpy在球体表面生成随机点。我已经复习了解释均匀分布的文章。但是,需要一些关于如何仅在球体表面上生成点的想法。我有坐标(x,y,z)和每个球体的半径。

我对这一级别的数学不是很精通,我试图理解蒙特卡罗模拟。

任何帮助都将不胜感激。

谢谢, 帕林


Tags: numpy文章半径数学表面球体
3条回答

基于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)

enter image description here

同样的方法也推广到单位圆(ndim=2)或高维单位超球表面上的均匀分布点的提取。

球面上的点可以用两个球坐标来表示,thetaphi,用0 < theta < 2pi0 < phi < pi

将公式转换为笛卡尔坐标:

x = r * cos(theta) * sin(phi)
y = r * sin(theta) * sin(phi)
z = r * cos(phi)

其中r是球体的半径。

因此,程序可以在它们的范围内以均匀分布随机采样thetaphi,并由此生成笛卡尔坐标。

但是这些点在球体的极点上分布得更为密集。为了使点在球面上均匀分布,需要选择phi作为phi = acos(a),其中-1 < a < 1选择均匀分布。

对于Numpy代码,它将与Sampling uniformly distributed random points inside a spherical volume中的相同,只是变量radius有一个固定值。

在与@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

相关问题 更多 >