CJam, 28 27 байтов
PP+mr_mc\ms]1.mrmqf*"(,)".\
Это решение не основано на отклонении. Я генерирую точки в полярных координатах, но с неравномерным распределением радиусов для достижения равномерной плотности точек.
Проверьте это здесь.
объяснение
PP+ e# Push 2π.
mr_ e# Get a random float between 0 and 2π, make a copy.
mc\ e# Take the cosine of one copy and swap with the other.
ms] e# Take the sine of the other copy and wrap them in an array.
e# This gives us a uniform point on the unit circle.
1.mr e# Get a random float between 0 and 1.
mq e# Take the square root. This is the random radius.
f* e# Multiply x and y by this radius.
"(,)".\ e# Put the resulting numbers in the required format.
Почему это работает? Рассмотрим узкое кольцо радиуса r
и (малой) ширины dr
. Площадь приблизительно равна 2π*r*dr
(если кольцо узкое, внутренняя и внешняя окружность почти идентичны, и кривизну можно игнорировать, так что площадь можно рассматривать как площадь прямоугольника с длинами сторон окружности и шириной окружности). кольцевое пространство). Таким образом, площадь увеличивается линейно с радиусом. Это означает, что мы также хотим линейное распределение случайных радиусов, чтобы достичь постоянной плотности (при удвоенном радиусе площадь заполнения должна быть в два раза больше, поэтому нам нужно в два раза больше точек).
Как мы генерируем линейное случайное распределение от 0 до 1? Давайте сначала посмотрим на дискретный случай. Скажем, у нас есть желаемое распределение в 4 значения, например {0.1, 0.4, 0.2, 0.3}
(т.е. мы хотим 1
быть в 4 раза чаще 0
и вдвое чаще 2
; мы хотим в 3
три раза чаще 0
):
Как выбрать одно из четырех значений с желаемым распределением? Мы можем сложить их, выбрать равномерно случайное значение между 0 и 1 по оси Y и выбрать сегмент в этой точке:
Хотя есть и другой способ визуализации этого выбора. Вместо этого мы могли бы заменить каждое значение распределения на накопление значений до этого момента:
А теперь мы рассматриваем верхнюю строку этой диаграммы как функцию f(x) = y
и инвертируем ее, чтобы получить функцию , которую мы можем применить к равномерно случайному значению в :g(y) = f-1(y) = x
y ∈ [0,1]
Круто, так как можно использовать это для генерации линейного распределения радиусов? Это дистрибутив, который мы хотим:
Первым шагом является накопление значений распределения. Но распределение непрерывно, поэтому вместо суммирования по всем предыдущим значениям мы берем интеграл от 0
до r
. Мы можем легко решить это аналитически . Тем не менее, мы хотим, чтобы это было нормализовано, то есть умножило это на константу так, чтобы это дало максимальное значение , так что мы действительно хотим :∫0r r dr = 1/2 r2
1
r
r2
И, наконец, мы инвертируем это, чтобы получить функцию, к которой мы можем применить единообразное значение [0,1]
, что мы снова можем сделать аналитически: просто r = √y
, где y
случайное значение:
Это довольно полезный метод, который часто можно использовать для точной генерации простых дистрибутивов (он работает для любого дистрибутива, но для сложных двух последних шагов, возможно, придется решать численно). Однако я бы не стал использовать его в данном конкретном случае в производственном коде, потому что квадратный корень, синус и косинус чрезмерно дороги: использование алгоритма на основе отклонения в среднем намного быстрее, потому что оно требует только сложения и умножения.