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


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.