您可以尝试使用数值方法进行逆采样。根据您的要求,这更多是关于方法的透明度而不是效率。
此函数将在给定范围内对给定函数进行数值积分(尽管它会修剪无限值)
cdf <- function(f, lower_bound, upper_bound)
{
if(lower_bound < -10000) lower_bound <- -10000 # Trim large negatives
if(upper_bound > 10000) upper_bound <- 10000 # Trim large positive
x <- seq(lower_bound, upper_bound, length.out = 100001) # Finely divide x axis
delta <- mean(diff(x)) # Get delta x (i.e. dx)
mid_x <- (x[-1] + x[-length(x)])/2 # Get the mid point of each slice
result <- cumsum(delta * f(mid_x)) # sum f(x) dx
result <- result / max(result) # normalize
list(x = mid_x, cdf = result) # return both x and f(x) in list
}
为了得到逆,我们在从 0 到 1 之间的均匀分布中抽取的随机数的 cdf 中找到最接近的值。然后我们看到 x 的哪个值对应于 cdf 的那个值。我们希望能够一次对 n 个样本执行此操作,因此我们使用sapply
:
inverse_sample <- function(f, n = 1, lower_bound = -1000, upper_bound = 1000)
{
CDF <- cdf(f, lower_bound, upper_bound)
samples <- runif(n)
sapply(samples, function(s) CDF$x[which.min(abs(s - CDF$cdf))])
}
我们可以通过绘制结果的直方图来测试它。我们将从正态分布的密度函数(dnorm
在 R 中)开始,抽取 1000 个样本并绘制它们的分布:
hist(inv_sample(dnorm, 1000))
我们可以对指数分布做同样的事情,这次将积分限制设置在 0 到 100 之间:
hist(inv_sample(dexp, 1000, 0, 100))
最后我们可以用你自己的例子做同样的事情:
f <- function(x) 3/2/(1 + x)^2
hist(inv_sample(f, 1000, 0, 10))