Рандомизировать точки на диске


14

Я где-то читал о кругах, и только сейчас узнал о дисках ( это на самом деле довольно распространенная концепция ) и подумал о Codegolf.

Ваша задача - рандомизировать точку / несколько точек на диске с радиусом 1.

Правила:

  • Все точки должны иметь одинаковую вероятность, чтобы быть сгенерированным
  • Должны использоваться координаты с плавающей точкой; минимальное требование - два десятичных знака (например, точки (0.12, -0.45)или (0.00, -1.00)действительны)
  • Вы получите -20 байт, если ваша программа на самом деле отображает ограничивающий круг и точки, сгенерированные в нем. Координаты по-прежнему должны быть действительными, но не отображаться, а размер сгенерированного изображения должен быть не менее 201 на 201 пикселей.
  • Вы получите -5 байт, если ваша программа берет количество точек, которые будут сгенерированы как ввод на стандартном вводе
  • Если вы решили не наносить ограничивающий круг и точки, ваша программа должна вывести точки, сгенерированные в формате (x, y)или (x,y)на стандартном выводе.
  • Если вы решили взять количество сгенерированных точек в качестве входных данных, но не строить их на графике - ваша программа должна вывести все рандомизированные точки в указанном выше формате с одним пробелом или без него между

Кратчайшая подача в байтах побеждает!


1
@sweerpotato Да, пожалуйста, укажите, что все точки в и на круге действительны. Я не понимал, что вы имели в виду оба. Кроме того, этот вопрос выглядит так, будто он лучше подходит для соревнований по коду, чем для конкурса популярности, но это только мое мнение.
Коул

5
« Делай XYZ творчески » - это классический Bad Popcon Question ™. То, что один человек считает творческим, это то, что другой человек считает очевидным.
Питер Тейлор

Из любопытства, почему для графиков требуется выходное разрешение 201x201 пикселей?
JohnE

@JohnE Я предложил 201x201 пикселей, так как он соответствует требуемой точности с точностью до 2 десятичных знаков
trichoplax

Можем ли мы вывести координаты в виде комплексных чисел? Например: 0.3503082505747327+0.13499221288682994j.
orlp

Ответы:


5

Pyth, 26 - 5 = 21 байт

VQp(sJ*@OZ2^.n1*yOZ.l_1)eJ

Принимает количество координат для генерации на stdin и выводит их на stdout следующим образом:

(-0.5260190768964058, -0.43631187015380823)(-0.12127959509302746, -0.08556306418467638)(-0.26813756369750996, -0.4564539715526493)

Использует стратегию, аналогичную @ MartinBüttner, для генерации полярных координат и радиусов, за исключением того, что она использует сложное возведение в степень.


Вы можете удалить p, не так ли? Он просто меняет вывод на отдельные строки.
PurkkaKoodari

@ Pietu1998 Это не разрешено, смотрите комментарии к основному вопросу.
Орл

Ох, все в порядке.
PurkkaKoodari

16

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) = xy ∈ [0,1]

введите описание изображения здесь

Круто, так как можно использовать это для генерации линейного распределения радиусов? Это дистрибутив, который мы хотим:

введите описание изображения здесь

Первым шагом является накопление значений распределения. Но распределение непрерывно, поэтому вместо суммирования по всем предыдущим значениям мы берем интеграл от 0до r. Мы можем легко решить это аналитически . Тем не менее, мы хотим, чтобы это было нормализовано, то есть умножило это на константу так, чтобы это дало максимальное значение , так что мы действительно хотим :0r r dr = 1/2 r21rr2

введите описание изображения здесь

И, наконец, мы инвертируем это, чтобы получить функцию, к которой мы можем применить единообразное значение [0,1], что мы снова можем сделать аналитически: просто r = √y, где yслучайное значение:

введите описание изображения здесь

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


1
Очень хорошее объяснение!
Sweerpotato

2
Ммм фотографии: D
бета-распад

12

Mathematica, 68 44 - 20 = 24 байта

Большое спасибо Дэвиду Каррахеру за сообщение о том RandomPoint, что сэкономило 24 (!) Байта. Mathematica имеет имеет встроенный для всего.

Graphics@{Circle[],Point@RandomPoint@Disk[]}

Это показывает точку и ограничивающий круг, чтобы претендовать на бонус:

введите описание изображения здесь

В результате получается векторное изображение, поэтому указание размера в 201x201 пикселей не имеет особого смысла, но по умолчанию оно отображается больше, чем это.


Как насчет Graphics[{Circle[], Point@RandomPoint@Disk[]}]?
DavidC

Будь моим гостем. Кроме того, чтобы сохранить 1 байт ...Graphics@{Circle[], Point@RandomPoint@Disk[]}
DavidC

@DavidCarraher Большое спасибо! :)
Мартин Эндер

Я не знаю синтаксиса Mathematica, но, конечно, вы можете сохранить еще один байт, удалив пробел после ,?
пушистый

@fluffy я уже сделал в опубликованной версии
Мартин Эндер

9

CJam, 31 26 байт

{];'({2dmr(_}2*@mhi}g',\')

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

Спасибо @ MartinBüttner за 3 байта!

Попробуйте онлайн в интерпретаторе CJam .

Как это устроено

{                  }g       Do:
 ];'(                         Clear the stack and push a left parenthesis.
     {      }2*               Do twice:
      2dmr                      Randomly select a Double between 0 and 2.
          (_                    Subtract 1 and push a copy.
               @              Rotate the copy of the first on top of the stack.
                mh            Compute the Euclidean norm of the vector consisting
                              of the two topmost Doubles on the stack.
                  i           Cast to integer.
                            If the result is non-zero, repeat the loop.
                     ',\    Insert a comma between the Doubles.
                        ')  Push a right parenthesis.

8

iKe , 53 51 байт

Ничего особенного, но я полагаю, у нас должно быть хотя бы одно графическое решение:

,(80+160*t@&{.5>%(x*x)+y*y}.+t:0N 2#-.5+?9999;cga;3)

участок

Попробуйте это в вашем браузере .

Изменить: я могу сбрить два байта, применяя подход @ MartinBüttner для изменения распределения полярных координат. Я думаю, что это также немного более прямо:

,(80*1+(%?c){x*(cos y;sin y)}'6.282*?c:9999;cga;3)

3
Если бы вы также нарисовали ограничивающий круг, вы бы получили право на -20.
orlp

1
У iKe есть растровая модель рисования, что делает это требование довольно несправедливым. Я думаю, что для визуализации аппроксимации ограничивающего круга потребуется чуть больше 20 символов.
JohnE

7

Perl, 59 байт

while(($x=1-rand 2)**2+($y=1-rand 2)**2>1){};print"($x,$y)"

Это просто простое решение: генерировать точки в квадрате и отбрасывать их слишком далеко. Моя особенная игра в гольф состоит в том, чтобы включить в условие домашние задания.

Изменить: В процессе игры в гольф, я нашел интересный способ печати случайных точек на круге .

use Math::Trig;$_=rand 2*pi;print"(",sin,",",cos,")"

7

Октава, 24 53 - 20 = 33 байта

polar([0:2e-3:1,rand]*2*pi,[ones(1,501),rand^.5],'.')

Генерирует 501 тэта-значения с равным интервалом плюс одно случайное число и масштабирует их все до [0..2π]. Затем генерирует 501 1 для радиуса круга плюс случайный радиус для точки и принимает квадратный корень, чтобы обеспечить равномерное распределение по диску. Затем строит все точки как полярные координаты.

введите описание изображения здесь


Вот быстрая демонстрация распределения (без единичного круга):

polar(2*pi*rand(99),rand(99).^.5,'.')

9801 баллов


5

Октава / Матлаб, 74 64 байта

Метод отклонения , 64 байта:

u=1;v=1;while u^2+v^2>1
u=rand;v=rand;end
sprintf('(%f,%f)',u,v)

Прямой метод , 74 байта (спасибо Martin Büttner за помощь в исправлении двух ошибок):

t=rand*2*pi;r=1-abs(1-sum(rand(2,1)));sprintf('(%f,%f)',r*cos(t),r*sin(t))

5

R, 99 95 81-20 = 79 75 61 байт

symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))

Используйте построение комплексного числа, чтобы построить x / y's из полярных координат. Взятие входных данных было немного дороже, и, вероятно, есть лучший способ сделать это. ylim Иxlim заключается в обеспечении весь круг строится иasp обеспечивает точки показаны под символом окружности.

Спасибо @jbaums и @flodel за экономию

Попробуй здесь


runif(9,0,1)можно упростить доrunif(9)
jbaums

@jbaums, спасибо ... одна из вещей, которые я всегда забываю :)
MickyT

Можно брить 14:symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))
Флодел

@flodel очень приятно, спасибо.
MickyT

Еще одна маленькая экономия: yliработает вместо ylim.
Jbaums

4

Обработка / Java 141 байт-20 = 121

требование минимального размера 201 * 201 требует, чтобы я включил setupметод, поскольку Processing.org по умолчанию имеет значение 200x200 :(

void setup(){noFill();size(201,201);}void draw(){float f=10,a=PI*2*random(),r=random();point(f+f*sin(a)*r,f+f*cos(a)*r);ellipse(f,f,f*2,f*2)}

Я не знал, что обработка / Java была разрешена, аккуратно!
Дж Аткин

4

QBasic, 138 байтов - 20 - 5 = 113

INPUT n
r=200
SCREEN 12
RANDOMIZE TIMER
CIRCLE(r,r),r
PAINT(r,r)
FOR i=1TO n
DO
x=RND*r*2
y=RND*r*2
LOOP UNTIL POINT(x,y)
PSET(x,y),1
NEXT

Принимает пользовательский ввод и рисует диск и очки. Протестировано на QB64 .

Это довольно простая стратегия «бросай в дартс». Суть в том, что «то, что прилипает», определяется не математически, а графически: белый диск наносится на черный фон, а затем случайно сгенерированные точки отклоняются до тех пор, пока они не станут черными. Сами точки нарисованы синим цветом (хотя трудно сказать, когда они единичные пиксели - нажмите на изображение, чтобы увеличить).


3

awk - 95 - 5 = 90

{
    for(;$1--;printf"("(rand()<.5?x:-x)","(rand()<.5?y:-y)")")
        while(1<(x=rand())^2+(y=rand())^2);
}

Так как я не был полностью уверен в части rand () <. 5, я провел некоторое тестирование дистрибутива с помощью этого сценария:

BEGIN{ srand() }
{ 
    split("0 0 0 0", s)
    split("0 0 0 0", a)

    for(i=$1; i--; )
    {
        while( 1 < r2 = ( x=rand() )^2 + ( y=rand() )^2 );

        x = rand()<.5 ? x : -x
        y = rand()<.5 ? y : -y

        ++s[ x>=0 ? y>=0 ? 1 : 4 : y>=0 ? 2 : 3 ]

        ++a[ r2>.75 ? 1 : r2>.5 ? 2 : r2>.25 ? 3 : 4]
    }

    print "sector distribution:"
        for(i in s) print "sector " i ": " s[i]/$1

    print "quarter area distribution:"
        for(i in a) print "ring " i ":   " a[i]/$1
}

который для входа 1e7 дает мне этот результат после того, как я выпил один или два раза за своим кофе:

1e7
sector distribution:
sector 1: 0.250167
sector 2: 0.249921
sector 3: 0.249964
sector 4: 0.249948
quarter area distribution:
ring 1:   0.24996
ring 2:   0.25002
ring 3:   0.250071
ring 4:   0.249949

что я думаю вполне нормально.

Небольшое объяснение:
после того, как я немного поцарапал, оказалось, что если вы хотите разделить диск на четыре кольца с равной площадью, то радиусы, по которым вам придется разрезать, будут sqrt (1/4), sqrt (1/2 ) и sqrt (3/4). Поскольку фактический радиус точки, которую я проверяю, будет sqrt (x ^ 2 + y ^ 2), я могу пропустить квадратный корень все вместе. «Совпадение» 1/4, 2/4, 3/4 может быть связано с тем, что ранее указывал М. Бюттнер.


3

HPPPL , 146 (171-20-5) байтов

EXPORT r(n)BEGIN LOCAL R,A,i,Q;RECT();Q:=118.;ARC_P(Q,Q,Q);FOR i FROM 1 TO n DO R:=√RANDOM(1.);A:=RANDOM(2*π);PIXON_P(G0,IP(Q+Q*R*COS(A)),IP(Q+Q*R*SIN(A)));END;FREEZE;END;

Пример для 10000 баллов (включая время в секундах для реального устройства):

Рандомизировать точки на диске, время

Сама функция вызывается r(n). Остальное на изображении выше только для целей синхронизации.

Результат (диаметр диска 236 пикселей):

введите описание изображения здесь

Версия выше не хранит координаты точки, поэтому я написал версию, которая принимает два параметра r(n,p). nколичество баллов и p=0возвращает баллы в терминал,p=1 наносит на карту точки и диск), если сохранение координат является обязательным. Эта версия имеет длину 283 (308-20-5) байтов:

EXPORT r(n,p)BEGIN LOCAL R,A,j,Q,x,y;Q:=118.0;CASE IF p==0 THEN print() END IF p==1 THEN RECT();ARC_P(Q,Q,Q) END END;FOR j FROM 1 TO n DO R:=√RANDOM(1.0);A:=RANDOM(2*π);x:=R*COS(A);y:=R*SIN(A);CASE IF p==0 THEN print("("+x+", "+y+")") END IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END END;END;FREEZE;END;

Негольфированная версия:

EXPORT r(n,p)
BEGIN
LOCAL R,A,j,Q,x,y;
  Q:=118.0;
  CASE
    IF p==0 THEN print() END
    IF p==1 THEN RECT();ARC_P(Q,Q,Q) END
  END;
  FOR j FROM 1 TO n DO
    R:=√RANDOM(1.0);
    A:=RANDOM(2*π);
    x:=R*COS(A);
    y:=R*SIN(A);
    CASE
      IF p==0 THEN print("("+x+", "+y+")") END
      IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END
    END;
  END;
  FREEZE;
END;

Терминальный выход для r(10,0):

Рандомизировать точки на выходе дискового терминала

r(10,1) показывает диск с точками, как показано выше.


2

JavaScript, 75 байт

Отторжение на основе:

do x=(r=()=>4*Math.random()-2)(),y=r()
while(x*x+y*y>1)
alert(`(${[x,y]})`)

Прямой метод (80 байт):

alert(`(${[(z=(m=Math).sqrt((r=m.random)()))*m.sin(p=m.PI*2*r()),z*m.cos(p)]})`)

2

Python, 135 130 байт

from random import*
def r():return uniform(-1,1)
p=[]
while not p:
    x,y=r(),r()
    if x**2+y**2<=1:p=x,y
print'(%.2f, %2f)'%p

Удалено **0.5спасибо предложению @ jimmy23013 (поскольку это единичный круг, я сейчас проверяю, равно ли квадрат в квадрате между (x, y) и (0, 0) равен 1 2. Это то же самое).

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


Я думаю, что вам не нужно **0.5.
jimmy23013

@ jimmy23013 Спасибо! удален.
JF
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.