2016-04-15 151 views
1

我試圖儘可能減少總結伯努利試驗序列輸出的函數的執行時間。生成沒有循環的隨機數

這是我的工作,但慢的方法:

set.seed(28100) 
sim <- data.frame(result = rep(NA, 10)) 
for (i in 1:nrow(sim)) { 
    sim$result[i] <- sum(rbinom(1200, size = 1, prob = 0.2)) 
} 
sim 
# result 
# 1  268 
# 2  230 
# 3  223 
# 4  242 
# 5  224 
# 6  218 
# 7  237 
# 8  254 
# 9  227 
# 10 247 

我怎麼能獲得同樣的結果沒有一個for循環?

我想這...

set.seed(28100) 
sim <- data.frame(result = rep(sum(rbinom(1200, size = 1, prob = 0.2)), 10)) 
sim 
# result 
# 1  269 
# 2  269 
# 3  269 
# 4  269 
# 5  269 
# 6  269 
# 7  269 
# 8  269 
# 9  269 
# 10 269 

但顯然只執行一次的rep()的說法。

+0

我會告訴你我的解答,但單行解決方案是'rbinom(10,size = 1200,prob = 0.2)'。 – Gregor

回答

5

二項分佈定義爲伯努利試驗的總和。

# this line from your question 
sum(rbinom(1200, size = 1, prob = 0.2)) 
# is equivalent to this 
rbinom(1, size = 1200, prob = 0.2) 

# and replicating it 
replicate(expr = sum(rbinom(1200, size = 1, prob = 0.2)), n = 10) 
# is equivalent to setting n higher: 

     ### This is the only line of code you need! #### 
rbinom(10, size = 1200, prob = 0.2) 

在我的(相當慢的)筆記本電腦上,100,000次模擬需要大約0.01秒,1M模擬需要0.12秒。

修改@ eipi的很好的標杆,這是比其他方法快約700-900倍

  expr  min  lq  mean median  uq  max neval cld 
     binom 1.324 1.377 1.607959 1.413 1.931 2.306 10 a 
    replicate 716.300 737.200 756.288641 749.900 765.300 812.400 10 b 
     sapply 706.300 743.300 778.863587 763.800 853.500 860.300 10 b 
matrixColSums 838.800 870.000 893.813083 894.800 907.500 978.200 10 c 

基準代碼(現在bug修復!):

nn = 10000 
n_bern = 1200 
library(microbenchmark) 
print(
    microbenchmark::microbenchmark(
     replicate = 
      replicate(nn, sum(rbinom(
       n_bern, size = 1, prob = 0.2 
      ))) 
     , 
     matrixColSums = 
      colSums(matrix(
       rbinom(n_bern * nn, size = 1, prob = 0.2), ncol = nn 
      )), 
     sapply = sapply(
      1:nn, 
      FUN = function(x) { 
       sum(rbinom(n_bern, size = 1, prob = 0.2)) 
      } 
     ), 
     binom = rbinom(nn, size = n_bern, prob = 0.2), 
     times = 10 
    ), 
    order = "median", 
    signif = 4 
) 
+0

如果你看看你的答案中的'replicate'代碼,你可以看到我沒有正確的參數值(1和12而不是1200和1)。我正在朝着類似於你的答案的方向前進,但我想我必須在運行的時候進行計時,而不是事先做好準備。無論如何,「複製」並不比其他兩種方法快,而你的顯然是要走的路。我只是想讓你知道,所以你可以更正代碼和'replicate'方法的時間點(我已經更正了我的答案)。 – eipi10

+0

有趣的是,當我爲基準參數化'nn'時,我也開始拉出一個'n_bernoulli = 1200',但是當我得到你的代碼時,你只有12個 - 我認爲你正在做一些奇怪的事情來解釋它其他地方 - 我沒有花時間思考這個問題。 – Gregor

2

這個怎麼樣:

set.seed(28100) 
sims <- 10 
n <- 1200 
r <- rbinom(n*sims, size = 1, prob = 0.2) 
r <- matrix(r, ncol=sims) 
colSums(r) 

對我來說,它是關於快兩倍擁有10萬個模擬(6對13秒爲單位),但R. Schifini的和eipi10解決方案是快一點(〜5.5秒)

1

執行以下操作:

sim = rep(NA, 10) 
sapply(sim,FUN = function(x) {sum(rbinom(1200, size = 1, prob = 0.2))}) 

結果:

[1] 216 231 234 249 249 236 255 251 231 244 

然後轉換爲一個數據幀

2
set.seed(28100) 
nsim=10 
sim = data.frame(result=replicate(nsim, sum(rbinom(1200, size=1, prob=0.2)))) 

sim 
result 
1  268 
2  230 
... 
9  227 
10 247 

下面是10,000模擬的各種方法的一些定時:

microbenchmark::microbenchmark(
    replicate = {nsim=10000 
    data.frame(result=replicate(nsim, sum(rbinom(1200, size=1, prob=0.2))))}, 
    matrixColSums = { 
    sims <- 10000 
    n <- 1200 
    r <- rbinom(n*sims, size = 1, prob = 0.2) 
    r <- matrix(r, ncol=sims) 
    data.frame(result=colSums(r)) }, 
    sapply = data.frame(result=sapply(1:10000, FUN = function(x) {sum(rbinom(1200, size = 1, prob = 0.2))})), 
    times=10 
) 
Unit: milliseconds 
     expr  min  lq  mean median  uq  max neval cld 
    replicate 584.2389 597.5571 615.7545 614.0977 630.7354 648.8328 10 a 
matrixColSums 655.0608 664.2053 684.0069 682.1868 702.1426 713.0240 10 b 
     sapply 589.9830 610.5784 626.8738 629.2161 642.2589 660.6092 10 a 
0

矢量化是關鍵。

主要節省時間(至少對於大n)是使用sample

例如對於

n <- 1e7 
sample(0:1, n, replace=TRUE) 

大約需要0。2秒,而

for(i in 1:n) sample(0:1, 1) 

大約需要24秒。矢量化操作通常可以取代循環,但瞭解何時何地取決於對可用功能的熟悉程度,以滿足您的需求。