在圆锥内(按体积)进行均匀采样

3
我正在寻找一种算法,可以在一个底部为平面的圆锥体内生成点。
我已经有了沿着创建圆锥体的归一化轴(我们假设它是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)))

以下图片是我要寻找的。如果能够在表面或内部生成样本,那就更好了。 enter image description here

生成一个随机点。如果它不在圆锥体内,则将其丢弃。 - Ignacio Vazquez-Abrams
我可以这样做,但我希望有更优化的方法,因为那种技术平均每次可能需要2个样本。此外,我不确定如何将锥体底部约束为平面而不是半球形。 - Kinru
1
@IgnacioVazquez-Abrams 这被称为接受/拒绝或拒绝采样 - pjs
...并且是蒙特卡罗方法的一种。 - Ignacio Vazquez-Abrams
2
@IgnacioVazquez-Abrams 这也是一种随机变量生成技术,但说“你尝试过随机变量生成吗?”并不会有帮助。我认为使用正确的技术名称是有帮助的。 - pjs
显示剩余2条评论
1个回答

8
我可以使用积分和概率分布详细解释我的解决方案,但是这个网站缺乏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())

我们现在需要求出半径为r的小圆周上某点的角度t(对应希腊字母 theta)。显然,这个弧度角不依赖于其他因素,所以我们只需使用均匀分布的随机变量即可得到。
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()

使用我上面用于圆锥体内部的xyz的相同代码,以获得圆锥侧面的最终随机点。

仅底部

这与选择内部点非常相似,只是高度预定为等于整个圆锥的高度。我们得到以下有些简化的代码:

h = a
r = b * sqrt(random())
t = 2 * pi * random()

再次使用之前的代码来确定最终的 xyz 值。

整个表面

首先,我们可以随机决定将点放在锥体的底部还是表面上,然后按照上述两种方式之一放置点。锥形的底面积为 pi * b * b,高度为 a,底半径为 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°,与您的文字说明相同,但不同于您的图片。从其他角度观察似乎证实它们在体积或面积上是均匀的。

enter image description here

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接