Efektywnie generuj punkty między kołem jednostki a kwadratem jednostki

17

Chciałbym wygenerować próbki z zdefiniowanego tutaj niebieskiego regionu:

wprowadź opis zdjęcia tutaj

Naiwnym rozwiązaniem jest stosowanie próbkowania odrzucania w jednostce kwadratowej, ale zapewnia to jedynie wydajność 1-π/4 (~ 21,4%).

Czy jest jakiś sposób na bardziej efektywne próbkowanie?

Cam.Davidson.Pilon
źródło
6
Wskazówka : Użyj symetrii, aby podwoić efektywność.
kardynał
3
Aha: jeśli wartość wynosi (0,0), można to odwzorować na (1,1)? Podoba mi się ten pomysł
Cam.Davidson.Pilon
@cardinal Czy nie powinno to zwiększać wydajności 4x? Możesz próbkować w a następnielustrzaną w poprzek osi x, osi y i początku. [0,,1]×[0,,1]
Martin Krämer,
1
@Martin: W czterech regionach symetrycznych zachodzi na siebie nakładanie się, z którym trzeba postępować ostrożniej.
kardynał
3
@Martin: Jeśli mam zrozumienia tego, co opisujesz, że nie spowoduje to zwiększenia wydajności w ogóle . (Znalazłeś jeden punkt, a teraz znasz trzy inne - w obszarze czterokrotnie większym - które albo leżą, albo nie leżą w dysku jednostki z prawdopodobieństwem jeden w zależności od tego, czy tak. Jak czy to pomaga?) Celem zwiększenia wydajności jest zwiększenie prawdopodobieństwa akceptacji dla każdego wygenerowanego ( x , y ) . Może to ja jestem gęsty? (x,y)(x,y)
kardynał

Odpowiedzi:

10

Czy zrobią to dwa miliony punktów na sekundę?

Rozkład jest symetryczny: musimy tylko opracować rozkład dla jednej ósmej pełnego koła, a następnie skopiować go wokół innych oktantów. We współrzędnych biegunowych skumulowany rozkład kąta Θ dla losowej lokalizacji ( X , Y ) przy wartości θ jest określony przez obszar między trójkątem ( 0(r,θ)Θ(X,Y)θ i łuk koła rozciągający się od ((0,0),(1,0),(1,dębnikθ) do ( cos θ , sin θ ) . Jest zatem proporcjonalny do(1,0)(sałataθ,grzechθ)

FΘ(θ)=Pr(Θθ)12)dębnik(θ)-θ2),

stąd jego gęstość jest

faΘ(θ)=rereθfaΘ(θ)dębnik2)(θ).

Możemy próbkować z tej gęstości, stosując, powiedzmy, metodę odrzucania (która ma wydajność ).8/π-2)54,6479%

Gęstość warunkowa współrzędnej promieniowej jest proporcjonalna do r d r między r = 1 i rRrrerr=1 . Można to próbkować przez łatwą inwersję CDF.r=secθ

Jeśli wygenerujemy niezależne próbki , konwersja z powrotem do współrzędnych kartezjańskich ( x i(rja,θja) próbki tego oktant. Ponieważ próbki są niezależne, losowa zamiana współrzędnych tworzy niezależną losową próbkę z pierwszej ćwiartki, zgodnie z potrzebami. (Swapy losowe wymagają wygenerowania tylko jednej zmiennej dwumianowej, aby określić liczbę realizacji do zamiany).(xja,yja)

Każda taka realizacja wymaga średnio jednej zmiany jednolitej (dla R ) plus 1 / ( 8 π - 2 ) razy dwóch zmian jednolitej (dla Θ ) i niewielkiej ilości (szybkiego) obliczenia. To 4 / ( π - 4 ) 4,66 zmienna na punkt (który oczywiście ma dwie współrzędne). Pełne szczegóły znajdują się w poniższym przykładzie kodu. Ta liczba przedstawia 10 000 z ponad pół miliona wygenerowanych punktów.(X,Y)R1/(8π-2))Θ4/(π-4)4,66

Postać

Oto Rkod, który wyprodukował tę symulację i dokonał jej pomiaru czasu.

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
źródło
1
Nie rozumiem tego zdania: „Ponieważ próbki są niezależne, systematycznie zamieniając współrzędne co drugą próbkę, zgodnie z życzeniem, tworzy niezależną próbkę losową z pierwszej ćwiartki”. Wydaje mi się, że systematyczna zamiana współrzędnych co drugą próbkę daje wysoce zależne próbki. Na przykład wydaje mi się, że twoja implementacja w kodzie generuje pół miliona próbek z rzędu z tego samego oktanta?
A. Rex
7
Ściśle mówiąc, to podejście nie do końca działa (dla punktów iid), ponieważ generuje identyczną liczbę próbek w dwóch oktantach: Punkty próbki są zatem zależne. Teraz, jeśli przerzucisz obiektywne monety, aby określić liczbę oktanową dla każdej próbki ...
kardynał
1
@Cardinal masz rację; Naprawię to - bez (asymptotycznie) zwiększania liczby losowych zmiennych do wygenerowania!
whuber
2
Ściśle mówiąc (i znowu tylko w najczystszym sensie teoretycznym), w przypadku skończonej próbki, twoja modyfikacja nie wymaga żadnych dodatkowych jednolitych zmiennych losowych. To znaczy: Od pierwszej jednolitej zmiennej losowej konstruuj sekwencję przerzucania z pierwszych bitów. Następnie użyj reszty (razy 2 n ) jako pierwszej wygenerowanej współrzędnej. n2n
kardynał
2
@ Xi'an Nie byłem w stanie uzyskać wygodnie obliczalnej odwrotności. Mogę zrobić nieco lepiej, odrzucając próbkowanie z rozkładu o gęstości proporcjonalnej do (wydajność wynosi ( 4 - π )2sin(θ)2 ), kosztem obliczenia arcsine . (4π)/(π2)75%
whuber
13

Proponuję następujące rozwiązanie, które powinno być prostsze, wydajniejsze i / lub tańsze obliczeniowo niż inne południowe firmy @cardinal, @whuber i @ stephan-kolassa.

Obejmuje następujące proste kroki:

1) Narysuj dwie standardowe jednolite próbki:

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

2a) Zastosuj następującą transformację ścinania do punktu (punkty w prawym dolnym trójkącie są odzwierciedlone w lewym górnym trójkącie i będą „nieodbite” w 2b): [ x y ] = [ 1 1 ] + [ min{u1,u2},max{u1,u2}

[xy]=[11]+[2)2)12)2)-10][min{u1,u2)}max{u1,u2)}].

2b) Zamień i y, jeśli u 1 > u 2xyu1>u2) .

3) Odrzuć próbkę, jeśli znajduje się w okręgu jednostkowym (akceptacja powinna wynosić około 72%), tj .:

x2)+y2)<1.

Intuicja tego algorytmu pokazano na rysunku. wprowadź opis zdjęcia tutaj

Kroki 2a i 2b można połączyć w jeden krok:

2) Zastosuj transformację ścinającą i zamień

x=1+2)2)min(u1,u2))-u2)y=1+2)2)min(u1,u2))-u1

Poniższy kod implementuje powyższy algorytm (i testuje go przy użyciu kodu @ 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")

Niektóre szybkie testy dają następujące wyniki.

Algorytm /stats//a/258349 . Najlepsze 3: 0,33 sekundy na milion punktów.

Ten algorytm Najlepsze 3: 0,18 sekundy na milion punktów.

Luca Citi
źródło
3
+1 Bardzo dobrze zrobione! Dziękujemy za podzielenie się przemyślanym, sprytnym i prostym rozwiązaniem.
whuber
Świetny pomysł! Myślałem o mapowaniu z jednostki sq do tej części, ale nie myślałem o niedoskonałym mapowaniu, a potem o schemacie odrzucenia. Dziękuję za poszerzenie mojego umysłu!
Cam.Davidson.Pilon
7

Dobrze, bardziej efektywnie można to zrobić, ale mam nadzieję, że nie szukasz szybszego .

Pomysł polegałby na próbkowaniu x wartość pierwsza, o gęstości proporcjonalnej do długości pionowego niebieskiego wycinka nad każdym x wartość:

fa(x)=1-1-x2).

Wolfram pomaga zintegrować, że :

0xfa(y)rey=-12)x1-x2)+x-12)arcsinx.

Więc funkcja skumulowanego rozkładu fa byłoby to wyrażenie skalowane w celu integracji do 1 (tzn. podzielone przez 01fa(y)rey).

Teraz wygeneruj swój x wartość, wybierz liczbę losową t, równomiernie rozmieszczone między 0 i 1. Następnie znajdźx takie, że fa(x)=t. Oznacza to, że musimy odwrócić CDF ( próbkowanie z transformacją odwrotną ). Można to zrobić, ale nie jest to łatwe. Ani szybko.

Wreszcie podane xwybierz losowy y który jest równomiernie rozmieszczony między 1-x2) i 1.

Poniżej znajduje się kod R. Zauważ, że wstępnie oceniam CDF na poziomiex wartości, a nawet wtedy zajmuje to kilka minut.

Prawdopodobnie możesz znacznie przyspieszyć inwersję CDF, jeśli zainwestujesz trochę myślenia. Z drugiej strony myślenie boli. Osobiście wybrałbym próbkowanie odrzucania, które jest szybsze i znacznie mniej podatne na błędy, chyba że mam bardzo dobre powody, aby tego nie robić .

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

losowe

S. Kolassa - Przywróć Monikę
źródło
Zastanawiam się, czy użycie wielomianów Czebyszewa do przybliżenia CDF poprawiłoby szybkość oceny.
Sycorax mówi Przywróć Monikę
@Sycorax, nie bez modyfikacji; patrz np . leczenie chegefunem osobliwości algebraicznych w punktach końcowych.
JM nie jest statystykiem