Genere puntos eficientemente entre el círculo unitario y el cuadrado unitario

17

Me gustaría generar muestras de la región azul definida aquí:

ingrese la descripción de la imagen aquí

La solución ingenua es utilizar el muestreo de rechazo en el cuadrado de la unidad, pero esto proporciona solo una eficiencia de (~ 21.4%).1π/4

¿Hay alguna manera de que pueda probar más eficientemente?

Cam.Davidson.Pilon
fuente
66
Sugerencia : use la simetría para duplicar trivialmente su eficiencia.
cardenal
3
Oh, como: si el valor es (0,0), esto se puede asignar a (1,1)? Me encanta esa idea
Cam.Davidson.Pilon
@cardinal ¿No debería ser 4 veces más eficiente? Puede muestrear en y luego reflejarlo en el eje x, el eje y y el origen. [0,,1]×[0,,1]
Martin Krämer
1
@ Martin: en las cuatro regiones simétricas, tiene una superposición, que debe tratar con más cuidado.
cardenal
3
@ Martin: Si entiendo lo que estás describiendo, eso no aumenta la eficiencia en absoluto . (Encontraste un punto y ahora conoces otros tres --- en un área cuatro veces mayor que --- que se encuentran o no dentro del disco de la unidad con una probabilidad de uno según si sí. ¿eso ayuda?) El punto de aumentar la eficiencia es aumentar la probabilidad de aceptación para cada generado. ¿Quizás soy yo el que está siendo denso? ( x , y )(x,y)(x,y)
cardenal

Respuestas:

10

¿Serán dos millones de puntos por segundo?

La distribución es simétrica: solo necesitamos calcular la distribución para un octavo del círculo completo y luego copiarla alrededor de los otros octantes. En coordenadas polares , la distribución acumulativa del ángulo Θ para la ubicación aleatoria ( X , Y ) en el valor θ viene dada por el área entre el triángulo ( 0 , 0 ) , ( 1 , 0 ) , ( 1 , tan θ ) y el arco del círculo que se extiende desde(r,θ)Θ(X,Y)θ(0,0),(1,0),(1,tanθ)(1,0)a . Por lo tanto, es proporcional a(cosθ,sinθ)

FΘ(θ)=Pr(Θθ)12tan(θ)θ2,

de donde es su densidad

fΘ(θ)=ddθFΘ(θ)tan2(θ).

Podemos tomar muestras de esta densidad usando, por ejemplo, un método de rechazo (que tiene una eficiencia de ).8/π254.6479%

La densidad condicional de la coordenada radial es proporcional a r d r entre r = 1 y r = sec θ . Eso se puede muestrear con una fácil inversión del CDF.Rrdrr=1r=secθ

Si generamos muestras independientes , la conversión de nuevo a coordenadas cartesianas ( x i , y i ) muestrea este octante. Debido a que las muestras son independientes, el intercambio aleatorio de las coordenadas produce una muestra aleatoria independiente del primer cuadrante, según se desee. (Los intercambios aleatorios requieren generar solo una variable binomial única para determinar cuántas de las realizaciones intercambiar).(ri,θi)(xi,yi)

Cada realización de requiere, en promedio, una variante uniforme (para R ) más 1 / ( 8 π - 2 ) veces dos variables uniformes (para Θ ) y una pequeña cantidad de cálculo (rápido). Eso es 4 / ( π - 4 ) 4.66 variantes por punto (que, por supuesto, tiene dos coordenadas). Los detalles completos se encuentran en el siguiente ejemplo de código. Esta cifra representa 10,000 de más de medio millón de puntos generados.(X,Y)R1/(8π2)Θ4/(π4)4.66

Figura

Aquí está el Rcódigo que produjo esta simulación y la cronometró.

n.sim <- 1e6
x.time <- system.time({
  # Generate trial angles `theta`
  theta <- sqrt(runif(n.sim)) * pi/4
  # Rejection step.
  theta <- theta[runif(n.sim) * 4 * theta <= pi * tan(theta)^2]
  # Generate radial coordinates `r`.
  n <- length(theta)
  r <- sqrt(1 + runif(n) * tan(theta)^2)
  # Convert to Cartesian coordinates.
  # (The products will generate a full circle)
  x <- r * cos(theta) #* c(1,1,-1,-1)
  y <- r * sin(theta) #* c(1,-1,1,-1)
  # Swap approximately half the coordinates.
  k <- rbinom(1, n, 1/2)
  if (k > 0) {
    z <- y[1:k]
    y[1:k] <- x[1:k]
    x[1:k] <- z
  }
})
message(signif(x.time[3] * 1e6/n, 2), " seconds per million points.")
#
# Plot the result to confirm.
#
plot(c(0,1), c(0,1), type="n", bty="n", asp=1, xlab="x", ylab="y")
rect(-1, -1, 1, 1, col="White", border="#00000040")
m <- sample.int(n, min(n, 1e4))
points(x[m],y[m], pch=19, cex=1/2, col="#0000e010")
whuber
fuente
1
No entiendo esta oración: "Debido a que las muestras son independientes, intercambiar sistemáticamente las coordenadas cada segunda muestra produce una muestra aleatoria independiente del primer cuadrante, según se desee". Me parece que intercambiar sistemáticamente las coordenadas cada segunda muestra produce muestras altamente dependientes. Por ejemplo, ¿me parece que su implementación en el código genera medio millón de muestras seguidas del mismo octante?
A. Rex
77
Estrictamente hablando, este enfoque no funciona del todo (para los puntos iid) ya que genera un número idéntico de muestras en los dos octantes: los puntos de muestra son, por lo tanto, dependientes. Ahora, si lanzas monedas imparciales para determinar el octante de cada muestra ...
cardenal
1
@ Cardenal tienes razón; Lo arreglaré, ¡sin (asintóticamente) aumentar el número de variables aleatorias para generar!
whuber
2
Estrictamente hablando (y, nuevamente, solo en el sentido teórico más puro), en el caso de la muestra finita, su modificación no requiere variaciones aleatorias uniformes adicionales. A saber: desde la primera variable aleatoria uniforme, construya la secuencia de volteo a partir de los primeros bits. Luego, use el resto (multiplicado por 2 n ) como la primera coordenada generada. norte2norte
Cardenal
2
@ Xi'an No pude obtener un inverso convenientemente computable. Puedo hacerlo un poco mejor al rechazar el muestreo de la distribución con densidad proporcional a (la eficiencia es ( 4 - π ) / ( π - 2 ) 75 % ), a costa de tener que calcular un arcoseno . 2sin(θ)2(4π)/(π2)75%
whuber
13

Propongo la siguiente solución, que debería ser más simple, más eficiente y / o computacionalmente más barata que otras soluciones de @cardinal, @whuber y @ stephan-kolassa hasta ahora.

Implica los siguientes pasos simples:

tu1UnorteyoF(0 0,1)tu2UnorteyoF(0 0,1).

min{tu1,tu2},max{tu1,tu2}

[Xy]=[11]+[22-122-10 0][min{tu1,tu2}max{tu1,tu2}].

Xytu1>tu2

X2+y2<1)

La intuición detrás de este algoritmo se muestra en la figura. ingrese la descripción de la imagen aquí

Los pasos 2a y 2b se pueden combinar en un solo paso:

X=1+22min(tu1,tu2)-tu2y=1+22min(tu1,tu2)-tu1

El siguiente código implementa el algoritmo anterior (y lo prueba usando el código de @ whuber).

n.sim <- 1e6
x.time <- system.time({
    # Draw two standard uniform samples
    u_1 <- runif(n.sim)
    u_2 <- runif(n.sim)
    # Apply shear transformation and swap
    tmp <- 1 + sqrt(2)/2 * pmin(u_1, u_2)
    x <- tmp - u_2
    y <- tmp - u_1
    # Reject if inside circle
    accept <- x^2 + y^2 > 1
    x <- x[accept]
    y <- y[accept]
    n <- length(x)
})
message(signif(x.time[3] * 1e6/n, 2), " seconds per million points.")
#
# Plot the result to confirm.
#
plot(c(0,1), c(0,1), type="n", bty="n", asp=1, xlab="x", ylab="y")
rect(-1, -1, 1, 1, col="White", border="#00000040")
m <- sample.int(n, min(n, 1e4))
points(x[m],y[m], pch=19, cex=1/2, col="#0000e010")

Algunas pruebas rápidas arrojan los siguientes resultados.

Algoritmo /stats//a/258349 . Lo mejor de 3: 0,33 segundos por millón de puntos.

Este algoritmo Lo mejor de 3: 0.18 segundos por millón de puntos.

Luca Citi
fuente
3
+1 ¡Muy bien hecho! Gracias por compartir una solución reflexiva, inteligente y simple.
whuber
¡Gran idea! Estaba pensando en un mapeo desde la unidad sq a esta porción, pero no pensé en un mapeo imperfecto y luego en un esquema de rechazo. ¡Gracias por expandir mi mente!
Cam.Davidson.Pilon
7

Bueno, se puede hacer de manera más eficiente , pero espero que no estés buscando más rápido .

XX

F(X)=1-1-X2.

Wolfram te ayuda a integrar eso :

0 0XF(y)rey=-12X1-X2+X-12arcsinX.

F0 01F(y)rey

Xt0 01XF(X)=t

Xy1-X21

X

Probablemente pueda acelerar bastante la inversión de CDF si invierte algo de pensamiento. Por otra parte, pensar duele. Yo personalmente elegiría muestras de rechazo, que es más rápido y mucho menos propenso a errores, a menos que tenga muy buenas razones para no hacerlo.

epsilon <- 1e-6
xx <- seq(0,1,by=epsilon)
x.cdf <- function(x) x-(x*sqrt(1-x^2)+asin(x))/2
xx.cdf <- x.cdf(xx)/x.cdf(1)

nn <- 1e4
rr <- matrix(nrow=nn,ncol=2)
set.seed(1)
pb <- winProgressBar(max=nn)
for ( ii in 1:nn ) {
    setWinProgressBar(pb,ii,paste(ii,"of",nn))
    x <- max(xx[xx.cdf<runif(1)])
    y <- runif(1,sqrt(1-x^2),1)
    rr[ii,] <- c(x,y)
}
close(pb)

plot(rr,pch=19,cex=.3,xlab="",ylab="")

Randoms

S. Kolassa - Restablece a Monica
fuente
Me pregunto si el uso de polinomios de Chebyshev para aproximar el CDF mejoraría la velocidad de evaluación.
Sycorax dice Reinstate Monica
@Sycorax, no sin modificaciones; véase, por ejemplo, el tratamiento chebfun de singularidades algebraicas en los puntos finales.
JM no es un estadístico