numpy 生成实心球体[复制]

vc6uscn9  于 2023-05-22  发布在  其他
关注(0)|答案(1)|浏览(141)

此问题已在此处有答案

How to generate uniform random points inside d-dimension ball / sphere?(1个答案)
两年前关闭。
我想从n维实心球体中生成随机均匀样本。
我现在的方法是这样的

def sample_sphere(d, npoints):
    points = np.zeros((npoints, d))
    for i in range(npoints):
        r = np.random.rand() #random radius
        v = np.random.uniform(low= -1, high=1, size=d) #random direction
        v = v / np.linalg.norm(v)
        points[i] = r * v
    return points

但令人惊讶的是,这种方法给出了接近0的更高浓度的点,因为采样密度与体积不成比例:

如何统一采样?

z8dt9xmd

z8dt9xmd1#

球面分布有两种基本解释:

1.有界实心球

在给定半径处得到一个点的概率由在该半径处厚度为dr的壳的体积给出:p(r) ~ r^D直到一个常数。因此,径向CDF是(r / R)^(D+1),其中R是外半径,D是维数。球体必须是有界的,除非你想从整个空间中进行选择。
给定CDF生成样本的标准方法是将其反转:

random_radius = R * np.pow(np.random.uniform(0, 1), 1 / D)

顺便说一句,正确选择均匀方向的常用方法是使用高斯分布(see here):

random_direction = np.random.normal(size=D)
random_direction /= np.linalg.norm(random_direction)

你可以而且应该对你的函数进行向量化。不需要循环:

def sample_sphere(r, d, n):
    radii = r * np.pow(1 - np.random.uniform(0, 1, size=(n, 1)), 1 / d)
    directions = np.random.normal(size=(n, d))
    directions *= radii / np.linalg.norm(directions, axis=1, keepdims=True)
    return directions

请注意1 - np.random.uniform(...)。这是因为函数的值域是[0, 1),而我们想要的是(0, 1]。减法在不影响均匀性的情况下反转范围。

2.无界衰减分布

在这种情况下,你要找的是具有球对称性的高斯分布。这种分布的径向CDF与R^(D-1)成比例。对于高斯分布,这在2D中称为瑞利分布,在3D中称为麦克斯韦分布,在一般情况下称为chi分布。它们可以分别通过scipy.stats.rayleighscipy.stats.maxwellscipy.stats.chi对象直接获得。
在这种情况下,半径的意义不大,但它可以解释为分布的标准差。
函数的无界版本(也是矢量化的),然后变为:

def sample_sphere(s, d, n):
    radii = scipy.stats.chi.rvs(df=d, scale=s, size=(n, 1))
    directions = np.random.normal(size=(n, d))
    directions *= radii / np.linalg.norm(directions, axis=1, keepdims=True)
    return directions

相关问题