在一个内部生成统一的点米m-维球

机器算法验证 r 模拟 均匀分布 功能
2022-03-26 05:42:41

目前的问题是本网站上其他一些问题的后续,这些问题询问如何在圆盘内生成均匀点(例如,请参见此处此处此处)。该问题的自然扩展是在一个m带中心的cRm和半径r0. 也就是说,我们要从以下分布生成 IID 随机变量:

XU(B(c,r))B(c,r){xRm|||xc||r}.

我们如何在这个空间上生成 IID 均匀点?有没有一种简单的编程方法?

2个回答

解决这个问题的一种简单有效的方法是使用著名的Box-Mueller 变换的变体,它将正态分布与球上的均匀分布联系起来。如果我们生成一个由 IID 标准正态随机变量和一个随机变量(独立于第一个随机向量),那么我们可以将统一的兴趣点构造为:Z=(Z1,...,Zm)UU(0,1)

X=c+rU1/mZ||Z||.

在下面的代码中,我们创建了一个实现此方法的R函数。runifball该功能允许用户生成n随机矢量,这些矢量是球上具有任意中心、半径和尺寸的点。

runifball <- function(n, centre = 0, center = centre, radius = 1) {
  
  #Check inputs
  if (!missing(centre) && !missing(center)) {
  if (sum((centre - center)^2) < 1e-15) { 
                 warning("specify 'centre' or 'center' but not both") } else {
                    stop("Error: specify 'centre' or 'center' but not both") } }
  if (radius < 0) { stop("Error: radius must be non-negative") }
  
  #Create output matrix
  m   <- length(center)
  OUT <- matrix(0, nrow = m, ncol = n)
  rownames(OUT) <- sprintf("x[%s]", 1:m)
  
  #Generate uniform values on circle
  UU  <- runif(n, min = 0, max = radius)
  ZZ  <- matrix(rnorm(n*m), nrow = m, ncol = n)
  for (i in 1:n) {
    OUT[, i] <- center + radius*UU[i]^(1/m)*ZZ[, i]/sqrt(sum(ZZ[, i]^2)) }
  
  OUT }

这是一个使用此函数在二维磁盘上均匀生成随机点的示例。该图显示,这些点在指定球上确实是均匀的。

#Generate points uniformly on a disk
set.seed(1)
n      <- 10^5
CENTRE <- c(5, 3)
RADIUS <- 3
UNIF   <- runifball(n, centre = CENTRE, radius = RADIUS)

#Plot the points
plot(UNIF, 
     col = rgb(0, 0, 0, 0.05), pch = 16, asp = 1,
     main = 'Points distributed uniformly over a circle', xlab = 'x', ylab = 'y')
points(x = CENTRE[1], y = CENTRE[2], col = 'red', pch = 16)

球上的点

最简单且最不容易出错的方法 - 对于低维(见下文!) - 仍然是拒绝采样维超立方体中挑选均匀分布的点,然后拒绝所有落在球外的点。m

runifball <- function(n, centre = 0, center = centre, radius = 1) {
  
  #Check inputs
  if (!missing(centre) && !missing(center)) {
  if (sum((centre - center)^2) < 1e-15) { 
                 warning("specify 'centre' or 'center' but not both") } else {
                    stop("Error: specify 'centre' or 'center' but not both") } }
  if (radius < 0) { stop("Error: radius must be non-negative") }

  n_to_generate <- 2^length(center)*gamma(length(center)/2+1)*n/pi^(length(center)/2) # see below
  
  original_sample_around_origin <- 
      matrix(replicate(length(center),runif(n_to_generate ,-radius,radius)),nrow=n_to_generate )
  index_to_keep <- rowSums(original_sample_around_origin^2)<radius^2
  original_sample_around_origin[index_to_keep,]+
      matrix(center,nrow=sum(index_to_keep),ncol=length(center),byrow=TRUE)
}

这是维磁盘的应用程序:m=2

#Generate points uniformly on a disk
set.seed(1)
n      <- 10^5
CENTRE <- c(5, 3)
RADIUS <- 3
UNIF   <- runifball(n, centre = CENTRE, radius = RADIUS)

#Plot the points
plot(UNIF, 
     col = rgb(0, 0, 0, 0.05), pch = 16, asp = 1,
     main = 'Points distributed uniformly over a circle', xlab = 'x', ylab = 'y')
points(x = CENTRE[1], y = CENTRE[2], col = 'red', pch = 16)

球

再一次,我们将需要最初生成更多的点,因为我们会拒绝一些。具体来说,我们希望保持维球到包围它维超立方体的体积。所以我们可以从生成开始,期望得到个点(这是上面代码采用的方法),或者只是开始生成直到我们保留πm22mΓ(m2+1)mm2mΓ(m2+1)nπm2nn

在任何一种情况下,我们最初需要在超立方体中绘制的点数,以便(期望)最终在球中得到一个点,随着维数的增加而迅速增加:m

最初生成的点数

(注意对数垂直轴!)

m <- 2:20
plot(m,2^m*gamma(m/2+1)/pi^(m/2),type="o",pch=19,log="y",
    xlab="Dimension (m)")

这只是一个事实的结果,对于大维超立方体的大部分体积在角落,而不是在中心(球所在的位置)。所以拒绝抽样可能只是低维度的一种选择。mm