我正在寻找一种算法,该算法可以在具有平坦底部(磁盘)的圆锥体内生成点。
我有创建圆锥的标准化轴(出于我们的目的,我们只说它是y轴,所以(0,1,0)以及圆锥的角度(假设是45度)。
我在网上可以找到的唯一资源是在圆锥体内生成矢量,但是它们基于对球体的采样,因此在底部,您会得到一种“雪锥”效果,而不是在底部有一个圆盘。
这是通过以下伪代码完成的:
// 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
较大圆锥体内部的高度较小的圆锥体,这两个圆锥体的顶点相同且底面平行。这两个圆锥体当然是相似的图形,并且方形立方体定律说较小的圆锥体的体积随其高度的立方体而变化。高度从0
到变化a
,我们希望其立方体在该范围内保持一致。因此,我们选择h
随统一随机变量的立方根变化,然后得到(在Python 3代码中),
h = a * (random()) ** (1/3)
接下来,我们考虑圆形区域,该圆形区域是较小的圆锥形高度的基础h
。该底的半径为(b / a) * h
,由相似的三角形组成。现在考虑r
在那个较大的圆形区域内半径较小的圆形区域,两个圆形都在同一平面上且具有相同的中心。较小的圆的面积随其半径的平方而变化,因此要在其范围内获得均匀的面积,我们取均匀随机变量的平方根。我们得到
r = (b / a) * h * sqrt(random())
现在,我们希望该点在半径较小的圆的圆周上的夹角t
(θ)r
。弧度的角度显然不取决于其他因素,因此我们只使用一个统一的随机变量来获得
t = 2 * pi * random()
现在,我们使用这三个随机变量h
,r
并t
在起始圆锥内选择我们的点。如果圆锥体的顶点位于原点并且圆锥体的轴沿y轴的正方向,则底边的中心为(0,a,0),底边的点为( b,a,0),您可以选择
x = r * cos(t) y = h z = r * sin(t)
当问及要在“表面”上生成样本时,您并没有澄清是指圆锥的侧面(还是“侧面”?),是指基座还是整个曲面。您的第二个图形似乎仅表示侧面,但是我将给出所有这三个的代码。
仅侧面
同样,我们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°,但与您的图片不同。从其他角度查看这些似乎可以确认它们的体积或面积是一致的。