我需要在半径为R的圆内生成一个均匀随机点。
我意识到,只要在区间[0…2π),且在区间(0…R)我最终会有更多的点指向中心,因为对于两个给定的半径,小半径内的点会比大半径内的点彼此更接近。
我在这里找到了一篇关于这个的博客文章,但我不明白他的推理。我认为这是正确的,但我真的很想知道他从哪里得到(2/R2)×r以及他如何得到最终解。
更新:在发布这个问题7年之后,关于平方根算法背后的数学问题,我仍然没有得到一个令人满意的答案。所以我花了一天时间自己写了一个答案。链接到我的答案。
我需要在半径为R的圆内生成一个均匀随机点。
我意识到,只要在区间[0…2π),且在区间(0…R)我最终会有更多的点指向中心,因为对于两个给定的半径,小半径内的点会比大半径内的点彼此更接近。
我在这里找到了一篇关于这个的博客文章,但我不明白他的推理。我认为这是正确的,但我真的很想知道他从哪里得到(2/R2)×r以及他如何得到最终解。
更新:在发布这个问题7年之后,关于平方根算法背后的数学问题,我仍然没有得到一个令人满意的答案。所以我花了一天时间自己写了一个答案。链接到我的答案。
当前回答
如何在半径为R的圆内随机生成一个点:
r = R * sqrt(random())
theta = random() * 2 * PI
(假设random()均匀地给出0到1之间的值)
如果你想把它转换成笛卡尔坐标,你可以做到
x = centerX + r * cos(theta)
y = centerY + r * sin(theta)
为什么sqrt(随机())?
让我们看看sqrt(random())之前的数学运算。为简单起见,假设我们是在单位圆上工作,即R = 1。
点与点之间的平均距离应该是相同的,不管我们看的距离中心有多远。这意味着,例如,观察一个周长为2的圆的周长,我们应该找到的点的数量是周长为1的圆周长上点的数量的两倍。
由于圆的周长(2πr)随r线性增长,因此随机点的数量应该随r线性增长。换句话说,期望的概率密度函数(PDF)线性增长。由于PDF的面积应该等于1,最大半径是1,我们有
所以我们知道随机值的理想密度应该是什么样的。 现在:当我们只有一个0到1之间的均匀随机值时,我们如何生成这样一个随机值?
我们用了一个叫做反变换采样的技巧
从PDF中创建累积分布函数(CDF) 沿着y = x镜像 将得到的函数应用于0到1之间的统一值。
听起来复杂吗?让我插入一段带有小侧轨的引语来传达直觉:
Suppose we want to generate a random point with the following distribution: That is 1/5 of the points uniformly between 1 and 2, and 4/5 of the points uniformly between 2 and 3. The CDF is, as the name suggests, the cumulative version of the PDF. Intuitively: While PDF(x) describes the number of random values at x, CDF(x) describes the number of random values less than x. In this case the CDF would look like: To see how this is useful, imagine that we shoot bullets from left to right at uniformly distributed heights. As the bullets hit the line, they drop down to the ground: See how the density of the bullets on the ground correspond to our desired distribution! We're almost there! The problem is that for this function, the y axis is the output and the x axis is the input. We can only "shoot bullets from the ground straight up"! We need the inverse function! This is why we mirror the whole thing; x becomes y and y becomes x: We call this CDF-1. To get values according to the desired distribution, we use CDF-1(random()).
所以,回到生成随机半径值,其中PDF等于2x。
步骤1:创建CDF: 由于我们处理的是实数,CDF表示为PDF的积分。
CDF(x) = ∫ 2x = x2
步骤2:沿y = x镜像CDF:
从数学上讲,这可以归结为交换x和y并求解y:
CDF: y = x2 交换:x = y2 解:y =√x CDF-1: y =√x
步骤3:将得到的函数应用于0到1之间的统一值
CDF-1(random()) =√random()
这就是我们要推导的:-)
其他回答
首先我们生成一个cdf[x]
一点到圆心的距离小于x的概率。假设圆的半径为R。
显然,如果x = 0,那么cdf[0] = 0
显然,如果x是R,则cdf[R] = 1
显然,如果x = r,则cdf[r] = (r^2)/(r^2)
这是因为圆上的每个“小区域”都有相同的被选中的概率,所以概率与问题区域成比例。距离圆心x的面积是r^2
所以cdf[x] = x^2/R^2因为两者相互抵消了
我们有cdf[x]=x^2/R^2其中x从0到R
我们解出x
R^2 cdf[x] = x^2
x = R Sqrt[ cdf[x] ]
现在我们可以用一个从0到1的随机数来替换cdf
x = R Sqrt[ RandomReal[{0,1}] ]
最后
r = R Sqrt[ RandomReal[{0,1}] ];
theta = 360 deg * RandomReal[{0,1}];
{r,theta}
我们得到极坐标 {0.601168 R, 311.915°}
程序员解决方案:
创建一个位图(布尔值的矩阵)。你想要多大就有多大。 在位图中画一个圆。 创建一个圆的点查找表。 在这个查找表中选择一个随机索引。
const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;
bool matrix[MATRIX_SIZE][MATRIX_SIZE] = {0};
struct Point { int x; int y; };
Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];
void init()
{
int numberOfOnBits = 0;
for (int x = 0 ; x < MATRIX_SIZE ; ++x)
{
for (int y = 0 ; y < MATRIX_SIZE ; ++y)
{
if (x * x + y * y < RADIUS * RADIUS)
{
matrix[x][y] = true;
loopUpTable[numberOfOnBits].x = x;
loopUpTable[numberOfOnBits].y = y;
++numberOfOnBits;
} // if
} // for
} // for
} // ()
Point choose()
{
int randomIndex = randomInt(numberOfBits);
return loopUpTable[randomIndex];
} // ()
位图仅用于解释逻辑。这是没有位图的代码:
const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;
struct Point { int x; int y; };
Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];
void init()
{
int numberOfOnBits = 0;
for (int x = 0 ; x < MATRIX_SIZE ; ++x)
{
for (int y = 0 ; y < MATRIX_SIZE ; ++y)
{
if (x * x + y * y < RADIUS * RADIUS)
{
loopUpTable[numberOfOnBits].x = x;
loopUpTable[numberOfOnBits].y = y;
++numberOfOnBits;
} // if
} // for
} // for
} // ()
Point choose()
{
int randomIndex = randomInt(numberOfBits);
return loopUpTable[randomIndex];
} // ()
这可能会帮助那些对选择速度算法感兴趣的人;最快的方法是(可能?)拒绝抽样。
只需在单位正方形内生成一个点,并拒绝它,直到它在圆内。如(伪代码),
def sample(r=1):
while True:
x = random(-1, 1)
y = random(-1, 1)
if x*x + y*y <= 1:
return (x, y) * r
虽然有时它可能运行不止一次或两次(而且它不是常量时间,也不适合并行执行),但它要快得多,因为它不使用像sin或cos这样复杂的公式。
这取决于你对"均匀随机"的定义。这是一个微妙的点,你可以在这里的wiki页面上阅读更多关于它的内容:http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29,在这里同样的问题,对“均匀随机”给出不同的解释会给出不同的答案!
根据你如何选择这些点,分布可能会有所不同,即使它们在某种意义上是均匀随机的。
It seems like the blog entry is trying to make it uniformly random in the following sense: If you take a sub-circle of the circle, with the same center, then the probability that the point falls in that region is proportional to the area of the region. That, I believe, is attempting to follow the now standard interpretation of 'uniformly random' for 2D regions with areas defined on them: probability of a point falling in any region (with area well defined) is proportional to the area of that region.
朴素解不起作用的原因是它给了靠近圆中心的点更高的概率密度。换句话说,半径为r/2的圆被选中点的概率为r/2,但它的面积(点的数量)为*r^2/4。
因此,我们希望半径概率密度具有以下性质:
选择半径小于或等于给定r的概率必须与半径为r的圆的面积成正比(因为我们希望在点上有一个均匀的分布,面积越大意味着点越多)。
换句话说,我们希望在[0,r]之间选择半径的概率等于它在圆的总面积中所占的份额。圆的总面积是*R^2,半径为R的圆的面积是*R^2。因此,我们希望在[0,r]之间选择半径的概率为(pi*r^2)/(pi* r^2) = r^2/ r^2。
现在来算算:
The probability of choosing a radius between [0,r] is the integral of p(r) dr from 0 to r (that's just because we add all the probabilities of the smaller radii). Thus we want integral(p(r)dr) = r^2/R^2. We can clearly see that R^2 is a constant, so all we need to do is figure out which p(r), when integrated would give us something like r^2. The answer is clearly r * constant. integral(r * constant dr) = r^2/2 * constant. This has to be equal to r^2/R^2, therefore constant = 2/R^2. Thus you have the probability distribution p(r) = r * 2/R^2
Note: Another more intuitive way to think about the problem is to imagine that you are trying to give each circle of radius r a probability density equal to the proportion of the number of points it has on its circumference. Thus a circle which has radius r will have 2 * pi * r "points" on its circumference. The total number of points is pi * R^2. Thus you should give the circle r a probability equal to (2 * pi * r) / (pi * R^2) = 2 * r/R^2. This is much easier to understand and more intuitive, but it's not quite as mathematically sound.