Довільно розподілити точки на диску


14

Я десь читав про гуртки, а щойно дізнався про диски ( це насправді досить поширена концепція ) і задумався про кодогольф.

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

Правила:

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

Найкоротше подання в байтах виграє!


1
@sweerpotato Так, будь ласка, вкажіть, що всі точки в колі та на ньому дійсні. Я не розумів, що ти маєш на увазі обох. Крім того, це питання здається, що він би відповідав виклику коду-гольфу краще, ніж конкурс популярності, але це лише моя думка.
коул

5
" Робіть XYZ творчо " - класичний Bad Popcon Question ™. Те, що одна людина вважає творчим, те, що інша вважає очевидним способом.
Пітер Тейлор

З цікавості, чому для графіків потрібна вихідна піксель розміром 201x201 пікселів?
Джон

@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 Це заборонено, дивіться коментарі до головного питання.
orlp

О, добре.
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):

enter image description here

Як можна вибрати одне з чотирьох значень із потрібним розподілом? Ми можемо складати їх, вибирати рівномірно випадкове значення між 0 та 1 на осі у та вибирати відрізок у цій точці:

enter image description here

Однак є інший спосіб візуалізації цього вибору. Ми могли замість цього замінити кожне значення розподілу накопиченням значень до цього моменту:

enter image description here

А тепер ми трактуємо верхній рядок цієї діаграми як функцію f(x) = yта інвертуємо її, щоб отримати функцію , яку ми можемо застосувати до рівномірно випадкового значення у :g(y) = f-1(y) = xy ∈ [0,1]

enter image description here

Класно, тож як можна скористатися цим для отримання лінійного розподілу радіусів? Цей розподіл ми хочемо:

enter image description here

Перший крок - накопичення значень розподілу. Але розподіл безперервно, так що замість підсумовування за всіма попередніми значеннями, ми беремо інтеграл від 0до r. Ми можемо легко вирішити, аналітично: . Однак ми хочемо, щоб це нормалізувалося, тобто помножило його на постійне так, що це дає максимальне значення , так що ми дійсно хочемо :0r r dr = 1/2 r21rr2

enter image description here

І, нарешті, ми інвертуємо це, щоб отримати функцію, до якої можна застосувати рівномірне значення [0,1], яке ми можемо знову зробити аналітично: це просто r = √y, де yвипадкове значення:

enter image description here

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


1
Дуже приємне пояснення!
солодка картопля

2
Mmm малюнки: D
бета-розпад

12

Математика, 68 44 - 20 = 24 байти

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

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

Це відображає крапку та обмежувальне коло, щоб отримати право на бонус:

enter image description here

У результаті виходить векторне зображення, тому специфікація розміру 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 байти (спасибі Мартіну Бюттнеру за допомогу мені виправити дві помилки):

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 з полярних координат. Введення даних було трохи дорогим, і, мабуть, є кращий спосіб зробити це. Це 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

@flodel дуже приємно дякую.
MickyT

Ще одна підліткова економія: yliпрацює замість ylim.
дбауми

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 дозволяється, акуратно!
J Atkin

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

Пітон, 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.