圆锥内的均匀采样(按体积)
Uniform sampling (by volume) within a cone
我正在寻找一种可以在底部平的圆锥体(圆盘)内生成点的算法。
我有创建圆锥体的标准化轴(为了我们的目的,我们只说它是 y 轴,所以 (0, 1, 0) 和圆锥体的角度(假设它是 45度)。
我能在网上找到的唯一资源是在圆锥内生成矢量,但它们是基于对球体的采样,所以在底部你会得到一种 "snow-cone" 效果而不是底部的圆盘。
这是通过以下伪代码完成的:
// Sample phi uniformly on [0, 2PI]
float phi = rand(0, 1) * 2 * PI
// Sample u uniformly from [cos(angle), 1]
float u = rand(0, 1) * (1 - cos(angle * PI/180)) + cos(angle * PI/180)
vec3 = vec3(sqrt(1 - u^2) * cos(phi), u, sqrt(1 - u^2) * sin(phi)))
下图就是我要的。能够在表面或内部生成样本也很好:
我可以使用积分和概率分布详细解释我的解决方案,但该站点上缺少 MathJax 使得这很困难。我将把我的解释保持在一个简单的水平,但它应该是清楚的。我也会使解决方案比你问的更通用一点:我们想要一个在高度 a
和底面半径 b
的直角圆锥内的随机点,我们想要均匀采样的点超过那个锥体的体积。此方法直接在锥体中随机选择一个点,不进行任何拒绝测试。
首先让我们考虑那个大圆锥内的高度 h
的小圆锥,两个圆锥具有相同的顶点和平行的底部。这两个圆锥体当然是相似的图形,square-cube 定律说较小的圆锥体的体积随着其高度的立方而变化。该高度从 0
到 a
不等,我们希望它的立方体在该范围内保持一致。因此我们选择h
随均匀随机变量的立方根变化,得到(在Python3代码中),
h = a * (random()) ** (1/3)
我们接下来考虑作为高度较小的锥体底部的圆形区域 h
。那个底的半径是(b / a) * h
,由相似的三角形组成。现在想想在那个更大的圆形区域内有一个半径为 r
的较小圆形区域,两个圆在同一平面上并且具有相同的中心。较小圆的面积随其半径的平方而变化,因此为了在其范围内获得均匀的面积,我们采用均匀随机变量的 平方根 。我们得到
r = (b / a) * h * sqrt(random())
我们现在需要半径为 r
的较小圆的圆周上的一个点的角度 t
(对于 theta)。以弧度为单位的角度显然不依赖于其他因素,所以我们只是使用一个统一的随机变量来得到
t = 2 * pi * random()
我们现在使用这三个随机变量 h
、r
和 t
来选择起始圆锥内的点。若圆锥的顶点在原点,圆锥的轴沿正y-axis,则底心为(0,a,0),底圆周上一点是(b,a,0),可以选择
x = r * cos(t)
y = h
z = r * sin(t)
当您询问有关生成样本 "on the surface" 时,您没有说明您是指锥体的一侧(还是 "sides"?)、底部还是整个表面。你的第二个图形似乎只是侧面,但我会给出所有三个的代码。
仅边
我们再次在较大的圆锥体内部使用高度 h
的较小圆锥体。它的表面积随其高度的平方而变化,因此我们取均匀随机变量的平方根。如果我们的点是在表面上,它底部的圆是固定的,而且角度也是均匀的。所以我们得到
h = a * sqrt(random())
r = (b / a) * h
t = 2 * pi * random()
对 x
、y
和 z
使用相同的代码,我在上面用于圆锥内部以获得圆锥侧面上的最终随机点.
仅基地
这很像在内部选择一个点,除了高度预先确定为等于整个圆锥体的高度。我们得到以下稍微简化的代码:
h = a
r = b * sqrt(random())
t = 2 * pi * random()
同样,对最后的 x
、y
和 z
使用之前的代码。
整个表面
这里我们可以先随机决定是把我们的点放在底面上还是放在表面上,然后用上面两种方式中的一种来放置点。高度 a
和底面半径 b
的圆锥底面积为 pi * b * b
,而圆锥侧面的表面积为 pi * b * sqrt(a*a + b*b)
。我们使用底面积与这些面积总和的比率来选择要用于我们的点的地下:
if random() < b / (b + sqrt(a*a + b*b)):
return point_on_base(a, b)
else:
return point_on_side(a, b)
将我上面的代码用于侧面和底部以完成该代码。
这里是 10,000 个随机点的简单 matplotlib 3D 散点图,首先在圆锥内部,然后在其侧面。请注意,我将顶角设为 45°,如您的文字所述,但与您的图片不同。从其他角度看这些似乎证实它们在体积或面积上是均匀的。
我正在寻找一种可以在底部平的圆锥体(圆盘)内生成点的算法。
我有创建圆锥体的标准化轴(为了我们的目的,我们只说它是 y 轴,所以 (0, 1, 0) 和圆锥体的角度(假设它是 45度)。
我能在网上找到的唯一资源是在圆锥内生成矢量,但它们是基于对球体的采样,所以在底部你会得到一种 "snow-cone" 效果而不是底部的圆盘。
这是通过以下伪代码完成的:
// Sample phi uniformly on [0, 2PI]
float phi = rand(0, 1) * 2 * PI
// Sample u uniformly from [cos(angle), 1]
float u = rand(0, 1) * (1 - cos(angle * PI/180)) + cos(angle * PI/180)
vec3 = vec3(sqrt(1 - u^2) * cos(phi), u, sqrt(1 - u^2) * sin(phi)))
下图就是我要的。能够在表面或内部生成样本也很好:
我可以使用积分和概率分布详细解释我的解决方案,但该站点上缺少 MathJax 使得这很困难。我将把我的解释保持在一个简单的水平,但它应该是清楚的。我也会使解决方案比你问的更通用一点:我们想要一个在高度 a
和底面半径 b
的直角圆锥内的随机点,我们想要均匀采样的点超过那个锥体的体积。此方法直接在锥体中随机选择一个点,不进行任何拒绝测试。
首先让我们考虑那个大圆锥内的高度 h
的小圆锥,两个圆锥具有相同的顶点和平行的底部。这两个圆锥体当然是相似的图形,square-cube 定律说较小的圆锥体的体积随着其高度的立方而变化。该高度从 0
到 a
不等,我们希望它的立方体在该范围内保持一致。因此我们选择h
随均匀随机变量的立方根变化,得到(在Python3代码中),
h = a * (random()) ** (1/3)
我们接下来考虑作为高度较小的锥体底部的圆形区域 h
。那个底的半径是(b / a) * h
,由相似的三角形组成。现在想想在那个更大的圆形区域内有一个半径为 r
的较小圆形区域,两个圆在同一平面上并且具有相同的中心。较小圆的面积随其半径的平方而变化,因此为了在其范围内获得均匀的面积,我们采用均匀随机变量的 平方根 。我们得到
r = (b / a) * h * sqrt(random())
我们现在需要半径为 r
的较小圆的圆周上的一个点的角度 t
(对于 theta)。以弧度为单位的角度显然不依赖于其他因素,所以我们只是使用一个统一的随机变量来得到
t = 2 * pi * random()
我们现在使用这三个随机变量 h
、r
和 t
来选择起始圆锥内的点。若圆锥的顶点在原点,圆锥的轴沿正y-axis,则底心为(0,a,0),底圆周上一点是(b,a,0),可以选择
x = r * cos(t)
y = h
z = r * sin(t)
当您询问有关生成样本 "on the surface" 时,您没有说明您是指锥体的一侧(还是 "sides"?)、底部还是整个表面。你的第二个图形似乎只是侧面,但我会给出所有三个的代码。
仅边
我们再次在较大的圆锥体内部使用高度 h
的较小圆锥体。它的表面积随其高度的平方而变化,因此我们取均匀随机变量的平方根。如果我们的点是在表面上,它底部的圆是固定的,而且角度也是均匀的。所以我们得到
h = a * sqrt(random())
r = (b / a) * h
t = 2 * pi * random()
对 x
、y
和 z
使用相同的代码,我在上面用于圆锥内部以获得圆锥侧面上的最终随机点.
仅基地
这很像在内部选择一个点,除了高度预先确定为等于整个圆锥体的高度。我们得到以下稍微简化的代码:
h = a
r = b * sqrt(random())
t = 2 * pi * random()
同样,对最后的 x
、y
和 z
使用之前的代码。
整个表面
这里我们可以先随机决定是把我们的点放在底面上还是放在表面上,然后用上面两种方式中的一种来放置点。高度 a
和底面半径 b
的圆锥底面积为 pi * b * b
,而圆锥侧面的表面积为 pi * b * sqrt(a*a + b*b)
。我们使用底面积与这些面积总和的比率来选择要用于我们的点的地下:
if random() < b / (b + sqrt(a*a + b*b)):
return point_on_base(a, b)
else:
return point_on_side(a, b)
将我上面的代码用于侧面和底部以完成该代码。
这里是 10,000 个随机点的简单 matplotlib 3D 散点图,首先在圆锥内部,然后在其侧面。请注意,我将顶角设为 45°,如您的文字所述,但与您的图片不同。从其他角度看这些似乎证实它们在体积或面积上是均匀的。