在 R 中有效地采样彩票号码

Pro*_*ino 3 r function sampling

我想编写一个函数,对n 个彩票进行抽样,每个彩票有6 个号码,从1 到 45,每个号码都没有替换。但是,我需要有效地执行此操作,这意味着没有循环或类似循环的函数。(我想 Rcpp 也可以工作,但我更喜欢基 R 中的矢量化解决方案)

无限制求解:

lottery_inef <- function(n){
  
 t(replicate(n,
          sample(1:45, 6)))
}
Run Code Online (Sandbox Code Playgroud)

所以在这里我得到一个矩阵,其中每一行对应一张彩票。现在,如果我想模拟数百万张彩票,这会变得非常慢,因此我对矢量化解决方案感兴趣。

我的想法是:

lottery_ef <- function(n){
  
  m <- matrix(sample(1:45, n*6, replace = TRUE), ncol = 6)
  
  # somehow subset the matrix without a loop to remove all the 
  # rows that have non-unique values as in the lottery we can only draw each number once
}
Run Code Online (Sandbox Code Playgroud)

对于高效版本,我在没有循环或 apply() 的子集点上有点迷失。如果有人可以解决这个子集问题,或者为我指出一个完全不同的方向,这将引导我找到解决方案,我将不胜感激。

Gre*_*gor 6

replicate在这个规模上实际上并没有那么好。通过即时编译(现在在 R 中使用了几年),for循环可以更快,尤其是当我们可以精确地预分配数据结构时。我们还可以避免t()

lottery_inef <- function(n){
 t(replicate(n,
          sample(1:45, 6)))
}

lottery_preall <- function(n){
  m = matrix(NA_integer_, nrow = n, ncol = 6)
  for(i in 1:n) {
    m[i, ] = sample.int(45L, size = 6)
  }
  m
}

nn = 1e6
microbenchmark::microbenchmark(
  lottery_inef(nn), 
  lottery_preall(nn),
  times = 2
)
# Unit: seconds
#                expr      min       lq     mean   median       uq      max neval
#    lottery_inef(nn) 9.400862 9.400862 9.571756 9.571756 9.742649 9.742649     2
#  lottery_preall(nn) 4.948216 4.948216 5.454482 5.454482 5.960749 5.960749     2
Run Code Online (Sandbox Code Playgroud)

replicate在 a 中累积结果,list然后需要检查每个维度的维度,然后再决定可以将其简化为矩阵,并且必须进行转换。使用预先分配的整数矩阵跳过所有这些开销,以获得大约 2 倍的加速。

我们也可以比较,比如说vapply(快速测试显示vapply比循环慢一点),但我认为要获得更高的速度,你需要并行运行 - 这在这里是一个不错的选择,并且可以可能会让您获得几乎等于您使用的内核数量的加速。

sample.int 几乎只是调用 C 代码,因此使用 Rcpp 可能不会做得更好 - 我认为并行化是提高速度的最佳选择。