Genera punti in modo efficiente tra il cerchio unitario e il quadrato unitario


17

Vorrei generare campioni dalla regione blu definita qui:

inserisci qui la descrizione dell'immagine

La soluzione ingenua è utilizzare il campionamento del rifiuto nel quadrato dell'unità, ma ciò fornisce solo un'efficienza di (~ 21,4%).1-π/4

C'è un modo per campionare in modo più efficiente?


6
Suggerimento : usa la simmetria per raddoppiare banalmente la tua efficienza.
cardinale il

3
Oh like: se il valore è (0,0), questo può essere mappato su (1,1)? Adoro quell'idea
Cam.Davidson.Pilon,

@cardinal Non dovrebbe 4x l'efficienza? È possibile campionare in e quindi eseguirne il mirroring sull'asse x, sull'asse y e sull'origine. [0,...,1]×[0,...,1]
Martin Krämer,

1
@Martin: attraverso le quattro regioni simmetriche, hai sovrapposizioni, che devi affrontare con più attenzione.
cardinale il

3
@ Martin: Se io sto capendo quello che stai descrivendo, che non aumenta l'efficienza a tutti . (Hai trovato un punto, e ora ne conosci altri tre --- in un'area quattro volte più grande --- che fanno o non si trovano all'interno del disco dell'unità con probabilità uno secondo se . aiuta?) Il punto di aumentare l'efficienza è aumentare la probabilità di accettazione per ogni generato. Forse sono io ad essere denso? (X,y)(X,y)
cardinale il

Risposte:


10

Faranno due milioni di punti al secondo?

La distribuzione è simmetrica: dobbiamo solo elaborare la distribuzione per un ottavo del cerchio completo e quindi copiarla attorno agli altri ottanti. In coordinate polari , la distribuzione cumulativa dell'angolo per la posizione casuale al valore è data dall'area tra il triangolo e l'arco del cerchio che si estende da a . È quindi proporzionale aΘ ( X , Y ) θ ( 0 , 0 ) , ( 1 , 0 ) , ( 1 , tan θ ) ( 1 , 0 ) ( cos θ ,(r,θ)Θ(X,Y)θ(0,0),(1,0),(1,abbronzaturaθ)(1,0)(cosθ,peccatoθ)

FΘ(θ)=Pr(Θθ)α12abbronzatura(θ)-θ2,

da dove la sua densità è

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

Possiamo campionare da questa densità usando, diciamo, un metodo di rifiuto (che ha efficienza ).8/π254.6479%

La densità condizionale della coordinata radiale è proporzionale a tra e . Ciò può essere campionato con una semplice inversione del CDF.r d r r = 1 r = sec θRrdrr=1r=secθ

Se generiamo campioni indipendenti , la conversione in coordinate cartesiane campiona questo ottante. Poiché i campioni sono indipendenti, lo scambio casuale delle coordinate produce un campione casuale indipendente dal primo quadrante, come desiderato. (Gli scambi casuali richiedono la generazione di una sola variabile binomiale per determinare quante realizzazioni scambiare.)( x i , y i )(ri,θi)(xi,yi)

Ciascuna di tali realizzazioni di richiede, in media, una variazione uniforme (per ) più volte due variate uniformi (per ) e una piccola quantità di calcolo (veloce). Sono variate per punto (che, ovviamente, ha due coordinate). I dettagli completi sono nell'esempio di codice seguente. Questa cifra traccia 10.000 di oltre mezzo milione di punti generati.R 1 / ( 8 π - 2 ) Θ 4 / ( π - 4 ) 4.66(X,Y)R1/(8π2)Θ4/(π4)4.66

figura

Ecco il Rcodice che ha prodotto questa simulazione e l'ha cronometrato.

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")

1
Non capisco questa frase: "Poiché i campioni sono indipendenti, scambiando sistematicamente le coordinate ogni secondo campione produce un campione casuale indipendente dal primo quadrante, come desiderato." Mi sembra che scambiando sistematicamente le coordinate ogni secondo campione produca campioni altamente dipendenti. Ad esempio, mi sembra che la tua implementazione nel codice generi mezzo milione di campioni consecutivi dallo stesso ottante?
A. Rex,

7
A rigor di termini, questo approccio non funziona del tutto (per i punti iid) poiché genera un numero identico di campioni nei due ottanti: I punti di campionamento sono, quindi, dipendenti. Ora, se lanci monete imparziali per determinare l'ottante per ciascun campione ...
Cardinale

1
@ Cardinale hai ragione; Lo aggiusterò - senza (asintoticamente) aumentare il numero di variate casuali da generare!
whuber

2
A rigor di termini (e, ancora, solo nel più puro senso teorico), nel caso del campione finito, la tua modifica non richiede ulteriori variate casuali uniformi. In altre parole: dalla prima variabile casuale uniforme, costruisci la sequenza di lanci dai primi bit. Quindi, usa il resto (volte ) come prima coordinata generata. 2 nn2n
cardinale il

2
@ Xi'an Non sono stato in grado di ottenere un inverso calcolabile convenientemente. Posso fare leggermente meglio rifiutando il campionamento dalla distribuzione con densità proporzionale a (l'efficienza è ), al costo di dover calcolare un arcsine. ( 4 - π ) / ( π - 2 ) 75 %2sin(θ)2(4π)/(π2)75%
whuber

13

Propongo la seguente soluzione, che dovrebbe essere più semplice, più efficiente e / o computazionalmente più economica rispetto alle altre soluzioni di @cardinal, @whuber e @ stephan-kolassa finora.

Implica i seguenti semplici passaggi:

1) Disegna due campioni uniformi standard:

u1Unif(0,1)u2Unif(0,1).

2a) Applicare la seguente trasformazione di taglio al punto (i punti nel triangolo in basso a destra sono riflessi nel triangolo in alto a sinistra e saranno "un- riflesso "in 2b): [ xmin{u1,u2},max{u1,u2}

[xy]=[11]+[2212210][min{u1,u2}max{u1,u2}].

2b) Swap e se .y u 1 > u 2xyu1>u2

3) Rifiuta il campione se all'interno del cerchio dell'unità (l'accettazione dovrebbe essere di circa il 72%), ovvero:

x2+y2<1.

L'intuizione dietro questo algoritmo è mostrata nella figura. inserisci qui la descrizione dell'immagine

I passaggi 2a e 2b possono essere uniti in un unico passaggio:

2) Applicare la trasformazione del taglio e scambiare

x=1+22min(u1,u2)u2y=1+22min(u1,u2)u1

Il codice seguente implementa l'algoritmo sopra (e lo verifica usando il codice @ 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")

Alcuni test rapidi producono i seguenti risultati.

Algoritmo /stats//a/258349 . Meglio di 3: 0,33 secondi per milione di punti.

Questo algoritmo. Meglio di 3: 0,18 secondi per milione di punti.


3
+1 Molto ben fatto! Grazie per aver condiviso una soluzione ponderata, intelligente e semplice.
whuber

Grande idea! Stavo pensando a una mappatura dall'unità sq a questa porzione, ma non ho pensato a una mappatura imperfetta e quindi a uno schema di rifiuto. Grazie per avermi ampliato la mente!
Cam.Davidson.Pilon

7

Bene, si può fare in modo più efficiente , ma spero proprio che tu non stia cercando più velocemente .

L'idea sarebbe di campionare prima un valore , con una densità proporzionale alla lunghezza della sezione blu verticale sopra ogni valore :xxx

f(x)=11x2.

Wolfram ti aiuta a integrare questo :

0xf(y)dy=12x1x2+x12arcsinx.

Quindi la funzione di distribuzione cumulativa sarebbe questa espressione, ridimensionata per integrarsi a 1 (cioè, divisa per ).F01f(y)dy

Ora, per generare il tuo valore , scegli un numero casuale , distribuito uniformemente tra e . Quindi trova tale che . Cioè, dobbiamo invertire il CDF ( campionamento di trasformazione inversa ). Questo può essere fatto, ma non è facile. Né veloce.xt01xF(x)=t

Alla fine, dato , scegli una casuale distribuita uniformemente tra e .xy1x21

Di seguito è riportato il codice R. Nota che sto pre-valutando il CDF su una griglia di valori , e anche in questo caso ci vogliono alcuni minuti.x

Probabilmente puoi accelerare un po 'l'inversione del CDF se investi un po' di pensiero. Poi di nuovo, il pensiero fa male. Io personalmente andrei per il campionamento di rifiuto, che è più veloce e molto meno soggetto a errori, a meno che non ho avuto molto buone ragioni per non farlo.

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


Mi chiedo se l'uso dei polinomi di Chebyshev per approssimare il CDF migliorerebbe la velocità di valutazione.
Sycorax dice di reintegrare Monica il

@Sycorax, non senza modifiche; vedere ad esempio il trattamento chebfun delle singolarità algebriche agli endpoint.
JM non è uno statistico il
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.