Aleatorizar puntos en un disco

14

Leí sobre círculos en alguna parte, y justo ahora aprendí sobre discos ( en realidad es un concepto bastante común ) y pensé en codegolf.

Su tarea es aleatorizar un punto / varios puntos en un disco con el radio 1.

Reglas:

  • Todos los puntos deben tener la misma probabilidad de ser generados
  • Se deben usar coordenadas de punto flotante; el requisito mínimo es de dos decimales (por ejemplo, los puntos (0.12, -0.45)o (0.00, -1.00)son válidos)
  • Obtiene -20 bytes si su programa realmente muestra el círculo delimitador y los puntos generados en él. Las coordenadas aún tienen que ser válidas pero no se muestran, y la imagen generada debe tener un tamaño de al menos 201 por 201 píxeles
  • Obtiene -5 bytes si su programa toma el número de puntos que se generarán como entrada en stdin
  • Si decide no trazar el círculo delimitador y los puntos, su programa debe generar los puntos generados en el formato (x, y)o (x,y)en stdout
  • Si decide tomar el número de puntos generados como entrada, pero no trazarlo, su programa debe generar todos los puntos aleatorios en el formato indicado anteriormente con o sin un espacio intermedio

¡La presentación más corta en bytes gana!

patata dulce
fuente
1
@sweerpotato Sí, especifique que todos los puntos dentro y dentro del círculo son válidos. No me di cuenta de que querías decir ambas cosas. Además, esta pregunta parece encajar mejor en un desafío de código de golf que un concurso de popularidad, pero esa es solo mi opinión.
cole
55
" Do XYZ de una manera creativa " es la clásica Bad Popcon Question ™. Lo que una persona considera creativa es lo que otra persona considera la manera obvia.
Peter Taylor
Por curiosidad, ¿por qué un requisito de salida de 201x201 píxeles para las parcelas?
JohnE
@JohnE Sugerí 201x201 píxeles, ya que coincide con la precisión de 2 decimales requerida
trichoplax
¿Podemos generar las coordenadas como números complejos? Por ejemplo: 0.3503082505747327+0.13499221288682994j.
orlp

Respuestas:

5

Pyth, 26 - 5 = 21 bytes

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

Toma el número de coordenadas para generar en stdin y las genera en stdout de la siguiente manera:

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

Utiliza una estrategia similar a @ MartinBüttner, que genera coordenadas polares y radios, excepto que lo hace usando exponenciación compleja.

orlp
fuente
Puedes eliminar el p, ¿no? Simplemente cambia la salida a líneas separadas.
PurkkaKoodari
@ Pietu1998 Eso no está permitido, vea los comentarios sobre la pregunta principal.
orlp
Oh, todo bien.
PurkkaKoodari
16

CJam, 28 27 bytes

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

Esta solución no está basada en el rechazo. Estoy generando los puntos en coordenadas polares, pero con una distribución no uniforme de los radios para lograr una densidad uniforme de los puntos.

Pruébalo aquí.

Explicación

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.

Por que funciona Considere un anillo estrecho de radio ry ancho (pequeño) dr. El área es aproximadamente 2π*r*dr(si el anillo es angosto, la circunferencia interna y externa son casi idénticas, y la curvatura puede ignorarse, de modo que el área puede tratarse como la de un rectángulo con longitudes laterales de la circunferencia y el ancho de la circunferencia). anillo). Entonces el área aumenta linealmente con el radio. Esto significa que también queremos una distribución lineal de los radios aleatorios, para lograr una densidad constante (al doble del radio, hay el doble de área para llenar, por lo que queremos el doble de puntos allí).

¿Cómo generamos una distribución aleatoria lineal de 0 a 1? Veamos primero el caso discreto. Digamos que tenemos una distribución deseada de 4 valores, como {0.1, 0.4, 0.2, 0.3}(es decir, queremos 1ser 4 veces más comunes 0y el doble de comunes 2; queremos 3tres veces más comunes 0):

ingrese la descripción de la imagen aquí

¿Cómo puede elegir uno de los cuatro valores con la distribución deseada? Podemos apilarlos, elegir un valor aleatorio uniforme entre 0 y 1 en el eje y y seleccionar el segmento en ese punto:

ingrese la descripción de la imagen aquí

Sin embargo, hay una forma diferente de visualizar esta selección. En su lugar, podríamos reemplazar cada valor de la distribución con la acumulación de los valores hasta ese punto:

ingrese la descripción de la imagen aquí

Y ahora tratamos la línea superior de este gráfico como una función f(x) = yy la invertimos para obtener una función , que podemos aplicar a un valor aleatorio uniforme en :g(y) = f-1(y) = xy ∈ [0,1]

ingrese la descripción de la imagen aquí

Genial, entonces, ¿cómo puede hacer uso de esto para generar una distribución lineal de radios? Esta es la distribución que queremos:

ingrese la descripción de la imagen aquí

El primer paso es acumular los valores de la distribución. Pero la distribución es continua, por lo que en lugar de sumar todos los valores anteriores, tomamos una integral de 0a r. Podemos resolver fácilmente que analíticamente: . Sin embargo, queremos que esto se normalice, es decir, que se multiplique por una constante de modo que se obtenga el valor máximo de , por lo que lo que realmente queremos es :0r r dr = 1/2 r21rr2

ingrese la descripción de la imagen aquí

Y finalmente, invertimos esto para obtener una función que podamos aplicar a un valor uniforme [0,1], lo que podemos hacer analíticamente nuevamente: es solo r = √ydónde yestá el valor aleatorio:

ingrese la descripción de la imagen aquí

Esta es una técnica bastante útil que a menudo se puede utilizar para generar distribuciones simples exactamente (funciona para cualquier distribución, pero para las complicadas los dos últimos pasos pueden tener que resolverse numéricamente). Sin embargo, no lo usaría en este caso particular en el código de producción, porque la raíz cuadrada, el seno y el coseno son prohibitivamente costosos: el uso de un algoritmo basado en rechazo es, en promedio, mucho más rápido, porque solo necesita sumar y multiplicar.

Martin Ender
fuente
1
Muy buena explicación!
sweerpotato
2
Fotos Mmm: D
Beta Decay
12

Mathematica, 68 44-20 = 24 bytes

Muchas gracias por David Carraher por informarme RandomPoint, que ahorró 24 (!) Bytes. Mathematica no tiene un built-in para todo.

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

Esto traza el punto y el círculo delimitador para calificar para el bono:

ingrese la descripción de la imagen aquí

El resultado es una imagen vectorial, por lo que la especificación de tamaño de 201x201 píxeles realmente no tiene sentido, pero por defecto se hace más grande que eso.

Martin Ender
fuente
¿Qué tal Graphics[{Circle[], Point@RandomPoint@Disk[]}]?
DavidC
Sé mi invitado. Además, para guardar 1 byte ...Graphics@{Circle[], Point@RandomPoint@Disk[]}
DavidC
@DavidCarraher ¡Muchas gracias! :)
Martin Ender
No sé la sintaxis de Mathematica, pero seguramente puede guardar otro byte eliminando el espacio después de ,?
esponjoso
@fluffy Ya lo hice en la versión publicada
Martin Ender
9

CJam, 31 26 bytes

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

Esto funciona generando repetidamente puntos aleatorios en un cuadrado de longitud lateral 2 y manteniendo el primero que cae dentro del disco de la unidad.

¡Gracias a @ MartinBüttner por jugar golf en 3 bytes!

Pruébelo en línea en el intérprete de CJam .

Cómo funciona

{                  }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.
Dennis
fuente
8

iKe , 53 51 bytes

Nada particularmente especial, pero supongo que deberíamos tener al menos una solución gráfica:

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

trama

Pruébalo en tu navegador .

Editar: puedo reducir dos bytes aplicando el enfoque de @ MartinBüttner para modificar la distribución de coordenadas polares. Creo que también es un poco más directo:

,(80*1+(%?c){x*(cos y;sin y)}'6.282*?c:9999;cga;3)
JohnE
fuente
3
Si también dibujara el círculo delimitador calificaría para -20.
orlp
1
iKe tiene un modelo de dibujo basado en ráster, lo que hace que ese requisito sea bastante injusto. Creo que también costaría un poco más de 20 caracteres representar una aproximación de un círculo delimitador.
JohnE
7

Perl, 59 Bytes

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

Esta es solo una solución simple, generar puntos en un cuadrado y rechazar los que están demasiado lejos. Mi truco de golf singular es incluir las tareas dentro de la condición.

Editar: En el proceso de golf, encontré una forma interesante de imprimir puntos aleatorios en un círculo .

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

Octava, 24 53 - 20 = 33 bytes

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

Genera 501 valores theta igualmente espaciados más un número aleatorio y los escala a todos a [0..2π]. Luego genera 501 1 para el radio del círculo, más un radio aleatorio para el punto y toma la raíz cuadrada para asegurar una distribución uniforme sobre el disco. Luego traza todos los puntos como coordenadas polares.

ingrese la descripción de la imagen aquí


Aquí hay una demostración rápida de la distribución (sin el círculo unitario):

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

9801 puntos

cubilete
fuente
5

Octave / Matlab, 74 64 bytes

Método de rechazo , 64 bytes:

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

Método directo , 74 bytes (gracias a Martin Büttner por ayudarme a corregir dos errores):

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

R, 99 95 81-20 = 79 75 61 Bytes

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

Usa la construcción de números complejos para construir las x / y a partir de coordenadas polares. Tomar la entrada fue un poco costoso y probablemente haya una mejor manera de hacerlo. El ylim yxlim es para asegurar que todo el círculo se traza y aspasegura que los puntos se muestran debajo del símbolo del círculo.

Gracias a @jbaums y @flodel por los ahorros.

Pruébalo aquí

MickyT
fuente
runif(9,0,1)se puede simplificar arunif(9)
jbaums
@jbaums, gracias ... una de las cosas que siempre parezco olvidar :)
MickyT
Puede afeitarse 14:symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))
flodel
@flodel muy bien gracias.
MickyT
Otro ahorro menor: ylifunciona en lugar de ylim.
jbaums
4

Procesamiento / Java 141 bytes-20 = 121

el requisito de que 201 * 201 sea el tamaño mínimo requiere que coloque el setupmétodo ya que Processing.org tiene por defecto 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)}
Timothy Groote
fuente
¡No sabía que el procesamiento / Java estaba permitido, ordenado!
J Atkin
4

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

Toma la entrada del usuario y dibuja el disco y los puntos. Probado en QB64 .

Esta es una estrategia bastante básica de "tirar al tablero y mantener lo que se pega". El problema es que "lo que se pega" no se determina matemáticamente sino gráficamente: se traza un disco blanco sobre un fondo negro, y luego los puntos generados aleatoriamente se rechazan hasta que no sean negros. Los puntos en sí están dibujados en azul (aunque es difícil saber cuándo son píxeles individuales; haga clic en la imagen para ampliarla).

DLosc
fuente
3

awk - 95-5 = 90

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

Como no estaba muy seguro de la parte rand () <. 5, hice algunas pruebas de distribución con esto, usando este 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
}

que para una entrada de 1e7 me da este resultado, después de tomar una o dos veces en mi café:

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

lo cual creo que está bastante bien.

Una pequeña explicación:
después de garabatear durante un tiempo, resultó que si desea dividir el disco en cuatro anillos con igual área, los radios donde tendría que cortar son sqrt (1/4), sqrt (1/2 ) y sqrt (3/4). Como el radio real del punto que pruebo sería sqrt (x ^ 2 + y ^ 2), puedo omitir el enraizamiento cuadrado todos juntos. La "coincidencia" 1/4, 2/4, 3/4 podría estar relacionada con lo que M. Buettner señaló anteriormente.

Cabbie407
fuente
3

HPPPL , 146 (171-20-5) bytes

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;

Ejemplo para 10000 puntos (incluido el tiempo en segundos para el dispositivo real):

Aleatorizar puntos en un disco, tiempo

La función en sí es llamada por r(n). El resto de la imagen de arriba es solo para fines de sincronización.

Resultado (el diámetro del disco es de 236 píxeles):

ingrese la descripción de la imagen aquí

La versión anterior no almacena las coordenadas del punto, por lo que escribí una versión que toma dos parámetros r(n,p). nes el número de puntos y p=0devuelve los puntos al terminal, p=1traza los puntos y el disco), en caso de que el almacenamiento de coordenadas sea obligatorio. Esta versión tiene 283 (308-20-5) bytes de longitud:

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 versión sin golf:

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;

Salida de terminal para r(10,0):

Aleatorizar puntos en una salida de terminal de disco

r(10,1) muestra el disco con los puntos, como se muestra arriba.

ML
fuente
2

JavaScript, 75 bytes

Basado en rechazo:

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

Método directo (80 bytes):

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

Python, 135 130 bytes

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

Se eliminó el **0.5agradecimiento a la sugerencia de @ jimmy23013 (debido a que es un círculo unitario, ahora estoy verificando si la distancia al cuadrado entre (x, y) y (0, 0) es igual a 1 2. Esto es lo mismo).

Esto también me liberó para eliminar los paréntesis.

JF
fuente
Creo que no necesitas el **0.5.
jimmy23013
@ jimmy23013 ¡Gracias! remoto.
JF