我正在教一个统计课,我让学生通过使用R的模拟来探索概率和统计学方面的问题。最近,在滚动5个骰子时获得正好两个6的可能性存在一些混淆。答案是选择(5,2)* 5 ^ 3/6 ^ 5,但有些学生确信“顺序不重要”;即答案应选择(5,2)*选择(25,3)/选择(30,5)。我认为让它们模拟滚动5个骰子数千次,跟踪每个实验的经验概率,然后多次重复实验将会很有趣。问题是上面的两个数字足够接近,很难得到模拟以统计上显着的方式梳理出差异(当然我可能只是做错了)。我尝试滚动5个骰子100000次,然后重复实验10000次。这需要一个小时左右才能在我的i7 linux机器上运行,并且仍然有25%的几率选择正确的答案(5,2)*选择(25,3)/选择(30,5)。所以我将每个实验的骰子数量增加到10 ^ 6。现在代码已运行超过2天,并且没有显示完成的迹象。我对此感到困惑,因为我只增加了一个数量级的操作次数,这意味着运行时间应该接近10小时。
第二个问题:有更好的方法吗?见下面发布的代码:
probdist = rep(0,10000)
for (j in 1:length(probdist))
{
outcome = rep(0,1000000)
for (k in 1:1000000)
{
rolls = sample(1:6, 5, replace=T)
if (length(rolls[rolls == 6]) == 2) outcome[k] = 1
}
probdist[j] = sum(outcome)/length(outcome)
}
答案 0 :(得分:3)
一个好的经验法则是永远不要在for
中写一个R
循环。这是另一种解决方案:
doSample <- function()
{
sum(sample(1:6,size=5,replace=TRUE)==6)==2
}
> system.time(samples <- replicate(n=10000,expr=doSample()))
user system elapsed
0.06 0.00 0.06
> mean(samples)
[1] 0.1588
> choose(5,2)*5^3/6^5
[1] 0.160751
$ 10,000 $样本似乎不太准确。好的$ 100,000 $:
> system.time(samples <- replicate(n=100000,expr=doSample()))
user system elapsed
0.61 0.02 0.61
> mean(samples)
[1] 0.16135
答案 1 :(得分:2)
我最初给M. Berk的正确答案检查是因为他/她建议使用R replicate()函数。进一步的调查已被迫取消我之前的认可。事实证明,replicate()只是sapply()的包装器,它实际上并没有为for循环提供任何性能优势(这似乎是一种常见的误解)。无论如何,我准备了3个版本的模拟,2个使用for循环,1个使用replicate,如建议的那样,并且每次从一个新的R会话开始一个接一个地运行它们,以便比较执行时间:
# dice26dist1.r: For () loop version with unnecessary array allocation
probdist = rep(0,100)
for (j in 1:length(probdist))
{
outcome = rep(0,1000000)
for (k in 1:1000000)
{
rolls = sample(1:6, 5, replace=T)
if (length(rolls[rolls == 6]) == 2) outcome[k] = 1
}
probdist[j] = sum(outcome)/length(outcome)
}
system.time(源( 'dice26dist1.r'))
用户系统已过
596.365 0.240 598.614
# dice26dist2.r: For () loop version
probdist = rep(0,100)
for (j in 1:length(probdist))
{
outcomes = 0
for (k in 1:1000000)
{
rolls = sample(1:6, 5, replace=T)
if (length(rolls[rolls == 6]) == 2) outcomes = outcomes + 1
}
probdist[j] = outcomes/1000000
}
system.time(源( 'dice26dist2.r'))
用户系统已过
506.331 0.076 508.104
# dice26dist3.r: replicate() version
doSample <- function()
{
sum(sample(1:6,size=5,replace=TRUE)==6)==2
}
probdist = rep(0,100)
for (j in 1:length(probdist))
{
samples = replicate(n=1000000,expr=doSample())
probdist[j] = mean(samples)
}
system.time(源( 'dice26dist3.r'))
用户系统已过
804.042 0.472 807.250
通过任何system.time指标,您可以看到replicate()版本比任何for循环版本更慢。我原本以为我的问题主要是由于缓存未命中,分配了百万字符结果[]数组,但是比较dice26dist1.r和dice26dist2.r的时间表明这只会对性能产生名义上的影响(尽管对系统的影响很大)时间相当可观:> 300%差异。
有人可能会说我在所有三个模拟中仍然使用for循环,但据我所知,这在模拟随机过程时是完全不可避免的;我必须每次模拟实际经历随机过程(在这种情况下,滚动5个模具)。我很想知道任何可以避免使用for循环的技术(当然,这种技术可以提高性能)。我知道这个问题可以非常有效地用于并行化,但我说的是使用单个R会话 - 有没有办法让它更快?
答案 2 :(得分:2)
矢量化几乎总是优于任何for循环。在这种情况下,您应该首先通过生成所有骰子投掷,然后检查每组中五个等于6的数量来看到实质性的加速。
set.seed(5)
N <- 1e6
foo <- matrix(sample(1:6, 5*N, replace=TRUE), ncol=5)
p <- mean(rowSums(foo==6)==2)
se <- sqrt(p*(1-p)/N)
p
## [1] 0.160382
这是一个95%的置信区间:
p + se*qnorm(0.975)*c(-1,1)
## [1] 0.1596628 0.1611012
我们可以看到真正的答案(ans1
)在区间内,但是错误的答案(ans2
)不是,或者我们可以进行显着性测试;测试真实答案时的p值是0.31,但假答案是0.0057。
(ans1 <- choose(5,2)*5^3/6^5)
## [1] 0.160751
pnorm(abs((ans1-p)/se), lower=FALSE)*2
## [1] 0.3145898
ans2 <- choose(5,2)*choose(25,3)/choose(30,5)
## [1] 0.1613967
pnorm(abs((ans2-p)/se), lower=FALSE)*2
## [1] 0.005689008
请注意,我立即生成所有掷骰子;如果记忆是一个问题,你可以把它分成碎片并组合,就像你在原始帖子中所做的那样。这可能是导致您意外加速的原因;如果有必要使用交换内存,这将大大减慢它。如果是这样,最好增加运行循环的时间,而不是循环内的滚动次数。