Генерировать случайную точку внутри круга (равномерно)

Мне нужно создать равномерно случайную точку в пределах круга радиуса R.

Я понимаю, что просто выбирая равномерно случайный угол в интервале [0 ... 2π) и равномерно случайный радиус в интервале (0 ... R) Я бы закончил с большим количеством точек к центру, так как для двух заданных радиусов точки меньшего радиуса будут ближе друг к другу, чем для точек большего радиуса.

Я нашел запись в блоге это здесьЯ бы очень хотел понять, откуда он получает (2/R2r и как он получает окончательное решение.


обновление: через 7 лет после публикации этого вопроса я все еще не получил удовлетворительного ответа на фактический вопрос о математике алгоритма квадратного корня. Так я провел день сам пишу ответ. ссылка на мой ответ.

21 ответов


давайте как Архимед бы.

как мы можем генерировать точку равномерно в треугольнике ABC, где / AB / =|BC/? Давайте упростим это, распространившись на параллелограмм ABCD. Легко генерировать точки равномерно в ABCD. Мы равномерно выбираем случайную точку X на AB и Y на BC и выбираем Z такой, что XBYZ является параллелограммом. Чтобы получить равномерно выбранную точку в исходном треугольнике, мы просто складываем все точки, которые появляются в АЦП, обратно в ABC вдоль ПЕРЕМЕННЫЙ ТОК.

Теперь рассмотрим кругу. В пределе мы можем думать о нем как о бесконечном множестве изоцелевых треугольников ABC с B в начале и A и C на окружности, исчезающе близких друг к другу. Мы можем выбрать один из этих треугольников, просто выбрав угол тета. Поэтому теперь нам нужно сгенерировать расстояние от центра, выбрав точку в Щепке ABC. Опять же, распространитесь на ABCD, где D теперь вдвое больше радиуса от центра круга.

выбор случайной точки в ABCD используя описанный выше метод. Выберите случайную точку на AB. Равномерно выберите случайную точку на BC. То есть. выберите пару случайных чисел X и y равномерно на [0, R], дающих расстояния от центра. Наш треугольник представляет собой тонкую полоску, поэтому 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


как создать случайную точку в пределах круга радиуса R:

r = R * sqrt(random())
theta = random() * 2 * PI

(если random() дает значение между 0 и 1 равновероятно)

если вы хотите преобразовать это в Декартовые координаты, вы можете сделать

x = r * cos(theta)
y = r * sin(theta)


почему sqrt(random())?

давайте посмотрим на математику, которая приводит к sqrt(random()). Предположим для простоты, что мы работаем с единичным кругом, т. е. R = 1.

среднее расстояние между точками должно быть одинаковым независимо от того, как далеко от центра мы смотрим. Это означает, например, что, глядя на периметр круга с окружностью 2, мы должны найти в два раза больше точек, чем количество точек на периметре круга с окружностью 1.


                

С длины окружности (2πr) растет линейно с r, из этого следует, что количество случайных точек должно линейно расти с r. Другими словами, желаемое функция плотности вероятности (PDF) растет линейно. Поскольку PDF должен иметь площадь равную 1, а максимальный радиус равен 1, то есть


                

таким образом, мы знаем, как должна выглядеть желаемая плотность наших случайных значений. Теперь:как мы генерируем такое случайное значение, когда все мы имеют равномерное случайное значение между 0 и 1?

мы используем трюк под названием выборка обратного преобразования

  1. из PDF создайте кумулятивная функция распределения (CDF)
  2. зеркало это вдоль y = x
  3. применить полученную функцию к равномерному значению от 0 до 1.

звучит сложно? Позвольте мне вставить желтую коробку с небольшим количеством боковой путь, который передает интуицию:

Предположим, мы хотим создать случайную точку со следующим распределением:

что это

  • 1/5 из пунктов равномерно между 1 и 2, и
  • 4/5 точек равномерно между 2 и 3.

CDF, как следует из названия, является накопительной версией PDF. Интуитивно: Пока PDF (x) описывает количество случайных значений на x, CDF (x) описывает количество случайных значений меньше, чем x.

в этом случае CDF будет выглядеть так:

чтобы увидеть, как это полезно, представим, что мы стреляем пулями слева направо на равномерно распределенных высот. Когда пули попадают в линию, они падают на землю:--10-->

посмотрите, как плотность пуль на земле соответствует нашему желаемому распределению! Мы почти на месте!

проблема в том, что для этой функции y ось -выход и x ось -вход. Мы можем только "стрелять пулями с земли прямо вверх"! Нам нужна обратная функция!

вот почему мы отражаем все это; x становится y и y становится x:

мы называем это CDF-1. Чтобы получить значения в соответствии с желаемым распределением, мы используем CDF-1(random ()).

...Итак, вернемся к генерации случайных значений радиуса, где наш PDF равен 2x.

Шаг 1: Создайте CDF:

Поскольку мы работаем с reals, CDF выражается как Интеграл PDF.

CDF(x) = ∫ 2x = x2

Шаг 2: Зеркало CDF вдоль y = x:

математически это сводится к замене x и y и решения для y:

CDF: y = x2
Обмен:x = y2
Решить:y = √x
CDF-1: y = √x

Шаг 3: примените полученную функцию к равномерному значению между 0 и 1

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

что мы намеревались вывести: -)


вот быстрое и простое решение.

выбрать два случайных числа в диапазоне (0, 1), а именно a и b. Если b < a, поменять их местами. Ваша точка зрения (b*R*cos(2*pi*a/b), b*R*sin(2*pi*a/b)).

вы можете думать об этом решении следующим образом. Если взять круг, вырезать его, а затем выпрямить, получится прямоугольный треугольник. Масштабируйте этот треугольник вниз, и у вас будет треугольник из (0, 0) до (1, 0) to (1, 1) и (0, 0). Все эти преобразования измените плотность равномерно. То, что вы сделали, - это равномерно выбрать случайную точку в треугольнике и обратить процесс, чтобы получить точку в круге.


обратите внимание на плотность точки пропорционально обратному квадрату радиуса, следовательно, вместо выбора r С [0, r_max] С [0, r_max^2], затем вычислите свои координаты как:

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

это даст вам равномерное распределение точек на диске.

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


подумайте об этом таким образом. Если у вас есть прямоугольник, где одна ось-радиус, а одна-угол, и вы берете точки внутри этого прямоугольника, которые находятся рядом с радиусом 0. Все они будут падать очень близко к началу (то есть близко друг к другу по кругу.) Однако точки вблизи радиуса R все они будут падать вблизи края круга (то есть далеко друг от друга.)

Это может дать вам некоторое представление о том, почему вы получаете такое поведение.

фактор это выведено на этой ссылке говорит вам, сколько соответствующей области в прямоугольнике необходимо настроить, чтобы не зависеть от радиуса, как только он сопоставлен с кругом.

Edit: Итак, то, что он пишет в ссылке, которую вы разделяете, - "это достаточно легко сделать, вычисляя обратное кумулятивное распределение, и мы получаем для r:".

основная предпосылка здесь заключается в том, что вы можете создать переменную с желаемым распределением из равномерного, сопоставив равномерное обратным функция кумулятивной функции распределения желаемой функции плотности вероятности. Почему? Просто примите это как должное, но это факт.

вот мой somehwat интуитивное объяснение математики. Функция плотности f (r) по отношению к r должна быть пропорциональна самой r. Понимание этого факта является частью любой основной книги по исчислению. См. разделы об элементах полярной области. Некоторые другие плакаты упоминали об этом.

поэтому мы назовем его f (r) = C*r;

это, оказывается, большая часть работы. Теперь, поскольку F(r) должна быть плотностью вероятности, вы можете легко увидеть,что, интегрируя f (r) через интервал (0, R), вы получите, что C = 2/R^2 (это упражнение для читателя.)

таким образом, f(r) = 2*r/r^2

OK, так вот как вы получаете формулу в ссылке.

тогда конечная часть идет от равномерной случайной величины u в (0,1), которую вы должны отобразить обратной функцией кумулятивного функция распределения от этой желаемой плотности 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 map to r = R. Также, он идет по функции квадратного корня, которая имеет смысл и соответствует ссылке.


Это действительно зависит от того, что вы подразумеваете под 'абсолютно случайная'. Это тонкий момент, и вы можете прочитать больше об этом на странице wiki здесь:http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29, где одна и та же проблема, давая разные интерпретации "равномерно случайным" дает разные ответы!

в зависимости от того, как вы выбираете точки, распределение может варьироваться, даже если они равномерно случайны в некоторые чувство.

похоже, что запись в блоге пытается сделать ее равномерно случайной в следующем смысле: если вы берете подпругу круга с тем же центром, то вероятность того, что точка падает в этой области, пропорциональна площади области. Это, я считаю, пытается следовать теперь стандартной интерпретации "равномерно случайных" для 2D-областей с области, определенные на них: вероятность падения точки в любой области (с областью хорошо defined) пропорциональна площади этой области.


причина, по которой наивное решение не работает, заключается в том, что оно дает более высокую плотность вероятности точкам ближе к центру круга. Другими словами, окружность с радиусом 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 * константа dr) = r^2/2 * константа. Это должно быть равно r^2/R^2, поэтому Константа = 2 / R^2. Таким образом, у вас есть распределение вероятностей p(r) = r * 2/R^2

Примечание:


вот мой код 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()

пусть ρ (радиус) и φ (Азимут) - две случайные величины, соответствующие полярным координатам произвольной точки внутри круга. Если точки распределены равномерно, то какова функция распределения ρ и φ?

для любого r: 0

P[ρ 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(φ)

не могу сопротивляться сообщение кода python для R=1.

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)

вы получите


решение на 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

на основе решения previus https://stackoverflow.com/a/5838055/5224246 от @sigfpe


Я думаю, что в этом случае использование полярных координат является способом усложнить проблему, было бы намного проще, если вы выберете случайные точки в квадрат со сторонами длины 2R, а затем выберите точки (x,y) такое, что x^2+y^2<=R^2.


Сначала мы генерируем 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

Итак, 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] ]

Теперь мы можем заменить 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}


существует линейная зависимость между радиусом и количеством точек "рядом" с этим радиусом, поэтому ему нужно использовать распределение радиуса, которое также делает количество точек данных рядом с радиусом r пропорционально r.


я использовал один раз этот метод: Это может быть полностью неоптимизировано (т. е. он использует массив точек, поэтому его нельзя использовать для больших кругов), но дает достаточно случайное распределение. Вы можете пропустить создание матрицы и рисовать напрямую, если хотите. Метод состоит в рандомизации всех точек в прямоугольнике, которые попадают внутрь круга.

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


элемент площади в окружности dA=rdr*dphi. Этот дополнительный фактор R разрушил вашу идею случайно выбрать r и phi. В то время как phi распределен плоско, r нет, но плоско в 1/r (т. е. вы с большей вероятностью попадете в границу, чем "глаз быка").

поэтому для генерации точек, равномерно распределенных по окружности, выберите phi из плоского распределения и r из распределения 1/R.

альтернативно используйте метод Монте-Карло, предложенный Мехрдад.

редактировать

чтобы выбрать случайную R-квартиру в 1/r, вы можете выбрать случайный x из интервала [1/R, бесконечность] и вычислить r=1 / x. r затем распределяется плоско в 1/r.

для вычисления случайного phi выберите случайный x из интервала [0, 1] и вычислите phi=2*pi*x.


Я не знаю, открыт ли этот вопрос для нового решения со всеми уже данными ответами, но я сам столкнулся с точно таким же вопросом. Я попытался "рассуждать" с самим собой для решения, и я нашел его. Это может быть то же самое, что некоторые уже предлагали здесь, но в любом случае здесь:

чтобы два элемента поверхности круга были равны, предполагая равные dr, мы должны иметь dtheta1/dtheta2 = r2 / r1. Пишу выражение вероятность этого элемента как P(R, тета) = Р{ Р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];
} // ()

Я все еще не уверен в точном "(2/R2)×r", но очевидно, что количество точек, необходимых для распределения в данной единице "dr", т. е. увеличение r будет пропорционально r2, а не r.

проверьте здесь...количество точек под некоторым углом тета и между r (0,1 r до 0,2 r), т. е. доля r и количество точек между r (0,6 r до 0,7 r) будут равны, если вы используете стандартную генерацию, так как разница составляет всего 0,1 r между двумя интервалами. но так как область покрытый между точками (от 0,6 r до 0,7 r) будет намного больше, чем площадь, покрытая от 0,1 r до 0,2 r, равное количество точек будет разнесено в большей области, это я предполагаю, что вы уже знаете, поэтому функция для генерации случайных точек должна быть не линейной, а квадратичной (так как количество точек, необходимых для распределения в данной единице "dr", т. е. увеличение r будет пропорционально r2, а не r), поэтому в этом случае она будет обратной квадратичной, так как Дельта у нас есть (0.1 r) в обоих интервалы должны быть квадратными какой-либо функции, поэтому он может выступать в качестве начального значения для линейной генерации точек (с послесловия, это семя используется линейно в Sin и cos) функция, так что мы знаем, доктор должен быть квадратным стоимость и чтобы это семя квадратичной, мы должны исходить из этого значения квадратный корень из р не р себя, я надеюсь, что это делает его немного более ясным.


такая забавная проблема.
Обоснование вероятности того, что выбранная точка будет снижаться по мере увеличения расстояния от начала оси, объясняется в несколько раз выше. Мы учитываем это, беря корень U[0,1]. Вот общее решение для положительного r в Python 3.

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


вы также можете использовать ваши интуиция.

площадь круга pi*r^2

на r=1

это дает нам площадь pi. Давайте предположим, что у нас есть какая-то функция fчто бы равномерно распределить N=10 точки внутри круга. Соотношение здесь 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 больше не равномерно распределен, но вектор теперь будет равномерно распределенный.


1) Выберите случайный X между -1 и 1.

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

2) используя формулу круга, вычислите максимальное и минимальное значения Y, учитывая, что X и радиус 1:

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;