Randomizza i punti su un disco


14

Ho letto dei circoli da qualche parte, e proprio ora ho imparato a conoscere i dischi ( in realtà è un concetto abbastanza comune ) e ho pensato al codegolf.

Il tuo compito è di randomizzare un punto / più punti su un disco con il raggio 1.

Regole:

  • Tutti i punti devono avere la stessa probabilità di essere generati
  • È necessario utilizzare le coordinate in virgola mobile; il requisito minimo è di due decimali (ad es. i punti (0.12, -0.45)o (0.00, -1.00)sono validi)
  • Si ottengono -20 byte se il programma visualizza effettivamente il cerchio di delimitazione e i punti generati in esso. Le coordinate devono essere ancora valide ma non visualizzate e l'immagine generata deve avere una dimensione di almeno 201 per 201 pixel
  • Ottieni -5 byte se il tuo programma accetta il numero di punti da generare come input su stdin
  • Se decidi di non tracciare il cerchio di delimitazione e i punti, il tuo programma deve generare i punti generati sul formato (x, y)o (x,y)su stdout
  • Se decidi di prendere il numero di punti generati come input, ma non di tracciarlo, il tuo programma deve produrre tutti i punti randomizzati nel formato sopra indicato con o senza uno spazio tra

Vince l'invio più breve in byte!


1
@sweerpotato Sì, specifica che tutti i punti all'interno e sul cerchio sono validi. Non avevo capito che intendevi entrambi. Inoltre, questa domanda sembra che si adatterebbe meglio a una sfida di code-golf di una gara di popolarità, ma questa è solo la mia opinione.
Cole

5
" Fai XYZ in modo creativo " è la classica domanda Bad Popcon ™. Ciò che una persona considera creativo è ciò che un'altra persona considera il modo ovvio.
Peter Taylor,

Per curiosità, perché un requisito di output di 201x201 pixel per i grafici?
Giovanni

@JohnE Ho suggerito 201x201 pixel poiché corrisponde alla precisione del 2 decimale richiesta
trichoplax

Possiamo emettere le coordinate come numeri complessi? Ad esempio: 0.3503082505747327+0.13499221288682994j.
orlp,

Risposte:


5

Pyth, 26-5 = 21 byte

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

Prende il numero di coordinate da generare su stdin e le emette su stdout in questo modo:

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

Utilizza una strategia simile a @ MartinBüttner, generando coordinate e raggi polari, tranne per il fatto che utilizza esponenziazione complessa.


Puoi rimuovere il p, no? Cambia semplicemente l'output in linee separate.
PurkkaKoodari,

@ Pietu1998 Non è consentito, vedere i commenti sulla domanda principale.
orlp,

Oh, va bene.
PurkkaKoodari,

16

CJam, 28 27 byte

PP+mr_mc\ms]1.mrmqf*"(,)".\

Questa soluzione non è basata sul rifiuto. Sto generando i punti in coordinate polari, ma con una distribuzione non uniforme dei raggi per ottenere una densità uniforme dei punti.

Provalo qui.

Spiegazione

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.

Perché funziona Considera uno stretto anello di raggio re (piccola) larghezza dr. L'area è approssimativamente 2π*r*dr(se l'anello è stretto, la circonferenza interna ed esterna sono quasi identiche e la curvatura può essere ignorata, in modo che l'area possa essere trattata come quella di un rettangolo con lunghezze laterali della circonferenza e larghezza del annulus). Quindi l'area aumenta linearmente con il raggio. Ciò significa che vogliamo anche una distribuzione lineare dei raggi casuali, al fine di ottenere una densità costante (al doppio del raggio, vi è il doppio dell'area da riempire, quindi vogliamo il doppio di punti lì).

Come possiamo generare una distribuzione casuale lineare da 0 a 1? Diamo prima un'occhiata al caso discreto. Diciamo, abbiamo una distribuzione desiderata di 4 valori, come {0.1, 0.4, 0.2, 0.3}(cioè vogliamo 1essere 4 volte più comuni di 0, e due volte più comuni di 2; vogliamo 3tre volte più comuni di 0):

enter image description here

Come può scegliere uno dei quattro valori con la distribuzione desiderata? Possiamo impilarli, scegliere un valore uniformemente casuale tra 0 e 1 sull'asse y e selezionare il segmento in quel punto:

enter image description here

Tuttavia, esiste un modo diverso di visualizzare questa selezione. Potremmo invece sostituire ogni valore della distribuzione con l'accumulo dei valori fino a quel punto:

enter image description here

E ora trattiamo la riga superiore di questo grafico come una funzione f(x) = ye la invertiamo per ottenere una funzione , che possiamo applicare a un valore uniformemente casuale in :g(y) = f-1(y) = xy ∈ [0,1]

enter image description here

Fantastico, quindi come può sfruttarlo per generare una distribuzione lineare dei raggi? Questa è la distribuzione che vogliamo:

enter image description here

Il primo passo è accumulare i valori della distribuzione. Ma la distribuzione è continua, quindi invece di sommare tutti i valori precedenti, prendiamo un integrale da 0a r. Siamo in grado di risolvere facilmente che analiticamente: . Tuttavia, vogliamo che questo sia normalizzato, cioè moltiplicandolo per una costante tale da dare il massimo valore , quindi quello che vogliamo davvero è :0r r dr = 1/2 r21rr2

enter image description here

E infine, invertiamo questo per ottenere una funzione in cui possiamo applicare un valore uniforme [0,1], che possiamo fare di nuovo analiticamente: è proprio r = √ydove si ytrova il valore casuale:

enter image description here

Questa è una tecnica abbastanza utile che spesso può essere utilizzata per generare esattamente semplici distribuzioni (funziona per qualsiasi distribuzione, ma per quelle complicate potrebbe essere necessario risolvere numericamente gli ultimi due passaggi). Tuttavia, non lo userei in questo caso particolare nel codice di produzione, perché la radice quadrata, il seno e il coseno sono proibitivamente costosi: l'uso di un algoritmo basato sul rifiuto è in media molto più veloce, perché ha solo bisogno di addizioni e moltiplicazioni.


1
Spiegazione molto bella!
sweerpotato,

2
Mmm foto: D
Decadimento Beta

12

Mathematica, 68 44-20 = 24 byte

Mille grazie per David Carraher per avermelo fatto conoscere RandomPoint, che ha permesso di risparmiare 24 (!) Byte. Mathematica non hanno un built-in per tutto.

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

Questo traccia il punto e il cerchio limite per qualificarsi per il bonus:

enter image description here

Il risultato è un'immagine vettoriale, quindi la specifica di dimensione di 201x201 pixel non ha davvero senso, ma per impostazione predefinita rende più grande di quello.


Che ne dici Graphics[{Circle[], Point@RandomPoint@Disk[]}]?
DavidC,

Essere mio ospite. Inoltre, per salvare 1 byte ...Graphics@{Circle[], Point@RandomPoint@Disk[]}
DavidC

@DavidCarraher Grazie mille! :)
Martin Ender,

Non conosco la sintassi di Mathematica ma sicuramente puoi salvare un altro byte rimuovendo lo spazio dopo il ,?
soffice

@fluffy l'ho già fatto nella versione pubblicata
Martin Ender il

9

CJam, 31 26 byte

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

Questo funziona generando ripetutamente punti casuali in un quadrato di lunghezza laterale 2 e mantenendo il primo che cade all'interno del disco dell'unità.

Grazie a @ MartinBüttner per giocare a golf con 3 byte!

Provalo online nell'interprete CJam .

Come funziona

{                  }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 byte

Niente di particolarmente speciale, ma suppongo che dovremmo avere almeno una soluzione grafica:

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

tracciare

Provalo nel tuo browser .

Modifica: posso radere due byte applicando l'approccio di @ MartinBüttner per modificare la distribuzione delle coordinate polari. Penso che sia anche un po 'più diretto:

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

3
Se anche tu disegnassi il cerchio di delimitazione ti qualificheresti per -20.
orlp,

1
iKe ha un modello di disegno basato su raster, che rende tale requisito piuttosto ingiusto. Penso che costerebbe un po 'più di 20 caratteri per rendere approssimativa anche un cerchio di delimitazione.
JohnE,

7

Perl, 59 byte

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

Questa è solo una soluzione semplice, che genera punti in un quadrato e rifiuta quelli troppo lontani. Il mio singolare trucco da golf è quello di includere gli incarichi all'interno della condizione.

Modifica: Nel processo di golf, ho trovato un modo interessante per stampare punti casuali su un cerchio .

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

7

Ottava, 24 53 - 20 = 33 byte

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

Genera 501 valori theta equidistanti più un numero casuale e li ridimensiona su [0..2π]. Quindi genera 501 1 per il raggio del cerchio, più un raggio casuale per il punto e prende la radice quadrata per garantire una distribuzione uniforme sul disco. Quindi traccia tutti i punti come coordinate polari.

inserisci qui la descrizione dell'immagine


Ecco una rapida dimostrazione della distribuzione (senza il cerchio unitario):

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

9801 punti


5

Octave / Matlab, 74 64 byte

Metodo di rifiuto , 64 byte:

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

Metodo diretto , 74 byte (grazie a Martin Büttner per avermi aiutato a correggere due errori):

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 byte

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

Usa la costruzione numerica complessa per costruire le x / y dalle coordinate polari. Prendere l'input è stato un po 'costoso e probabilmente c'è un modo migliore per farlo. Il ylim exlimasp simbolo assicura che l'intero cerchio sia tracciato e che i punti siano mostrati sotto il simbolo del cerchio.

Grazie a @jbaums e @flodel per i risparmi

Provalo qui


runif(9,0,1)può essere semplificato arunif(9)
jbaums il

@jbaums, grazie ... una delle cose che mi sembra sempre di dimenticare :)
MickyT

Può radersi 14:symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))
flodel,

@flodel molto bello grazie.
MickyT,

Un altro risparmio per adolescenti: ylifunziona al posto di ylim.
jbaums,

4

Elaborazione / Java 141 byte-20 = 121

il requisito per 201 * 201 è la dimensione minima mi richiede di inserire il setupmetodo poiché Processing.org è impostato su 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)}

Non sapevo che l'elaborazione / Java fosse permesso, pulito!
J Atkin,

4

QBasic, 138 byte - 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

Accetta l'input dell'utente e disegna il disco e i punti. Testato su QB64 .

Questa è una strategia piuttosto semplice "lancia il bersaglio e mantieni ciò che attacca". Il problema è che "ciò che si attacca" non è determinato matematicamente ma graficamente: un disco bianco viene tracciato su uno sfondo nero e quindi i punti generati casualmente vengono rifiutati fino a quando non sono neri. I punti stessi sono disegnati in blu (anche se è difficile capire quando sono singoli pixel - fai clic sull'immagine per ingrandirla).


3

awk - 95-5 = 90

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

Dato che non ero abbastanza sicuro della parte rand () <. 5, ho fatto alcuni test di distribuzione con questo, usando questo script:

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
}

che per un input di 1e7 mi dà questo risultato, dopo aver sorseggiato una o due volte al mio caffè:

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

che penso sia abbastanza bene.

Una piccola spiegazione:
dopo aver scarabocchiato per un po 'si è scoperto che se si desidera dividere il disco in quattro anelli con uguale area, i raggi in cui si dovrebbe tagliare sono sqrt (1/4), sqrt (1/2 ) e sqrt (3/4). Poiché il raggio effettivo del punto testato sarebbe sqrt (x ^ 2 + y ^ 2), posso saltare il rooting quadrato tutti insieme. La "coincidenza" 1/4, 2/4, 3/4 potrebbe essere correlata a ciò che M. Buettner ha sottolineato in precedenza.


3

HPPPL , 146 (171-20-5) byte

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;

Esempio per 10000 punti (incluso il tempo in secondi per il dispositivo reale):

Randomizza punti su un disco, tempismo

La funzione stessa è chiamata da r(n) . Il resto nell'immagine sopra è solo a scopo di temporizzazione.

Risultato (il diametro del disco è di 236 pixel):

inserisci qui la descrizione dell'immagine

La versione sopra non memorizza le coordinate del punto, quindi ho scritto una versione che accetta due parametri r(n,p). nè il numero di punti e p=0restituisce i punti al terminale, p=1traccia i punti e il disco), nel caso in cui la memorizzazione delle coordinate sia obbligatoria. Questa versione è lunga 283 (308-20-5) byte:

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;

La versione ungolfed:

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;

Uscita terminale per r(10,0):

Randomizza i punti sull'uscita del terminale del disco

r(10,1) mostra il disco con i punti, come mostrato sopra.


2

JavaScript, 75 byte

Rifiuto a base di:

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

Metodo diretto (80 byte):

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

2

Python, 135 130 byte

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

Rimosso il **0.5ringraziamento al suggerimento di @jimmy23013 (poiché si tratta di un cerchio unitario, ora sto verificando se la distanza al quadrato tra (x, y) e (0, 0) è uguale a 1 2. Questa è la stessa cosa).

Questo mi ha anche liberato di rimuovere le parentesi.


Penso che non ti serva **0.5.
jimmy23013,

@ jimmy23013 Grazie! rimosso.
JF,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.