Я б хотів генерувати зразки з блакитної області, визначеної тут:
Наївним рішенням є використання відбору проб відхилення в одиничному квадраті, але це забезпечує лише ефективність (~ 21,4%).
Чи є якийсь спосіб я можу зробити вибірку більш ефективно?
Я б хотів генерувати зразки з блакитної області, визначеної тут:
Наївним рішенням є використання відбору проб відхилення в одиничному квадраті, але це забезпечує лише ефективність (~ 21,4%).
Чи є якийсь спосіб я можу зробити вибірку більш ефективно?
Відповіді:
Зроблять два мільйони очок за секунду?
Розподіл симетричний: нам потрібно лише розробити розподіл на одну восьму повного кола, а потім скопіювати його навколо інших октантів. У полярних координатах кумулятивний розподіл кута Θ для випадкового розташування ( X , Y ) при значенні θ задається площею між трикутником ( 0 , 0 ) , ( 1 , 0 ) , ( 1 , tan θ ) і дуга кола, що проходить від ( до ( cos θ , sin θ ) . Таким чином, він пропорційний
звідки його щільність
Ми можемо взяти вибірку з цієї щільності, використовуючи, скажімо, метод відкидання (який має ефективність ).
Умовна щільність радіальної координати пропорційна r d r між r = 1 та r = sec θ . Це можна пробити за допомогою легкої інверсії CDF.
Якщо ми генеруємо незалежні зразки , перетворення назад до декартових координат ( x i , y i ) вибірки цього октанта. Оскільки вибірки є незалежними, випадкове зміна координат створює незалежну випадкову вибірку з першого квадранта, як бажано. (Випадкові свопи вимагають генерування лише однієї біноміальної змінної, щоб визначити, скільки реалізується для заміни.)
Кожна така реалізація вимагає, в середньому, однієї рівномірної змінної (для R ) плюс 1 / ( 8 π - 2 ) разів двох рівномірних змінних (для Θ ) і невеликої кількості (швидкого) обчислення. Це 4 / ( π - 4 ) ≈ 4,66 змінних на точку (що, звичайно, має дві координати). Повна інформація наведена в прикладі коду нижче. Ця цифра відображає 10 000 з більш ніж півмільйона отриманих балів.
Ось Rкод, який створив це моделювання та приуротив його.
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")
Я пропоную наступне рішення, яке повинно бути простішим, ефективнішим та / або обчислювально дешевшим, ніж інші соуси від @cardinal, @whuber та @ stephan-kolassa поки що.
Він включає наступні прості кроки:
Інтуїція, що стоїть за цим алгоритмом, показана на малюнку.

Кроки 2a та 2b можна об'єднати в один крок:
Наступний код реалізує алгоритм, наведений вище (і тестує його за допомогою коду @ 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")
Деякі швидкі тести дають такі результати.
Алгоритм /stats//a/258349 . Найкраще 3: 0,33 секунди на мільйон очок.
Цей алгоритм. Найкраще 3: 0,18 секунди на мільйон очок.
Що ж, ефективніше можна зробити, але я впевнений, що ви швидше не шукаєте .
Wolfram допомагає вам інтегрувати це :
Нарешті, дано , виберіть випадковий що рівномірно розподілено між собою і .
Нижче наведено код R. Зауважте, що я попередньо оцінював CDF в сітці значення, і навіть тоді це займає досить декількох хвилин.
Напевно, ви можете трохи прискорити інверсію CDF, якщо вкладете трохи думок. Потім знову мислення боляче. Я особисто хотів би взяти вибірку відхилень, що швидше і набагато менш схильне до помилок, якщо б у мене не було дуже вагомих причин цього не робити.
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="")