web-dev-qa-db-ja.com

円内にランダムな点を生成する(均一に)

半径Rの円内に一様にランダムなポイントを生成する必要があります。

間隔[0 ...2π)で一様にランダムな角度、および間隔[0 ...R)で一様にランダムな半径を選択するだけで、これは、指定された2つの半径の場合、半径の大きい方のポイントよりも半径の小さい方のポイントが互いに近くなるため、中心に向かうポイントが多くなります。

これに関するブログエントリを見つけました こちら しかし、私は彼の理由を理解していません。私はそれが正しいと思いますが、私は本当に彼がどこから来るのか理解したいです(2 /R2)×rおよび彼が最終解を導き出す方法


更新:この質問を投稿してから7年経った今でも、平方根アルゴリズムの背後にある数学に関する実際の質問について満足のいく答えを受け取っていませんでした。だから私は自分で答えを書くのに一日費やしました。 私の答えへのリンク

184
aioobe

半径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倍の点を見つける必要があることを意味します。




円の円周(2πr)はrで線形に成長し、ランダムポイントの数はrで線形に成長します。つまり、目的の 確率密度関数 (PDF)は直線的に増加します。 PDFの面積は1でなければならず、最大半径は1なので、


そのため、ランダム値の望ましい密度がどのように見えるかを知っています。今:から1の間の一様なランダム値しか持っていない場合、このようなランダム値をどのように生成しますか?

逆変換サンプリング というトリックを使用します

  1. PDFから、 累積分布関数 (CDF)を作成します
  2. y = xに沿ってこれをミラーリングします
  3. 結果の関数を0〜1の均一な値に適用します。

複雑に聞こえますか?直感を伝える小さなサイドトラックが付いた黄色のボックスを挿入しましょう。

次の分布でランダムポイントを生成するとします。

あれは

  • 1と2の間で均一に1/5のポイント、および
  • 2〜3の間で均一に4/5のポイント。

CDFは、その名前が示すとおり、PDFの累積バージョンです。直感的に:PDF(x)はランダム値の数at xを記述しますが、CDF(x)はランダム値の数より小さいx

この場合、CDFは次のようになります。

これがどのように役立つかを見るために、均一に分布した高さで左から右に弾丸を撃つことを想像してください。弾丸がラインに当たると、地面に落ちます。

地上の弾丸の密度が望ましい分布にどのように対応しているかを確認してください!もうすぐだ!

問題は、この関数の場合、y軸がoutputであり、x軸がinputであるということです。 「弾丸を地面からまっすぐ撃つ」ことしかできません!逆関数が必要です!

これが、すべてをミラーリングする理由です。 xyになり、yxになります。

これをCDF-1。目的の分布に従って値を取得するには、CDFを使用します-1(ランダム())。

…そのため、PDFが2に等しいランダムな半径値の生成に戻りますバツ

ステップ1:CDFの作成:

実数で作業しているため、CDFはPDFの積分として表されます。

CDFバツ)=∫2バツ = バツ2

ステップ2:y = xに沿ってCDFをミラーリングします:

数学的には、これはxyを交換し、yを解くことになります:

CDFyバツ2
スワップ:xy2
解決策:y =√バツ
CDF-1y =√バツ

ステップ3:結果の関数を0〜1の均一な値に適用する

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

これが、導出するために設定したものです:-)

65
aioobe

アルキメデスのようにこれにアプローチしましょう。

| AB | = | BC |の三角形ABC内でポイントを均一に生成するにはどうすればよいですか?平行四辺形ABCDに拡張して、これを簡単にしましょう。 ABCDでポイントを均一に生成するのは簡単です。 AB上のランダムポイントXとBC上のYを一様に選択し、XBYZが平行四辺形になるようにZを選択します。元の三角形で一様に選択されたポイントを取得するには、ADCに表示されるポイントをACに沿ってABCに折り返します。

次に、円について考えます。極限では、原点にBがあり、円周上のAとCが互いにゼロに近い無限の多くの等辺三角形ABCと考えることができます。角度シータを選択するだけで、これらの三角形の1つを選択できます。そのため、スライバーABCのポイントを選択して、中心からの距離を生成する必要があります。再び、ABCDに拡張します。ここで、Dは円の中心から半径の2倍になりました。

上記の方法を使用すると、ABCDでランダムポイントを選択するのは簡単です。 ABのランダムポイントを選択します。 BCでランダムにランダムなポイントを選択します。すなわち。 [0、R]で一様に乱数xとyのペアを選択し、中心からの距離を指定します。三角形は細いスライバーなので、ABとBCは基本的に平行です。したがって、点Zは、単に原点からの距離x + yです。 x + y> Rの場合、折り返します。

R = 1の完全なアルゴリズムを次に示します。かなり簡単だということに同意していただければ幸いです。トリガーを使用しますが、拒否サンプリングとは異なり、所要時間と必要な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

181
sigfpe

これが高速でシンプルなソリューションです。

範囲(0、1)の2つの乱数、つまり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)に戻る三角形ができます。これらの変換はすべて、密度を均一に変更します。あなたがやったことは、三角形の中のランダムな点を一様に選び、プロセスを逆にして円の中の点を取得することです。

26
btilly

半径の逆二乗に比例する点密度に注意してください。したがって、[0, r_max]からrを選択する代わりに、[0, r_max^2]から選択して、座標を次のように計算します。

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

これにより、ディスク上の均一なポイント分布が得られます。

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

18
Libor

このように考えてください。 1つの軸が半径で、1つが角度である長方形がある場合、半径0に近いこの長方形内のポイントを取ります。これらはすべて、原点に非常に近く(円上で互いに近く)なります。半径Rに近いポイント、これらはすべて円のエッジの近くに(つまり、互いに離れて)落ちます。

これにより、なぜこの動作が発生しているのかがわかります。

そのリンクから導き出される係数は、円にマッピングされた後、半径に依存しないように調整する必要がある長方形内の対応する領域の量を示します。

編集:だから彼があなたが共有するリンクに書いているのは、「累積分布の逆数を計算することで簡単にでき、r:が得られる」ということです。

ここでの基本的な前提は、目的の確率密度関数の累積分布関数の逆関数でユニフォームをマッピングすることにより、ユニフォームから目的の分布を持つ変数を作成できることです。どうして?とりあえずそれを当然のことと思ってください、しかしこれは事実です。

これが数学の私の直観的な説明です。 rに関する密度関数f(r)は、r自体に比例する必要があります。この事実を理解することは、基本的な計算の本の一部です。極域要素のセクションを参照してください。他のポスターがこれについて言及しています。

したがって、それをf(r) = C * r;と呼びます。

これがほとんどの作業であることがわかりました。これで、f(r)は確率密度になるはずなので、f(r)を区間(0、R)で積分すると、C = 2になることが簡単にわかります。/R ^ 2(これは読者向けの演習です。)

したがって、f(r) = 2 * r/R ^ 2

OK、それでリンクに数式を取得できます。

次に、最終部分は(0,1)の一様ランダム変数uから得られます。この所望の密度f(r)から累積分布関数の逆関数でマッピングする必要があります。これがなぜそうなのかを理解するには、おそらくパポウリスのような高度な確率テキストを見つける必要があります(または、それを自分で導出します)。

f(r)を統合すると、F(r) = r ^ 2/R ^ 2が得られます

この逆関数を見つけるには、u = r ^ 2/R ^ 2を設定し、rを解きます。これにより、r = R * sqrt(u)が得られます。

これは直感的にも理にかなっています。u= 0はr = 0にマップする必要があります。また、u = 1はr = Rにマップします。また、理にかなってリンクに一致する平方根関数を使用します。

12
Chris A.

単純な解決策が機能しないのは、円の中心に近いポイントほど高い確率密度を与えるためです。言い換えれば、半径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]間の半径を選択する確率は、0からrまでのp(r) drの積分です(これは、より小さい半径のすべての確率を加算するためです)。したがって、integral(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

注:問題を考えるもう1つのより直感的な方法は、半径rの各円に、その円周上の点の数の割合に等しい確率密度を与えようとしていると想像することです。したがって、半径rの円は、円周上に2 * pi * rの「点」を持ちます。ポイントの総数はpi * R ^ 2です。したがって、円rに(2 * pi * r)/(pi * R ^ 2)= 2 * r/R ^ 2に等しい確率を与える必要があります。これは理解しやすく直感的ですが、数学的にはそれほど適切ではありません。

8
user502248

Ρ(半径)とφ(方位角)を、円内の任意の点の極座標に対応する2つのランダム変数とします。点が均一に分布している場合、ρとφの分布関数は何ですか?

任意の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が[0、1]で一様であるランダム変数sqrt(X)には、この正確なCDFがあることに注意してください(P [sqrt(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)

あなたが取得します

enter image description here

7
Pommy

それは本当に「一様にランダム」という意味に依存します。これは微妙なポイントであり、次のWikiページで詳細を読むことができます。 http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29 「一様にランダム」への解釈は異なる答えを与えます!

someの意味で一様にランダムであるにもかかわらず、ポイントの選択方法に応じて、分布は変化する可能性があります。

ブログのエントリは、次の意味で一様にランダムにしようとしているようです:同じ中心の円のサブサークルを取得すると、その領域にポイントが落ちる確率は、地域。それは、2D領域の「均一にランダムな」という現在の標準的な解釈に従うことを試みています それらに定義された領域 :任意の領域(領域が明確に定義された)に落ちる点の確率その地域のエリアに。

7
Aryabhatta

以下は、半径numの円からradランダムポイントを生成するPythonコードです。

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()
6
krishnab

この場合、極座標を使用することは問題を複雑にする方法だと思います。長さ2Rの辺を持つ正方形にランダムなポイントを選択し、(x,y)のようなポイントx^2+y^2<=R^2を選択すると、はるかに簡単になると思います。

3
ascanio

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

前のソリューションに基づいて https://stackoverflow.com/a/5838055/5224246 from @sigfpe

3
bolec_kolec

まず、cdf [x]を生成します

点が円の中心からの距離xよりも小さい確率。円の半径がRであると仮定します。

明らかにxがゼロの場合、cdf [0] = 0

明らかにxがRの場合、cdf [R] = 1

明らかにx = rの場合、cdf [r] =(Pi r ^ 2)/(Pi R ^ 2)

これは、円上の各「小さな領域」が選択される確率が同じであるためです。したがって、確率は問題の領域に比例します。そして、円の中心から距離xが与えられた面積はPi r ^ 2

piは互いに打ち消し合うため、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] ]

Cdfを0〜1の乱数に置き換えることができます

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}を取得します

2
Steven Siew

半径と、その半径に「近い」ポイントの数との間には線形関係があるため、彼は、半径rに近いデータポイントの数をrに比例させる半径分布を使用する必要があります。

1
recursive

私は一度この方法を使用しました:これは完全に最適化されていない可能性があります(つまり、ポイントの配列を使用しているため、大きな円では使用できません)。マトリックスの作成をスキップして、必要に応じて直接描画することもできます。この方法は、円の内側にある長方形内のすべてのポイントをランダム化することです。

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

1
Marino Šimić

円内の面積要素はdA = rdr * dphiです。その余分な要因rは、rとphiをランダムに選択するというアイデアを破壊しました。 phiはフラットに分布していますが、rはフラットではありませんが、1/rでフラットです(つまり、「ブルズアイ」よりも境界に当たる可能性が高い)。

したがって、円上に均等に分布する点を生成するには、フラット分布からファイを選択し、1/r分布からrを選択します。

または、Mehrdadが提案したモンテカルロ法を使用します。

編集

1/rでランダムなrフラットを選択するには、間隔[1/R、infinity]からランダムなxを選択し、r = 1/xを計算します。 rは、1/rでフラットに分布します。

ランダムなファイを計算するには、間隔[0、1]からランダムなxを選択し、phi = 2 * pi * xを計算します。

1

プログラマーソリューション:

  • ビットマップ(ブール値のマトリックス)を作成します。必要なだけ大きくすることができます。
  • そのビットマップに円を描きます。
  • 円のポイントのルックアップテーブルを作成します。
  • このルックアップテーブルでランダムインデックスを選択します。
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];
} // ()
0
selalerer

直感を使うこともできます。

円の面積はpi*r^2です

r=1の場合

これにより、piの領域が得られます。 N=10ポイントを円の中に均一に分散させる何らかの関数fがあると仮定しましょう。ここでの比率は10 / piです

面積とポイント数を倍にします

r=2およびN=20の場合

これにより、4piの領域が得られ、比率は20/4piまたは10/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はもはや均一に分布していませんが、ベクトルは均一に分布するようになりました。

0
Maik Klein

正確な「(2/R2)×r」についてはまだわかりませんが、明らかなのは、指定された単位「dr」で分散する必要があるポイントの数です。つまり、rの増加はrではなくr2に比例します。

この方法を確認してください...ある角度シータでr(0.1rから0.2r)の間のポイントの数。つまり、rの割合とr(0.6rから0.7r)の間のポイントの数は、標準生成を使用する場合は等しくなります。 2つの間隔の差はわずか0.1rであるためです。しかし、ポイント間(0.6rから0.7r)でカバーされるエリアは、0.1rから0.2rでカバーされるエリアよりもはるかに大きいため、等しい数のポイントは、より広いエリアでまばらに間隔が空けられます。ランダムポイントを生成するには、線形ではなく2次でなければなりません(指定された単位「dr」に分布する必要があるポイントの数、つまりrの増加はrではなくr2に比例するため)、この場合は逆になります二次式、両方の間隔にあるデルタ(0.1r)は関数の2乗でなければならないため、ポイントの線形生成のシード値として機能できるため(後書きのため、このシードはsinおよびcos関数で線形に使用されます)、 drは2次値でなければならず、このシードを2次値にするには、r自体ではなくrの平方根からこの値を生成する必要があります。

0
cheesefest

この質問がすべての答えがすでに与えられた新しい解決策のためにまだ開かれているかどうかはわかりませんが、私はたまたままったく同じ質問に直面しました。私は自分自身で解決策を「理性」しようとしましたが、それを見つけました。すでにここで提案されているものと同じかもしれませんが、とにかくここにあります:

円の表面の2つの要素を等しくするには、drが等しいと仮定して、dtheta1/dtheta2 = r2/r1が必要です。その要素の確率の表現をP(r、theta)= P {r1 <r <r1 + dr、theta1 <theta <theta + dtheta1} = f(r、theta)* dr * dtheta1として記述し、2つを設定する(r1とr2の)確率が等しい場合、(rとthetaが独立していると仮定して)f(r1)/ r1 = f(r2)/ r2 = constantに到達し、f(r) = cが得られます* r。そして、残りは、定数cを決定することは、PDFであるf(r)の条件に従います。

0
arsaKasra

このような楽しい問題。
軸原点からの距離が増加するにつれて、ポイントが選択される確率が低くなる理由は、上記で複数回説明されています。 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

0
AChervony