球面上 n 个点的均匀分布

我需要一个算法,可以给我的位置周围的球体 N 点(小于20,可能) ,模糊地分散他们。不需要“完美”,但我需要它,这样它们就不会捆绑在一起了。

  • 这个问题 提供了很好的代码,但是我找不到一种方法来使这个统一,因为这看起来是100% 随机的。
  • 这篇博客文章 推荐了两种允许输入球面上的点数的方法,但是 萨夫和奎吉拉尔算法正好是我可以转录的伪代码,而且我发现 代码示例包含了“ node [ k ]”,我看不到解释并且破坏了这种可能性。第二个博客例子是黄金分割螺旋,它给了我奇怪的,捆绑的结果,没有明确的方法来定义一个恒定的半径。
  • 这个来自 这个问题的算法 似乎可以工作,但我不能把页面上的内容拼接成假代码或其他任何东西。

我遇到的其他一些问题涉及到随机统一分布,这增加了我并不关心的复杂程度。我很抱歉这是个愚蠢的问题,但是我想证明我真的很努力地寻找,但是仍然没有找到。

所以我要找的是一个简单的伪代码将 N 个点均匀地分布在一个单位球面上,这个单位球面要么以球面坐标返回,要么以笛卡尔坐标返回。如果它能随机分布就更好了(想象一下围绕恒星的行星,体面地散开,但有余地)。

144101 次浏览

N的两个最大因子为例,如果是 N==20,那么两个最大因子是 {5,4},或者更一般地说是 {a,b}。计算

dlat  = 180/(a+1)
dlong = 360/(b+1})

把你的第一个点放在 {90-dlat/2,(dlong/2)-180},你的第二个点放在 {90-dlat/2,(3*dlong/2)-180},你的第三个点放在 {90-dlat/2,(5*dlong/2)-180},直到你环球旅行过一次,那时候当你走在 {90-3*dlat/2,(dlong/2)-180}旁边的时候,你已经到了大约 {75,150}

很明显,我是在球形地球表面,按照惯例,把 +/-转换成 N/S 或 E/W。很明显,这给了你一个完全非随机的分布,但是它是均匀的,而且这些点不是聚集在一起的。

为了增加一定程度的随机性,可以生成2个正态分布(平均值为0,标准开发人员为{ dlat3,dlong/3}) ,并将它们添加到均匀分布的点上。

这个示例代码中,node[k]只是第 kth 节点。您正在生成一个数组 N 点,并且 node[k]是 kth (从0到 N-1)。如果这就是让你感到困惑的地方,希望你现在可以利用这一点。

(换句话说,k是一个大小为 N 的数组,它在代码片段开始之前定义,并且包含一个点列表)。

或者 ,基于这里的另一个答案(并使用 Python) :

> cat ll.py
from math import asin
nx = 4; ny = 5
for x in range(nx):
lon = 360 * ((x+0.5) / nx)
for y in range(ny):
midpt = (y+0.5) / ny
lat = 180 * asin(2*((y+0.5)/ny-0.5))
print lon,lat
> python2.7 ll.py
45.0 -166.91313924
45.0 -74.0730322921
45.0 0.0
45.0 74.0730322921
45.0 166.91313924
135.0 -166.91313924
135.0 -74.0730322921
135.0 0.0
135.0 74.0730322921
135.0 166.91313924
225.0 -166.91313924
225.0 -74.0730322921
225.0 0.0
225.0 74.0730322921
225.0 166.91313924
315.0 -166.91313924
315.0 -74.0730322921
315.0 0.0
315.0 74.0730322921
315.0 166.91313924

如果你画出来,你会看到两极附近的垂直间距更大,所以每个点都位于大约相同的总空间 面积(两极附近的“水平”空间更少,所以它给出的“垂直”空间更多)。

这并不意味着所有的点都与它们的邻居有相同的距离(这就是我认为你的链接所说的) ,但是它可能足以满足你的需要,并且可以改进简单的制作一个统一的 lat/lon 网格。

用少量的点,你可以运行一个模拟:

from random import random,randint
r = 10
n = 20
best_closest_d = 0
best_points = []
points = [(r,0,0) for i in range(n)]
for simulation in range(10000):
x = random()*r
y = random()*r
z = r-(x**2+y**2)**0.5
if randint(0,1):
x = -x
if randint(0,1):
y = -y
if randint(0,1):
z = -z
closest_dist = (2*r)**2
closest_index = None
for i in range(n):
for j in range(n):
if i==j:
continue
p1,p2 = points[i],points[j]
x1,y1,z1 = p1
x2,y2,z2 = p2
d = (x1-x2)**2+(y1-y2)**2+(z1-z2)**2
if d < closest_dist:
closest_dist = d
closest_index = i
if simulation % 100 == 0:
print simulation,closest_dist
if closest_dist > best_closest_d:
best_closest_d = closest_dist
best_points = points[:]
points[closest_index]=(x,y,z)




print best_points
>>> best_points
[(9.921692138442777, -9.930808529773849, 4.037839326088124),
(5.141893371460546, 1.7274947332807744, -4.575674650522637),
(-4.917695758662436, -1.090127967097737, -4.9629263893193745),
(3.6164803265540666, 7.004158551438312, -2.1172868271109184),
(-9.550655088997003, -9.580386054762917, 3.5277052594769422),
(-0.062238110294250415, 6.803105171979587, 3.1966101417463655),
(-9.600996012203195, 9.488067284474834, -3.498242301168819),
(-8.601522086624803, 4.519484132245867, -0.2834204048792728),
(-1.1198210500791472, -2.2916581379035694, 7.44937337008726),
(7.981831370440529, 8.539378431788634, 1.6889099589074377),
(0.513546008372332, -2.974333486904779, -6.981657873262494),
(-4.13615438946178, -6.707488383678717, 2.1197605651446807),
(2.2859494919024326, -8.14336582650039, 1.5418694699275672),
(-7.241410895247996, 9.907335206038226, 2.271647103735541),
(-9.433349952523232, -7.999106443463781, -2.3682575660694347),
(3.704772125650199, 1.0526567864085812, 6.148581714099761),
(-3.5710511242327048, 5.512552040316693, -3.4318468250897647),
(-7.483466337225052, -1.506434920354559, 2.36641535124918),
(7.73363824231576, -8.460241422163824, -1.4623228616326003),
(10, 0, 0)]

编辑: 这并没有回答 OP 想要问的问题,把它留在这里以防人们发现它有用。

我们使用概率的乘法规则,结合无穷小。这将产生两行代码来实现您所期望的结果:

longitude: φ = uniform([0,2pi))
azimuth:   θ = -arcsin(1 - 2*uniform([0,1]))

(在下列坐标系中定义:)

enter image description here

您的语言通常有一个统一的随机数原语。例如,在 python 中,可以使用 random.random()返回范围为 [0,1)的数字。你可以把这个数字乘以 k 得到一个在 [0,k)范围内的随机数。因此在 python 中,uniform([0,2pi))意味着 random.random()*2*math.pi


证据

现在我们不能一致地给 θ 赋值,否则我们会在极点处聚集。我们希望赋予与球形楔子表面积成正比的概率(图中的 θ 实际上是 φ) :

enter image description here

赤道上的角移 dφ 会导致 dφ * r 的位移。在任意方位角 θ 的位移是多少?Z 轴的半径是 r*sin(θ)所以纬度与楔子的交点是 dφ * r*sin(θ)。因此,我们通过积分从南极到北极的切片面积来计算从它取样的面积的 累积分布

enter image description here(其中的东西 = dφ*r)

我们现在将尝试获取 CDF 的相反值以从中取样: http://en.wikipedia.org/wiki/Inverse_transform_sampling

首先,我们通过将几乎 CDF 除以它的最大值来规范化。这有抵消 dφ 和 r 的副作用。

azimuthalCDF: cumProb = (sin(θ)+1)/2 from -pi/2 to pi/2


inverseCDF: θ = -sin^(-1)(1 - 2*cumProb)

因此:

let x by a random float in range [0,1]
θ = -arcsin(1-2*x)

这就是球面上的填充点,没有一般的(已知的)完全解。然而,存在大量不完美的解决方案。最受欢迎的三种似乎是:

  1. 创建一个模拟 。把每个点看作是一个电子束缚在一个球体上,然后运行一定数量的步骤模拟。电子的排斥力自然会使系统趋向于更稳定的状态,在这种状态下,两个点之间的距离尽可能的远。
  2. 超立方体排斥 。这种听起来很奇特的方法实际上非常简单: 在立方体内部均匀地选择包围球体的 (远远超过 n)点,然后拒绝球体外部的点。将剩余的点视为向量,并对它们进行标准化。这些是你的“样本”-选择他们的 n使用一些方法(随机,贪婪,等)。
  3. 螺旋近似。你沿着一个球体画一个螺旋线,然后把这些点均匀地分布在螺旋线周围。由于涉及到数学,这些比模拟更加复杂,但是速度更快(可能涉及更少的代码)。最受欢迎的似乎是 萨夫等人

一个 很多关于这个问题的更多信息可以找到 给你

# create uniform spiral grid
numOfPoints = varargin[0]
vxyz = zeros((numOfPoints,3),dtype=float)
sq0 = 0.00033333333**2
sq2 = 0.9999998**2
sumsq = 2*sq0 + sq2
vxyz[numOfPoints -1] = array([(sqrt(sq0/sumsq)),
(sqrt(sq0/sumsq)),
(-sqrt(sq2/sumsq))])
vxyz[0] = -vxyz[numOfPoints -1]
phi2 = sqrt(5)*0.5 + 2.5
rootCnt = sqrt(numOfPoints)
prevLongitude = 0
for index in arange(1, (numOfPoints -1), 1, dtype=float):
zInc = (2*index)/(numOfPoints) -1
radius = sqrt(1-zInc**2)


longitude = phi2/(rootCnt*radius)
longitude = longitude + prevLongitude
while (longitude > 2*pi):
longitude = longitude - 2*pi


prevLongitude = longitude
if (longitude > pi):
longitude = longitude - 2*pi


latitude = arccos(zInc) - pi/2
vxyz[index] = array([ (cos(latitude) * cos(longitude)) ,
(cos(latitude) * sin(longitude)),
sin(latitude)])

这个方法很有效,而且非常简单:

    private function moveTweets():void {




var newScale:Number=Scale(meshes.length,50,500,6,2);
trace("new scale:"+newScale);




var l:Number=this.meshes.length;
var tweetMeshInstance:TweetMesh;
var destx:Number;
var desty:Number;
var destz:Number;
for (var i:Number=0;i<this.meshes.length;i++){


tweetMeshInstance=meshes[i];


var phi:Number = Math.acos( -1 + ( 2 * i ) / l );
var theta:Number = Math.sqrt( l * Math.PI ) * phi;


tweetMeshInstance.origX = (sphereRadius+5) * Math.cos( theta ) * Math.sin( phi );
tweetMeshInstance.origY= (sphereRadius+5) * Math.sin( theta ) * Math.sin( phi );
tweetMeshInstance.origZ = (sphereRadius+5) * Math.cos( phi );


destx=sphereRadius * Math.cos( theta ) * Math.sin( phi );
desty=sphereRadius * Math.sin( theta ) * Math.sin( phi );
destz=sphereRadius * Math.cos( phi );


tweetMeshInstance.lookAt(new Vector3D());




TweenMax.to(tweetMeshInstance, 1, {scaleX:newScale,scaleY:newScale,x:destx,y:desty,z:destz,onUpdate:onLookAtTween, onUpdateParams:[tweetMeshInstance]});


}


}
private function onLookAtTween(theMesh:TweetMesh):void {
theMesh.lookAt(new Vector3D());
}

这个答案是基于同样的“理论”,这是概述良好的 这个答案

我加上这个答案:
--没有其他选择符合“一致性”需要“准确无误”(或者不那么明显)。(注意到在原始请求中特别需要像行星一样的分布行为,你只需要随机地从 k 均匀创建的点的有限列表中拒绝(随机地写回 k 项中的索引计数)
--最接近的其他暗示迫使你通过“角轴”来决定“ N”,而在两个角轴值之间只有“ N 的一个值”(在 N 数较少的情况下,很难知道什么可能重要,什么可能不重要(例如,你想要“5”点——玩得开心))
此外,如果没有任何图像,很难“理解”如何区分其他选项,所以下面是这个选项的样子(下图) ,以及随之而来的准备运行的实现。< br >

N 在20:

enter image description here
然后 N 在80: enter image description here


下面是现成运行的 python3代码,其中的模拟与其他代码找到的“ http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere”是同一个源代码。(我所包含的绘图,当以‘ main’运行时触发,取自: http://www.scipy.org/Cookbook/Matplotlib/mplot3D)

from math import cos, sin, pi, sqrt


def GetPointsEquiAngularlyDistancedOnSphere(numberOfPoints=45):
""" each point you get will be of form 'x, y, z'; in cartesian coordinates
eg. the 'l2 distance' from the origion [0., 0., 0.] for each point will be 1.0
------------
converted from:  http://web.archive.org/web/20120421191837/http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere )
"""
dlong = pi*(3.0-sqrt(5.0))  # ~2.39996323
dz   =  2.0/numberOfPoints
long =  0.0
z    =  1.0 - dz/2.0
ptsOnSphere =[]
for k in range( 0, numberOfPoints):
r    = sqrt(1.0-z*z)
ptNew = (cos(long)*r, sin(long)*r, z)
ptsOnSphere.append( ptNew )
z    = z - dz
long = long + dlong
return ptsOnSphere


if __name__ == '__main__':
ptsOnSphere = GetPointsEquiAngularlyDistancedOnSphere( 80)


#toggle True/False to print them
if( True ):
for pt in ptsOnSphere:  print( pt)


#toggle True/False to plot them
if(True):
from numpy import *
import pylab as p
import mpl_toolkits.mplot3d.axes3d as p3


fig=p.figure()
ax = p3.Axes3D(fig)


x_s=[];y_s=[]; z_s=[]


for pt in ptsOnSphere:
x_s.append( pt[0]); y_s.append( pt[1]); z_s.append( pt[2])


ax.scatter3D( array( x_s), array( y_s), array( z_s) )
ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('Z')
p.show()
#end

测试在低计数(N 在2,5,7,13等) ,似乎工作’不错’

试试:

function sphere ( N:float,k:int):Vector3 {
var inc =  Mathf.PI  * (3 - Mathf.Sqrt(5));
var off = 2 / N;
var y = k * off - 1 + (off / 2);
var r = Mathf.Sqrt(1 - y*y);
var phi = k * inc;
return Vector3((Mathf.Cos(phi)*r), y, Mathf.Sin(phi)*r);
};

以上函数采用 N 回路总循环和 k 回路电流迭代进行循环运行。

它是基于向日葵种子图案,除了向日葵种子是弯曲成半圆顶,并再次成为一个球体。

这是一张照片,除了我把相机放在球体的一半,这样它看起来是2d 而不是3d,因为相机与所有点的距离是相同的。 Http://3.bp.blogspot.com/-9lbphlccqha/usxf88_bvvi/aaaaaaaaady/j7qhqsszsa8/s640/sphere.jpg

或者... 放置20个点,计算二十面体面的中心。对于12点,求二十面体的顶点。对于30点,二十面体边缘的中点。你可以对四面体,立方体,十二面体和八面体做同样的事情: 一组点在顶点上,另一组在面的中心,另一组在边的中心。然而,它们不能混合在一起。

斐波那契球面算法 对此很有用。它的速度很快,而且给出的结果一目了然,很容易欺骗人的眼睛。您可以看到一个处理完成的示例将随着时间的推移显示结果作为点被添加。这是另一个很棒的交互式例子由@gman 制作。下面是 Python 中的一个简单实现。

import math




def fibonacci_sphere(samples=1000):


points = []
phi = math.pi * (3. - math.sqrt(5.))  # golden angle in radians


for i in range(samples):
y = 1 - (i / float(samples - 1)) * 2  # y goes from 1 to -1
radius = math.sqrt(1 - y * y)  # radius at y


theta = phi * i  # golden angle increment


x = math.cos(theta) * radius
z = math.sin(theta) * radius


points.append((x, y, z))


return points

1000个样品给你这个:

enter image description here

你正在寻找的是所谓的 球形覆盖物球形覆盖物。球面覆盖问题是一个非常困难的问题,除了少数几个点外,其它解都是未知的。可以肯定的是,给定球面上的 n 个点,总是存在距离 d = (4-csc^2(\pi n/6(n-2)))^(1/2)或更近的两个点。

如果你想要一个概率方法来生成均匀分布在球面上的点,这很简单: 通过正态分布在空间中生成均匀分布的点(这是内置在 Java 中的,不难找到其他语言的代码)。所以在三维空间中,你需要

Random r = new Random();
double[] p = { r.nextGaussian(), r.nextGaussian(), r.nextGaussian() };

然后通过归一化球体与原点的距离将该点投影到球体上

double norm = Math.sqrt( (p[0])^2 + (p[1])^2 + (p[2])^2 );
double[] sphereRandomPoint = { p[0]/norm, p[1]/norm, p[2]/norm };

N 维的正态分布是球对称的,所以投影到球上是均匀的。

当然,并不能保证一个均匀生成的点集合中的任何两个点之间的距离都在下面,所以你可以使用拒绝来强制执行任何你可能具有的条件: 也许最好是生成整个集合,然后在必要时拒绝整个集合。(或者使用“早期拒绝”来拒绝你到目前为止已经生成的所有集合; 只是不要保留一些点数而放弃另一些点数。)您可以使用上面给出的 d公式,减去一些松弛,来确定点之间的最小距离,低于这个距离您将拒绝一组点。你将不得不计算 n 选择2个距离,拒绝的概率将取决于松弛; 很难说如何,所以运行一个模拟,以获得相关的统计数据的感觉。

黄金螺旋法

你说过你不能让黄金螺旋方法起作用那真是太遗憾了,因为它真的真的很好。我想给你一个完整的理解,这样也许你可以理解如何避免这个被“捆绑”

所以这里有一个快速,非随机的方法来创建一个近似正确的格子; ,如上所述,没有格子是完美的,但这可能是足够好的。它与其他方法相比,例如在 BendWavy.org,但它只是有一个很好的和漂亮的外观,以及一个保证,甚至间隔的限制。

底漆: 单位圆盘上的向日葵螺旋

为了理解这个算法,我首先请大家看看2D 向日葵螺旋算法。这是基于这样一个事实,即最无理数的是黄金比率 (1 + sqrt(5))/2,如果一个人通过“站在中心,转一个整圈的黄金比率,然后朝那个方向发射另一个点”的方法发射点,他就会自然而然地构建一个螺旋,当你得到越来越多的点时,尽管如此,仍然拒绝有明确定义的“条形”让这些点对齐

磁盘上均匀间距的算法是,

from numpy import pi, cos, sin, sqrt, arange
import matplotlib.pyplot as pp


num_pts = 100
indices = arange(0, num_pts, dtype=float) + 0.5


r = sqrt(indices/num_pts)
theta = pi * (1 + 5**0.5) * indices


pp.scatter(r*cos(theta), r*sin(theta))
pp.show()

它产生的结果看起来像(n = 100和 n = 1000) :

enter image description here

放射状的点间距

最奇怪的是公式 r = sqrt(indices / num_pts),我是怎么得到这个的

我在这里使用平方根,因为我希望它们在磁盘周围有均匀的面积间距。也就是说,在大 N的极限下,我希望一个小区域 R∈(RR + dR) ,Θ∈(ΘΘ + dΘ)包含与其面积成正比的多个点,即 R dR dΘ。现在,如果我们假设我们在这里讨论的是一个随机变量,这里有一个直接的解释,就是(RΘ)的联合概率密度,对于某个常数 R5只是 R4。在单位圆盘上的标准化将迫使 R5 = 1/π。

现在我来介绍一个小把戏。它来自于一个叫做 采样反向 CDF的概率论: 假设你想要 产生一个概率密度为 F(Z)的随机变量,并且你有一个随机变量 美国 ~ Uniform (0,1) ,就像大多数编程语言中的 random()一样。你是怎么做到的?

  1. 首先,将你的密度转换成 累积分布函数或 CDF,我们称之为 F(Z)。请记住,CDF 随着导数 F(Z)从0单调增加到1。
  2. 然后计算 CDF 的反函数 F-1(Z)。
  3. 你会发现 Z = F-1(美国)是根据目标密度分布的

现在,黄金比例螺旋技巧为 Θ以一种很好的均匀模式来空间指出,所以让我们把它积分出来; 对于单位圆盘,我们剩下的是 F(R) = R2。所以反函数是 F-1() = Θ0,因此我们可以用极坐标系 r = sqrt(random()); theta = 2 * pi * random()在圆盘上生成随机点。

现在我们不再使用 随机的采样,而是使用反函数 一致的采样,统一采样的好处是,我们得到的关于点在大型 N极限下的分布情况的结果,就好像我们是随机采样一样。这个组合就是诀窍。取代 random(),我们使用 (arange(0, num_pts, dtype=float) + 0.5)/num_pts,因此,如果我们想要采样10个点,他们是 r = 0.05, 0.15, 0.25, ... 0.95。我们统一采样 R得到等面积间隔,我们使用向日葵增量,以避免可怕的“酒吧”的点在输出。

现在在球体上画向日葵

我们需要做的改变,以点的球体只是涉及切换极坐标球坐标。径向坐标当然不包括在内,因为我们是在一个单位球面上。为了保持一致性,即使我是物理学家,我也会使用数学家的坐标,其中0≤ Φ≤ π 是从极点向下的纬度,0≤ Θ≤2π 是经度。所以与上面不同的是,我们基本上用 Φ代替了变量 R

我们的 area 元素是 R dR dΘ,现在变成了不太复杂的 sin (Φ) dΦ dΘ。因此,均匀间距的联合密度是 sin (Φ)/4π。积分出 Θ,我们发现 F(Φ) = sin (Φ)/2,因此 R1(Φ) = (1-cos (Φ))/2。反过来,我们可以看到一个统一的随机变量看起来像 acos (1-2 R4) ,但是我们是统一采样而不是随机采样,所以我们使用 ΦR6 = acos (1-2(R7 + 0.5)/R8)。算法的其他部分只是把它投影到 x,y 和 z 坐标上:

from numpy import pi, cos, sin, arccos, arange
import mpl_toolkits.mplot3d
import matplotlib.pyplot as pp


num_pts = 1000
indices = arange(0, num_pts, dtype=float) + 0.5


phi = arccos(1 - 2*indices/num_pts)
theta = pi * (1 + 5**0.5) * indices


x, y, z = cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi);


pp.figure().add_subplot(111, projection='3d').scatter(x, y, z);
pp.show()

对于 n = 100和 n = 1000,结果同样如下: enter image description here enter image description here

进一步研究

我想为马丁 · 罗伯茨的博客大声疾呼。请注意,上面我通过向每个索引添加0.5来创建索引的偏移量。这只是视觉上吸引我,但 事实证明,补偿的选择非常重要和不是恒定的间隔,可以意味着获得多达8% 更好的准确性包装,如果选择正确。也应该有一种方法让 他的 R < sub > 2 序列覆盖一个球体,这将是有趣的,看看这是否也产生一个不错的均匀覆盖,也许是-是,但也许需要,说,从只有一半的单位方形斜切或左右,并拉伸周围得到一个圆。

笔记

  1. 这些“条”是由一个数的有理近似形成的,最好的有理近似来自于它的连分数表达式,其中 z是一个整数,而 n_1, n_2, n_3, ...是一个正整数的有限或无限序列:

    def continued_fraction(r):
    while r != 0:
    n = floor(r)
    yield n
    r = 1/(r - n)
    

    因为小数部分 1/(...)总是介于0和1之间,所以连分数中的大整数允许一个特别好的有理近似: “一除以100和101之间的某个数”比“一除以1和2之间的某个数”要好因此,最有无理数的是 1 + 1/(1 + 1/(1 + ...)),它没有特别好的有理近似,人们可以通过乘以 Φ来求解 Φ = 1 + 1/Φ,从而得到黄金分割率的公式。

  2. 对于不太熟悉 NumPy 的人来说——所有的函数都是“向量化的”,这样 sqrt(array)就和其他语言可能编写的 map(sqrt, array)一样。这是一个逐个组件的 sqrt应用程序。这同样适用于标量除法或标量加法——它们适用于并行的所有组件。

  3. 一旦你知道这就是结果,证明就很简单了。如果你问 Z < Z < Z + dZ的概率是多少,这和问 Z < F-1(美国) < Z + dZ的概率是多少是一样的,对所有三个表达式都应用 F,注意它是一个单调递增的函数,因此 F(Z) < 美国 < F(Z + dZ) ,向右边展开找到 F(Z) + Z9(Z) dZ,由于 美国是一致的,所以这个概率只是 Z9(Z) dZ

Healpix 解决了一个紧密相关的问题(用等面积像素像素化球体) :

Http://healpix.sourceforge.net/

它可能是过度杀伤力,但也许在看了它之后,你会意识到它的其他一些不错的属性是你感兴趣的。它不仅仅是一个输出点云的函数。

我降落在这里,试图再次找到它; 名称“健康”不完全唤起球体..。

@ Robert King 这真是个不错的解决方案,不过里面有些马虎的小虫子。我知道这对我帮助很大,所以不要介意马虎。:) 这里是一个清理版本... 。

from math import pi, asin, sin, degrees
halfpi, twopi = .5 * pi, 2 * pi
sphere_area = lambda R=1.0: 4 * pi * R ** 2


lat_dist = lambda lat, R=1.0: R*(1-sin(lat))


#A = 2*pi*R^2(1-sin(lat))
def sphere_latarea(lat, R=1.0):
if -halfpi > lat or lat > halfpi:
raise ValueError("lat must be between -halfpi and halfpi")
return 2 * pi * R ** 2 * (1-sin(lat))


sphere_lonarea = lambda lon, R=1.0: \
4 * pi * R ** 2 * lon / twopi


#A = 2*pi*R^2 |sin(lat1)-sin(lat2)| |lon1-lon2|/360
#    = (pi/180)R^2 |sin(lat1)-sin(lat2)| |lon1-lon2|
sphere_rectarea = lambda lat0, lat1, lon0, lon1, R=1.0: \
(sphere_latarea(lat0, R)-sphere_latarea(lat1, R)) * (lon1-lon0) / twopi




def test_sphere(n_lats=10, n_lons=19, radius=540.0):
total_area = 0.0
for i_lons in range(n_lons):
lon0 = twopi * float(i_lons) / n_lons
lon1 = twopi * float(i_lons+1) / n_lons
for i_lats in range(n_lats):
lat0 = asin(2 * float(i_lats) / n_lats - 1)
lat1 = asin(2 * float(i_lats+1)/n_lats - 1)
area = sphere_rectarea(lat0, lat1, lon0, lon1, radius)
print("{:} {:}: {:9.4f} to  {:9.4f}, {:9.4f} to  {:9.4f} => area {:10.4f}"
.format(i_lats, i_lons
, degrees(lat0), degrees(lat1)
, degrees(lon0), degrees(lon1)
, area))
total_area += area
print("total_area = {:10.4f} (difference of {:10.4f})"
.format(total_area, abs(total_area) - sphere_area(radius)))


test_sphere()

基于 fnord 的回答,这里有一个 Unity3D 版本,增加了范围:

密码:

// golden angle in radians
static float Phi = Mathf.PI * ( 3f - Mathf.Sqrt( 5f ) );
static float Pi2 = Mathf.PI * 2;


public static Vector3 Point( float radius , int index , int total , float min = 0f, float max = 1f , float angleStartDeg = 0f, float angleRangeDeg = 360 )
{
// y goes from min (-) to max (+)
var y = ( ( index / ( total - 1f ) ) * ( max - min ) + min ) * 2f - 1f;


// golden angle increment
var theta = Phi * index ;
        

if( angleStartDeg != 0 || angleRangeDeg != 360 )
{
theta = ( theta % ( Pi2 ) ) ;
theta = theta < 0 ? theta + Pi2 : theta ;
            

var a1 = angleStartDeg * Mathf.Deg2Rad;
var a2 = angleRangeDeg * Mathf.Deg2Rad;
            

theta = theta * a2 / Pi2 + a1;
}


// https://stackoverflow.com/a/26127012/2496170
    

// radius at y
var rY = Mathf.Sqrt( 1 - y * y );
    

var x = Mathf.Cos( theta ) * rY;
var z = Mathf.Sin( theta ) * rY;


return  new Vector3( x, y, z ) * radius;
}

要点: https://gist.github.com/nukadelic/7449f0872f708065bc1afeb19df666f7/edit

预览:

MP4