我需要在半径为R的圆内生成一个均匀随机点。

我意识到,只要在区间[0…2π),且在区间(0…R)我最终会有更多的点指向中心,因为对于两个给定的半径,小半径内的点会比大半径内的点彼此更接近。

我在这里找到了一篇关于这个的博客文章,但我不明白他的推理。我认为这是正确的,但我真的很想知道他从哪里得到(2/R2)×r以及他如何得到最终解。


更新:在发布这个问题7年之后,关于平方根算法背后的数学问题,我仍然没有得到一个令人满意的答案。所以我花了一天时间自己写了一个答案。链接到我的答案。


当前回答

我认为在这种情况下,使用极坐标是一种使问题复杂化的方法,如果你在一个边长为2R的正方形中随机选择点,然后选择点(x,y)使x^2+y^2<=R^2,这将会容易得多。

其他回答

程序员解决方案:

创建一个位图(布尔值的矩阵)。你想要多大就有多大。 在位图中画一个圆。 创建一个圆的点查找表。 在这个查找表中选择一个随机索引。

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];
} // ()

设ρ(半径)和φ(方位角)是两个随机变量,对应于圆内任意一点的极坐标。如果这些点是均匀分布的,那么ρ和φ的分布函数是什么?

对于任意r: 0 < r < r,半径坐标ρ小于r的概率为

P[ρ < r] = P[点在半径r的圆内]= S1 / S0 =(r/ r)2

其中S1和S0分别是半径为r和r的圆的面积。 因此,CDF可表示为:

          0          if r<=0
  CDF =   (r/R)**2   if 0 < r <= R
          1          if r > R

和PDF格式:

PDF = d/dr(CDF) = 2 * (r/R**2) (0 < r <= R).

请注意,对于R=1随机变量根号(X),其中X在[0,1]上是一致的,有这个确切的CDF(因为P[根号(X) < y] = P[X < y**2] = y**2对于0 < y <= 1)。

φ在0 ~ 2*π范围内分布明显均匀。现在你可以创建随机极坐标,并使用三角方程将其转换为笛卡尔坐标:

x = ρ * cos(φ)
y = ρ * sin(φ)

忍不住要发布R=1的python代码。

from matplotlib import pyplot as plt
import numpy as np

rho = np.sqrt(np.random.uniform(0, 1, 5000))
phi = np.random.uniform(0, 2*np.pi, 5000)

x = rho * np.cos(phi)
y = rho * np.sin(phi)

plt.scatter(x, y, s = 4)

你会得到

这样一个有趣的问题。 一个点被选择的概率随着距离轴原点的增加而降低的基本原理在上面已经解释了多次。我们通过取U[0,1]的根来解释这一点。 下面是Python 3中正r的通解。

import numpy
import math
import matplotlib.pyplot as plt

def sq_point_in_circle(r):
    """
    Generate a random point in an r radius circle 
    centered around the start of the axis
    """

    t = 2*math.pi*numpy.random.uniform()
    R = (numpy.random.uniform(0,1) ** 0.5) * r

    return(R*math.cos(t), R*math.sin(t))

R = 200 # Radius
N = 1000 # Samples

points = numpy.array([sq_point_in_circle(R) for i in range(N)])
plt.scatter(points[:, 0], points[:,1])

如何在半径为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()

这就是我们要推导的:-)

你也可以用你的直觉。

圆的面积是*r^2

为r = 1

得到的面积是。让我们假设我们有某种函数f,它将N=10个点均匀分布在一个圆内。这个比值是10 /

现在我们把面积和点数翻倍

对于r=2和N=20

面积是4,比值是20/4或10/2。半径越大,比值就越小,因为它的增长是二次的,N是线性的。

为了解决这个问题,我们可以说

x = r^2
sqrt(x) = r

如果在极坐标下生成一个向量

length = random_0_1();
angle = random_0_2pi();

更多的点会落在中心周围。

length = sqrt(random_0_1());
angle = random_0_2pi();

长度不再是均匀分布的,但是向量现在是均匀分布的。