加快循环中重复生成指数随机变量

时间:2017-08-25 05:37:22

标签: r performance random rcpp

我正在实现一种算法,作为其中的一部分,我需要生成指数随机变量。不幸的是,我无法真正避免循环,因为每个生成的随机变量都取决于前一个,所以我认为矢量化是不可能的。我在这一代人中做了一些计算,但瓶颈(目前)是一代。此时我假设N将很大(N> = 1,000,000)。

以下是一些示例代码:

N <- 1e7
#Preallocate
x <- rep(0, times=N)

#Set a starting seed
x[1] <- runif(1)

for(i in 2:N) {
    #Do some calculations
    x[i] <- x[i-1] + rexp(1, x[i-1])  #Bottleneck
    #Do some more calculations
}

我怎样才能加快速度?我曾尝试在Rcpp中实施,但在这种情况下它似乎没有做太多。在每次迭代中,我是否有另一种聪明的方法可以绕过rexp()调用?

2 个答案:

答案 0 :(得分:3)

我们可以使用如果X~Exp(λ)然后kX_Exp(λ/ k) (source: Wikipedia)来加速代码的事实。通过这种方式,我们可以预先使用rate = 1进行所有随机绘制,然后在循环内进行划分以适当地缩放它们。

draws = rexp(N, rate = 1)
x <- rep(0, times = N)
x[1] <- runif(1)
for(i in 2:N) {
    #Do some calculations
    x[i] <- x[i-1] + draws[i] / x[i-1] 
    #Do some more calculations
}

N = 1e6值的微基准测试表明这个速度提高了约14倍:

N <- 1e6
draws = rexp(N, rate = 1)
x <- rep(0, times = N)
x[1] <- runif(1)

microbenchmark::microbenchmark(
    draw_up_front = {
        draws = rexp(N, rate = 1)
        for (i in 2:N)
            x[i] <- x[i - 1] + draws[i] / x[i - 1]
    },
    draw_one_at_time = {
        for (i in 2:N)
            x[i] <- x[i - 1] + rexp(1, x[i - 1])
    },
    times = 10
)
# Unit: milliseconds
#              expr       min        lq      mean    median        uq       max neval cld
#     draw_up_front  153.9547  156.6552  159.9622  160.1901  161.9803  167.2831    10  a 
#  draw_one_at_time 2207.1997 2212.0460 2280.1265 2236.5197 2332.9913 2478.5104    10   b

答案 1 :(得分:3)

蛮力Rcpp解决方案:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector genExp(int N) {

  NumericVector res(N);
  double prev;

  res[0] = unif_rand();
  for (int i = 1; i < N; i++) {
    prev = res[i-1];
    res[i] = prev + exp_rand() / prev;
  }

  return res;
}

Microbenchmark与N = 1e6

Unit: milliseconds
             expr        min         lq       mean     median         uq        max neval
    draw_up_front  167.17031  168.57345  170.62292  170.18072  171.73782  175.46868    20
 draw_one_at_time 1415.01898 1465.57139 1510.81220 1502.15753 1550.07829 1623.70831    20
             rcpp   28.25466   29.33682   33.52528   29.89636   30.74908   94.38009    20

使用N = 1e7

Unit: milliseconds
          expr      min       lq      mean    median        uq       max neval
 draw_up_front 1698.730 1708.739 1737.8633 1716.1345 1752.3276 1923.3940    20
          rcpp  297.142  319.794  338.6755  327.6626  364.6308  398.1554    20