开发者社区> 问答> 正文

球面上均匀分布点的万无一失算法?

我一直试图在一个半径为“内半径”的球面上生成点,使它们均匀分布。该算法在半径为1的情况下工作,但在半径较大的情况下生成的点比预期的少。 我已经看过了类似的问题,但它们似乎是为了在整个体积中生成点,而不只是在球面上。

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()只是将球面点转换成笛卡尔坐标。 对于半径为1的100个点,它会生成99个点。 但是,如果半径为2并且请求100个点,则只生成26个点。 问题来源StackOverflow 地址:/questions/59384199/fool-proof-algorithm-for-uniformly-distributing-points-on-a-spheres-surface

展开
收起
kun坤 2019-12-26 15:36:29 807 0
1 条回答
写回答
取消 提交回答
  • 如果你可以在球面上均匀地生成点,那么要得到球面上的均匀分布,你可以简单地对向量进行归一化,使它们的半径等于球面的半径。 或者,你可以利用独立的等分布正态分布是旋转不变的这一事实。如果你从三个平均值为1,标准差为0的正态分布中取样,然后对向量进行归一化,它在球面上是均匀的。这里有一个例子:

    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)
    

    绝对可靠的是,当x、y和z都恰好为0时,我们可以处理非常不可能出现的0除错误:

    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
    
    2019-12-26 15:37:04
    赞同 展开评论 打赏
问答分类:
问答标签:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
数据+算法定义新世界 立即下载
袋鼠云基于实时计算的反黄牛算法 立即下载
Alink:基于Apache Flink的算法平台 立即下载