在圆内生成随机点(均匀)

时间:2011-04-29 21:27:50

标签: math random geometry probability

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

我意识到,只需在区间[0 ...2π)中选择一个均匀的随机角度,并在区间(0 ... R )中均匀随机半径,我最终会得到更多的点朝向中心,因为对于两个给定的半径,较小半径中的点将比较大半径中的点更接近彼此。

我在over here找到了一篇博客文章,但我不理解他的推理。我认为这是正确的,但是我真的很想知道他从哪里得到(2 / R 2 )× r 和他是如何得出最终解决方案的。


更新:发布此问题7年后,我仍然没有收到有关平方根算法背后数学的实际问题的满意答案。所以我花了一天时间自己写答案。 Link to my answer

21 个答案:

答案 0 :(得分:181)

让我们像阿基米德那样接近这个。

如何在三角形ABC中统一生成一个点,其中| AB | = | BC |?让我们通过扩展到平行四边形ABCD来简化这一过程。在ABCD中统一生成点很容易。我们在BC上的AB和Y上均匀地选取随机点X并选择Z使得XBYZ是平行四边形。要在原始三角形中获得统一选择的点,我们只需将ADC中出现的任何点折叠回AC上的ABC。

现在考虑一个圆圈。在极限中,我们可以将其视为无限多的等腰三角形ABC,其原点为B,而圆周上的A和C消失得彼此相近。我们可以通过选择角度θ来选择这些三角形中的一个。因此,我们现在需要通过在棉条A​​BC中选取一个点来生成距离中心的距离。再次,延伸到ABCD,其中D现在是圆心的半径的两倍。

使用上述方法很容易在ABCD中挑选一个随机点。在AB上选择一个随机点。在BC上统一选择一个随机点。 IE浏览器。在[0,R]上均匀地选择一对随机数x和y,给出距离中心的距离。我们的三角形是一个薄的条子,因此AB和BC基本上是平行的。所以Z点只是距离原点的距离x + y。如果x + y> R,我们会向下折叠。

这是R = 1的完整算法。我希望你同意这很简单。它使用trig,但你可以保证它需要多长时间,以及它需要多少random()次调用,这与拒绝采样不同。

t = 2*pi*random()
u = random()+random()
r = if u>1 then 2-u else u
[r*cos(t), r*sin(t)]

这是在Mathematica。

f[] := Block[{u, t, r},
  u = Random[] + Random[];
  t = Random[] 2 Pi;
  r = If[u > 1, 2 - u, u];
  {r Cos[t], r Sin[t]}
]

ListPlot[Table[f[], {10000}], AspectRatio -> Automatic]

enter image description here

答案 1 :(得分:64)

如何在半径 R

的圆内生成随机点
r = R * sqrt(random())
theta = random() * 2 * PI

(假设random()统一给出0到1之间的值)

如果要将其转换为笛卡尔坐标,可以执行

x = centerX + r * cos(theta)
y = centerY + r * sin(theta)


为什么sqrt(random())

让我们来看看导致sqrt(random())的数学。为简单起见,假设我们使用单位圆,即 R = 1。

无论距中心多远,点之间的平均距离都应相同。这意味着,例如,在周长为2的圆周上,我们应该找到两倍于周长为1的圆周点数的点。




由于圆的周长(2π)与 r 线性增长,因此随机点的数量应与 r线性增长。换句话说,期望的probability density function(PDF)线性增长。由于PDF的面积应等于1且最大半径为1,因此我们有


因此我们知道随机值的所需密度应如何。 现在:当我们拥有的是0到1之间的均匀随机值时,我们如何生成这样的随机值?

我们使用名为inverse transform sampling

的技巧
  1. 从PDF中创建cumulative distribution function(CDF)
  2. 沿 y = x
  3. 镜像
  4. 将生成的函数应用于介于0和1之间的统一值。
  5. 听起来很复杂?让我插入一个带有一个小侧轨的黄色方框,传达出直觉:

      

    假设我们想要生成具有以下分布的随机点:

         

         

    那是

         
        
    • 1/5的点均匀地在1和2之间,并且
    •   
    • 4/5的点均匀地在2和3之间。
    •   
         

    正如其名称所示,CDF是PDF的累积版本。直观地说:虽然PDF( x )描述了 / x>处的随机值的数量,但CDF( x )描述了随机值的数量小于x

         

    在这种情况下,CDF看起来像:

         

         

    要想看看它是如何有用的,想象一下我们在均匀分布的高度从左到右射击子弹。当子弹击中线时,它们会掉到地上:

         

         

    了解地面上子弹的密度如何与我们所需的分布相对应!我们快到了!

         

    问题是对于此功能, y 轴是输出 x 轴是输入。我们只能“直接从地面射击子弹”!我们需要反函数!

         

    这就是为什么我们反映整个事情; x 变为 y y 变为 x

         

         

    我们称之为 CDF -1 。为了根据所需的分布得到值,我们使用CDF -1 (random())。

    ...所以,回到生成随机半径值,其中我们的PDF等于2

    第1步:创建CDF:

    由于我们正在与reals合作,因此CDF被表示为PDF的整体。

    CDF x )=∫2 x = x 2 < / p>

    第2步:沿 y镜像CDF = x

    从数学上讲,这可以归结为交换 x y 并解决 y

    CDF y = x 2
    交换: x = y 2
    解决: y =√ x
    CDF -1 y =√ x

    步骤3:将结果函数应用于0到1之间的统一值

    CDF -1 (random())=√random()

    我们打算推导出来: - )

答案 2 :(得分:26)

这是一个快速而简单的解决方案。

选择范围(0,1)中的两个随机数,即ab。如果b < a,请交换它们。你的观点是(b*R*cos(2*pi*a/b), b*R*sin(2*pi*a/b))

您可以按如下方式考虑此解决方案。如果你拿圆圈,剪掉它,然后将它拉直,你会得到一个直角三角形。将该三角形向下缩放,您将得到一个从(0, 0)(1, 0)(1, 1)的三角形,然后再返回(0, 0)。所有这些变换均匀地改变了密度。你所做的就是在三角形中统一选取一个随机点并反转过程以获得圆圈中的一个点。

答案 3 :(得分:18)

注意点密度与半径的平方成反比,因此不是从r中选择[0, r_max],而是从[0, r_max^2]中选择,然后将坐标计算为:

x = sqrt(r) * cos(angle)
y = sqrt(r) * sin(angle)

这将为您提供磁盘上的统一点分布。

http://mathworld.wolfram.com/DiskPointPicking.html

答案 4 :(得分:12)

以这种方式思考。如果你有一个矩形,其中一个轴是半径,一个是角度,你取这个矩形内的点几乎接近半径0.这些都将非常接近原点(在圆上靠近在一起)。但是,在半径R附近的点,它们都会落在圆的边缘附近(也就是彼此相距很远。)

这可能会让您知道为什么会出现这种行为。

在该链接上导出的因子告诉您,在将矩形映射到圆后,需要将矩形中的相应区域调整为不依赖于半径。

编辑:所以他在你分享的链接中写道:“通过计算累积分布的倒数很容易做到,我们得到r:”。

基本前提是,您可以通过使用所需概率密度函数的累积分布函数的反函数映射均匀来创建具有所需分布的变量。为什么?现在就把它视为理所当然,但这是事实。

这是我对数学的直观解释。相对于r的密度函数f(r)必须与r本身成比例。理解这一事实是任何基础微积分书籍的一部分。请参阅极区元素部分。其他一些海报也提到了这一点。

所以我们称之为f(r)= C * r;

这证明是大部分工作。现在,因为f(r)应该是概率密度,你可以很容易地看到通过在区间(0,R)上积分f(r)得到C = 2 / R ^ 2(这是读者的练习) 。)

因此,f(r)= 2 * r / R ^ 2

好的,这就是你如何在链接中获得公式。

然后,最后一部分来自(0,1)中的均匀随机变量u,你必须通过累积分布函数的反函数从该期望密度f(r)进行映射。要了解为什么会出现这种情况,您需要找到像Papoulis这样的高级概率文本(或者自己推导出来)。

积分f(r)得到F(r)= r ^ 2 / R ^ 2

要找到这个的反函数,你设置u = r ^ 2 / R ^ 2然后求解r,这给你r = R * sqrt(u)

这也很直观,u = 0应映射到r = 0.此外,u = 1 shoudl映射到r = R.此外,它通过平方根函数,这是有意义的并匹配链接。

答案 5 :(得分:8)

天真解决方案无法工作的原因是它为靠近圆心的点提供了更高的概率密度。换句话说,半径为r / 2的圆具有在其中选择一个点的概率r / 2,但它具有面积(点数)pi * r ^ 2/4。

因此,我们希望半径概率密度具有以下属性:

选择小于或等于给定r的半径的概率必须与半径为r的圆的面积成比例。 (因为我们希望在点上有均匀的分布,而更大的区域意味着更多的点)

换句话说,我们希望选择[0,r]之间的半径的概率等于它在圆的整个区域中的份额。总圆面积是pi * R ^ 2,半径为r的圆的面积是pi * r ^ 2。因此,我们希望选择[0,r]之间的半径的概率为(pi * r ^ 2)/(pi * R ^ 2)= r ^ 2 / R ^ 2.

现在是数学:

在[0,r]之间选择半径的概率是p(r)dr从0到r的积分(这只是因为我们加上了所有较小半径的概率)。因此,我们想要积分(p(r)dr)= r ^ 2 / R ^ 2。我们可以清楚地看到R ^ 2是一个常数,所以我们需要做的就是找出哪个p(r),当积分时会给出类似r ^ 2的东西。答案显然是r *不变的。积分(r *常数dr)= r ^ 2/2 *常数。这必须等于r ^ 2 / R ^ 2,因此常数= 2 / R ^ 2。因此,你有概率分布p(r)= r * 2 / R ^ 2

注意:另一种更直观的方式来思考这个问题就是想象你试图给每个半径为ra的概率密度等于它所拥有的点数的比例。周。因此,具有半径r的圆将具有2 * pi * r&#34;点&#34;在它的圆周上。总点数是pi * R ^ 2。因此,你应该给圆圈r的概率等于(2 * pi * r)/(pi * R ^ 2)= 2 * r / R ^ 2。这更容易理解,更直观,但它并不像数学上那么健全。

答案 6 :(得分:7)

这实际上取决于“均匀随机”的含义。这是一个微妙的观点,您可以在维基页面上阅读更多相关内容:http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29,其中同样的问题,对“均匀随机”给出不同的解释会给出不同的答案!

根据您选择积分的方式,分布可能会有所不同,即使它们在某些意义上均匀随机。

似乎博客条目试图在以下意义上使其均匀随机:如果您采用圆的子圆,具有相同的中心,则该点落入该区域的概率与该地区的面积。我认为,这是在尝试遵循areas defined on them对于2D区域的“均匀随机”的现在标准解释:任何区域中的点落下的概率(区域定义明确)与该区域的面积成比例

答案 7 :(得分:7)

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

对于任何r:0&lt; r&lt; R半径坐标ρ的概率小于r

P [ρ&lt; 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随机变量sqrt(X),其中X在[0,1]上是均匀的,具有这个精确的CDF(因为P [sqrt(X)

φ的分布在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)

你会得到

enter image description here

答案 8 :(得分:6)

以下是我的Python代码,用于从半径num的圆圈生成rad个随机点:

import matplotlib.pyplot as plt
import numpy as np
rad = 10
num = 1000

t = np.random.uniform(0.0, 2.0*np.pi, num)
r = rad * np.sqrt(np.random.uniform(0.0, 1.0, num))
x = r * np.cos(t)
y = r * np.sin(t)

plt.plot(x, y, "ro", ms=1)
plt.axis([-15, 15, -15, 15])
plt.show()

答案 9 :(得分:3)

Java解决方案和分发示例(2000分)

public void getRandomPointInCircle() {
    double t = 2 * Math.PI * Math.random();
    double r = Math.sqrt(Math.random());
    double x = r * Math.cos(t);
    double y = r * Math.sin(t);
    System.out.println(x);
    System.out.println(y);
}

Distribution 2000 points

基于来自@sigfpe

的previus解决方案https://stackoverflow.com/a/5838055/5224246

答案 10 :(得分:3)

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

答案 11 :(得分:2)

首先我们生成一个

的cdf [x]

一个点小于距离圆心的距离x的概率。假设圆的半径为R.

显然,如果x为零,则cdf [0] = 0

显然,如果x是R,那么cdf [R] = 1

显然如果x = r那么cdf [r] =(Pi r ^ 2)/(Pi R ^ 2)

这是因为每个&#34;小区域&#34;在圆上具有相同的被挑选概率,因此概率与所讨论的区域成比例。并且距圆的中心距离为x的区域是Pi r ^ 2

所以cdf [x] = x ^ 2 / R ^ 2因为Pi相互抵消

我们有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 deg}

答案 12 :(得分:1)

半径与“半径”附近的点数之间存在线性关系,因此他需要使用半径分布,这也使得半径r附近的数据点数量与{成比例} {1}}。

答案 13 :(得分:1)

我曾经使用过这种方法: 这可能是完全没有优化的(即它使用了一个点数组,因此它对大圆圈无法使用),但足够随机分布。如果您愿意,可以跳过矩阵的创建并直接绘制。该方法是随机化落在圆内的矩形中的所有点。

bool[,] getMatrix(System.Drawing.Rectangle r) {
    bool[,] matrix = new bool[r.Width, r.Height];
    return matrix;
}

void fillMatrix(ref bool[,] matrix, Vector center) {
    double radius = center.X;
    Random r = new Random();
    for (int y = 0; y < matrix.GetLength(0); y++) {
        for (int x = 0; x < matrix.GetLength(1); x++)
        {
            double distance = (center - new Vector(x, y)).Length;
            if (distance < radius) {
                matrix[x, y] = r.NextDouble() > 0.5;
            }
        }
    }

}

private void drawMatrix(Vector centerPoint, double radius, bool[,] matrix) {
    var g = this.CreateGraphics();

    Bitmap pixel = new Bitmap(1,1);
    pixel.SetPixel(0, 0, Color.Black);

    for (int y = 0; y < matrix.GetLength(0); y++)
    {
        for (int x = 0; x < matrix.GetLength(1); x++)
        {
            if (matrix[x, y]) {
                g.DrawImage(pixel, new PointF((float)(centerPoint.X - radius + x), (float)(centerPoint.Y - radius + y)));
            }
        }
    }

    g.Dispose();
}

private void button1_Click(object sender, EventArgs e)
{
    System.Drawing.Rectangle r = new System.Drawing.Rectangle(100,100,200,200);
    double radius = r.Width / 2;
    Vector center = new Vector(r.Left + radius, r.Top + radius);
    Vector normalizedCenter = new Vector(radius, radius);
    bool[,] matrix = getMatrix(r);
    fillMatrix(ref matrix, normalizedCenter);
    drawMatrix(center, radius, matrix);
}

enter image description here

答案 14 :(得分:1)

圆中的面积元素是dA = rdr * dphi。额外的因素r摧毁了你随机选择r和phi的想法。虽然phi是平坦的,但r不是,但是1 / r是平的(即你比“牛眼”更容易击中边界)。

因此,要生成均匀分布在圆圈上的点,从平面分布中选择phi,从1 / r分布中生成r。

或者使用Mehrdad提出的蒙特卡罗方法。

修改

要在1 / r中选择随机r平面,您可以从区间[1 / R,无穷大]中选择一个随机x并计算r = 1 / x。然后r在1 / r中分布平坦。

计算随机phi从区间[0,1]中选择一个随机x并计算phi = 2 * pi * x。

答案 15 :(得分:0)

我不知道这个问题是否仍然适用于已经给出所有答案的新解决方案,但我自己也遇到了完全相同的问题。我试图与自己“推理”一个解决方案,我找到了一个。这可能与某些人已经在这里建议的相同,但无论如何它是:

为了使圆的表面的两个元素相等,假设相等的dr,我们必须有dtheta1 / dtheta2 = r2 / r1。将该元素的概率表达式写为P(r,theta)= P {r1 <1。为r r1 + dr,theta1&lt; THETA&LT; theta + dtheta1} = f(r,theta)* dr * dtheta1,并设置两个概率(对于r1和r2)相等,我们到达(假设r和theta是独立的)f(r1)/ r1 = f(r2 )/ r2 =常数,得到f(r)= c * r。其余的,确定常数c遵循f(r)为PDF的条件。

答案 16 :(得分:0)

程序员解决方案:

  • 创建位图(布尔值矩阵)。它可以大到你想要的大小。
  • 在该位图中绘制一个圆圈。
  • 创建圆点的查找表。
  • 在此查找表中选择随机索引。
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];
} // ()

答案 17 :(得分:0)

我仍然不确定确切的'(2 / R2)×r'但显而易见的是在给定单位'dr'中需要分配的点数,即r的增加将与r2成比例而不是河

检查这种方式...在某个角度θ和r(0.1r到0.2r)之间的点数,即r的分数和r(0.6r到0.7r)之间的点数如果使用则相等标准生成,因为两个间隔之间的差异仅为0.1r。但由于点之间覆盖的区域(0.6r至0.7r)将远大于0.1r至0.2r之间覆盖的区域,因此相同数量的点将在较大区域中稀疏地间隔开,这我假设您已经知道,所以函数生成随机点不能是线性的,而是二次的(因为需要在给定单位'dr'中分布的点数,即r的增加将与r2成比例而不是r),所以在这种情况下它将是二次方,因为在两个区间中我们有(0.1r)的delta必须是某个函数的平方,所以它可以作为点的线性生成的种子值(因为后面这个种子在sin和cos函数中线性使用),所以我们知道,dr必须是二次值并且要使这个种子成为二次方,我们需要从r的平方根本身产生这个值,而不是r本身,我希望这会使它更加清晰。

答案 18 :(得分:0)

这么有趣的问题 随着距离轴原点的距离增加而选择点的概率的基本原理在上面被多次解释。我们通过取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])

enter image description here

答案 19 :(得分:0)

您也可以使用自己的直觉。

圆的面积为pi*r^2

对于r=1

这给了我们pi的面积。让我们假设我们有某种函数f,它会在一个圆内均匀地扭曲N=10点。这里的比率是10 / pi

现在我们将面积和点数加倍

对于r=2N=20

这给出4pi的面积,并且比率现在为20/4pi10/2pi。半径越大,该比率就越小,因为它的增长是二次方且N呈线性比例。

要解决这个问题,我们只能说

x = r^2
sqrt(x) = r

如果您要像这样在极坐标中生成向量

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

更多点将落在中心附近。

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

length不再是均匀分布的,但是矢量现在将是均匀分布的。

答案 20 :(得分:-1)

1)选择介于-1和1之间的随机X.

var X:Number = Math.random() * 2 - 1;

2)使用圆公式,计算给定X和半径为1的Y的最大值和最小值:

var YMin:Number = -Math.sqrt(1 - X * X);
var YMax:Number = Math.sqrt(1 - X * X);

3)在这些极端之间选择一个随机Y:

var Y:Number = Math.random() * (YMax - YMin) + YMin;

4)将您的位置和半径值合并到最终值中:

var finalX:Number = X * radius + pos.x;
var finalY:Number = Y * radois + pos.y;