简体   繁体   English

用于在球体表面均匀分布点的万无一失算法?

[英]Fool-proof algorithm for uniformly distributing points on a sphere's surface?

I've been trying to generate points on the surface of a sphere of radius "inner_radius", such that they're uniformly spread out.我一直在尝试在半径为“inner_radius”的球体的表面上生成点,以便它们均匀分布。 The algorithm works as expected for a radius of 1, but generates lesser than expected points for greater radii.该算法在半径为 1 时按预期工作,但对于更大的半径生成比预期更少的点。 I have looked through similar questions on here, but they seem to be for generating points throughout the volume and not just on the surface of the sphere.我在这里查看了类似的问题,但它们似乎用于在整个体积中生成点,而不仅仅是在球体的表面上。

import numpy as np
PI=np.pi

def spherical_to_cartesian(pol_ang,azim_ang,radius): #This function converts given spherical coordinates (theta, phi and radius) to cartesian coordinates.
    return np.array((radius*np.sin(pol_ang) * np.cos(azim_ang),
                        radius*np.sin(pol_ang) * np.sin(azim_ang),
                        radius*np.cos(pol_ang))
                        )

def get_electron_coordinates_list(inner_radius,electron_count):
    #Algorithm used was mostly  taken from https://www.cmu.edu/biolphys/deserno/pdf/sphere_equi.pdf . Explanations in code added by me.
    electron_coordinate_list=[]
    inner_area=4*(PI*inner_radius**2)
    area_per_electron=inner_area/electron_count
    pseudo_length_per_electron=np.sqrt(area_per_electron) #This is the side length of a square where the area of it is the area per electron on the sphere.
    #Now, we need to get a value of angular space, such that angular space between electrons on latitude and longitude per electron is equal
    #As a first step to obtaining this, we must make another value holding a whole number approximation of the ratio between PI and the pseudo_length. This will give the number of 
    #possible latitudes.

    possible_count_of_lats=np.round(PI/pseudo_length_per_electron)

    approx_length_per_electron_lat=PI/possible_count_of_lats #This is the length between electrons on a latitude
    approx_length_per_electron_long=area_per_electron/approx_length_per_electron_lat #This is the length between electrons on a longitude

    for electron_num_lat in range(int(possible_count_of_lats.item())): #The int(somenumpyvalue.item()) is used because Python cannot iterate over a numpy integer and it must be converted to normal int.
        pol_ang=PI*(electron_num_lat+0.5)/possible_count_of_lats #The original algorithm recommended pol_ang=PI*(electron_num_lat+0.5)/possible_count_of_lats. The 0.5 appears to be added in order to get a larger number of coordinates.
        #not sure if removing the 0.5 affects results. It didnt do so drastically, so what gives? Anyway, this gets the polar angle as PI*(latitudenumber)/totalnumberoflatitudes.

        possible_count_of_longs=np.round(2*PI*np.sin(pol_ang)/approx_length_per_electron_long)

        for electron_num_long in range(int(possible_count_of_longs.item())):

            azim_ang=(2*PI)*(electron_num_long)/possible_count_of_longs #This gets the azimuthal angle as 2PI*longitudenumber/totalnumberoflongitudes

            electron_coordinate=spherical_to_cartesian(pol_ang, azim_ang,inner_radius) #Converts the recieved spherical coordinates to cartesian so Manim can easily handle them.
            electron_coordinate_list.append(electron_coordinate) #Add this coordinate to the electron_coordinate_list

            print("Got coordinates: ",electron_coordinate) #Print the coordinate recieved.
    print(len(electron_coordinate_list)," points generated.") #Print the amount of electrons will exist. Comment these two lines out if you don't need the data.

    return electron_coordinate_list 
get_electron_coordinates_list(1,100)
get_electron_coordinates_list(2,100)

Spherical_to_Cartesian() does nothing other than convert the spherical points to Cartesian. Spherical_to_Cartesian() 除了将球形点转换为笛卡尔坐标外,什么都不做。

For 100 points and radius 1, it generates 99 points.对于 100 个点和半径 1,它生成 99 个点。 But, only 26 points are made if the radius is 2 and 100 points are requested.但是,如果半径为 2 且请求 100 点,则仅生成 26 点。

If you can generate points uniformly in the sphere's volume, then to get a uniform distribution on the sphere's surface, you can simply normalize the vectors so their radius equals the sphere's radius.如果您可以在球体的体积中均匀地生成点,那么为了在球体的表面上获得均匀的分布,您可以简单地将向量归一化,使其半径等于球体的半径。

Alternatively, you can use the fact that independent identically-distributed normal distributions are rotationally-invariant .或者,您可以使用独立同分布正态分布是旋转不变的事实。 If you sample from 3 normal distributions with mean 1 and standard deviation 0, and then likewise normalize the vector, it will be uniform on the sphere's surface.如果您从 3 个均值为 1 且标准差为 0 的正态分布中采样,然后同样对向量进行归一化,则它在球体表面上将是均匀的。 Here's an example:下面是一个例子:

import random

def sample_sphere_surface(radius=1):
    x, y, z = (random.normalvariate(0, 1) for i in range(3))
    scalar = radius / (x**2 + y**2 + z**2) ** 0.5
    return (x * scalar, y * scalar, z * scalar)

To be absolutely foolproof, we can handle the astronomically unlikely case of a division-by-zero error when x , y and z all happen to be zero:为了绝对万无一失,当xyz都恰好为零时,我们可以处理天文上不可能发生的被零除错误的情况:

def sample_sphere_surface(radius=1):
    while True:
        try:
            x, y, z = (random.normalvariate(0, 1) for i in range(3))
            scalar = radius / (x**2 + y**2 + z**2) ** 0.5
            return (x * scalar, y * scalar, z * scalar)
        except ZeroDivisionError:
            pass

The element of area is, in polar coordinates, sinΘ dΘ dφ .在极坐标中,面积元素是sinΘ dΘ dφ Hence the azimuth angle can be uniformly distributed, while the inclination must be redistributed.因此方位角可以均匀分布,而倾角必须重新分布。 Using the inverse transform sampling trick, Θ=arccos(u) where u is drawn uniformly will do.使用逆变换采样技巧, Θ=arccos(u)其中u被均匀绘制就可以了。

Hence in Cartesian coordinates, (√(1-u²) cos v, √(1-u²) sin v, u) where u is drawn from [-1,1) and v from [0,2π) .因此,在笛卡尔坐标中, (√(1-u²) cos v, √(1-u²) sin v, u)其中u来自[-1,1)v来自[0,2π)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM