Burada tanımlanan mavi bölgeden örnekler oluşturmak istiyorum:
Saf çözüm, birim karede reddetme örneklemesi kullanmaktır, ancak bu sadece (~% 21.4) verimlilik sağlar.
Daha verimli örneklememin bir yolu var mı?
Burada tanımlanan mavi bölgeden örnekler oluşturmak istiyorum:
Saf çözüm, birim karede reddetme örneklemesi kullanmaktır, ancak bu sadece (~% 21.4) verimlilik sağlar.
Daha verimli örneklememin bir yolu var mı?
Yanıtlar:
Saniyede iki milyon puan kazanacak mı?
Dağıtım simetriktir: sadece tüm dairenin sekizde biri için dağıtımı yapmamız ve sonra diğer oktanların etrafına kopyalamamız gerekir. Kutupsal koordinatlarda , açı kümülatif dağılım İçeride ISTV melerin RWMAIWi'nin rastgele konum ( X , Y ) değeri ile θ üçgen arasındaki alan ile verilmiştir ( veuzanan dairenin yayı ila ( cos θ , sin θ ) . Böylece orantılıdır.
yoğunluğu
Bu yoğunluktan, örneğin, verimi 8 / π - 2 olan bir reddetme yöntemi kullanarak örnekleme yapabiliriz ).
Koordinat radyal koşullu yoğunluk ile orantılıdır r d r arasındaki r = 1 ve r = . Bu, CDF'nin kolayca ters çevrilmesi ile örneklenebilir.
Bağımsız numuneler üretersek , Kartezyen koordinatlara ( x i , y i ) dönüş bu oktanı örnekler. Numuneler bağımsız olduğu için, koordinatları rastgele değiştirmek, istendiği gibi birinci kadranda bağımsız bir rasgele numune üretir. (Rastgele değiş tokuşlar, gerçekleşecek kaç gerçekleşmenin değişeceğini belirlemek için yalnızca tek bir Binom değişkeni oluşturulmasını gerektirir.)
Her biri, gerçekleştirilmesi , ortalama, tek tip bir varyant ile (için, ihtiyaç R ) artı 1 / ( 8 π - 2 ) iki katı homojen için (dağılımı özellikleri İçeride ISTV melerin RWMAIWi'nin ) ve (hızlı) hesaplama küçük bir miktar. Bu 4 / ( π - 4 ) ≈ nokta başına değişmektedir (ki bu elbette iki koordinatı vardır). Tüm ayrıntılar aşağıdaki kod örneğinde verilmiştir. Bu rakam, üretilen yarım milyondan fazla noktanın 10.000'ini çiziyor.
İşte R
bu simülasyonu üreten ve zamanlayan kod.
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")
Şimdiye kadar @cardinal, @whuber ve @ stephan-kolassa'nın diğer çözümlerden daha basit, daha verimli ve / veya hesaplamalı olarak daha ucuz olması gereken aşağıdaki çözümü öneriyorum.
Aşağıdaki basit adımları içerir:
1) İki standart tek tip numune çizin:
2b) Swap and if .
3) Reject the sample if inside the unit circle (acceptance should be around 72%), i.e.:
The intuition behind this algorithm is shown in the figure.
Steps 2a and 2b can be merged into a single step:
2) Apply shear transformation and swap
The following code implements the algorithm above (and tests it using @whuber's code).
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")
Some quick tests yield the following results.
Algorithm /stats//a/258349 . Best of 3: 0.33 seconds per million points.
This algorithm. Best of 3: 0.18 seconds per million points.
Well, more efficiently can be done, but I sure hope you are not looking for faster.
The idea would be to sample an value first, with a density proportional to the length of the vertical blue slice above each value:
Wolfram helps you to integrate that:
So the cumulative distribution function would be this expression, scaled to integrate to 1 (i.e., divided by ).
Now, to generate your value, pick a random number , uniformly distributed between and . Then find such that . That is, we need to invert the CDF (inverse transform sampling). This can be done, but it's not easy. Nor fast.
Finally, given , pick a random that is uniformly distributed between and .
Below is R code. Note that I am pre-evaluating the CDF at a grid of values, and even then this takes quite a few minutes.
You can probably speed the CDF inversion up quite a bit if you invest some thinking. Then again, thinking hurts. I personally would go for rejection sampling, which is faster and far less error-prone, unless I had very good reasons not to.
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="")