是什么导致了这种奇怪的随机数生成器行为?

What's causing this strange random number generator behaviour?

我正在尝试为某些模拟研究制作一些可重现的示例,因此我使用 set.seed 从 "random" 部分获得一致的结果。但是,我注意到我有时会得到不同的结果。

我试图将其缩小到一个最小的可重现示例,但似乎需要多个元素才能触发错误。

这是应该发生的事情:

library(RcppExamples)

set.seed(1)
RcppRNGs(3)
#        rnorm         rt rpois
# 1 -0.6264538  1.5144787     1
# 2  0.1836433 -0.3536704     0
# 3 -0.8356286 11.4386179     1
rnorm(3)
# [1]  1.12493092 -0.04493361 -0.01619026

这段代码似乎是激活错误的原因:

library(plyr)
S <- llply(1:100, function(.) Sys.sleep(1), .progress="text")
# |==========================                                        |  25%

注意:

  1. 我在 llply 完成之前点击了 "stop" 按钮。
  2. 我知道您通常会在这种情况下使用 rlply,但这不会触发错误。

现在是这样:

set.seed(1)
RcppRNGs(3)
#        rnorm         rt rpois
# 1 -0.6264538  1.5144787     1
# 2  0.1836433 -0.3536704     0
# 3 -0.8356286 11.4386179     1
rnorm(3)
# [1] -0.6264538  0.1836433 -0.8356286

我应该得到与第一个示例相同的结果,但是 rnorm 给出了不同的数字。

问题似乎是 Rcpp 不再影响随机数种子:

set.seed(1)
rnorm(3)
# [1] -0.6264538  0.1836433 -0.8356286

考虑到这里所有的交互部分,我应该向谁提交错误报告?


这是我的 sessionInfo() 输出:

R version 3.1.2 (2014-10-31)
Platform: x86_64-w64-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_New Zealand.1252  LC_CTYPE=English_New Zealand.1252    LC_MONETARY=English_New Zealand.1252 LC_NUMERIC=C                        
[5] LC_TIME=English_New Zealand.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] plyr_1.8.1         RcppExamples_0.1.6 Rcpp_0.11.3       

loaded via a namespace (and not attached):
[1] tools_3.1.2

llply 下降到 loop_apply 最终调用此函数:

// [[Rcpp::export]]
List loop_apply(int n, Function f) {
  List out(n);

  for(int i = 0; i < n; ++i) {
    out[i] = f(i + 1);
  }

  return out;
}

因此,作为 Rcpp::export 业务的一部分,我们接到了 enterRNGScope

的电话
    // [[Rcpp::register]]
    unsigned long enterRNGScope() {
        if (RNGScopeCounter == 0) GetRNGstate();
        RNGScopeCounter++;
        return RNGScopeCounter ;
    }

并且在 loop_apply 结束时我们应该接到 exitRNGScope 的电话。

    // [[Rcpp::register]]
    unsigned long exitRNGScope() {
        RNGScopeCounter--;
        if (RNGScopeCounter == 0) PutRNGstate();
        return RNGScopeCounter ;
    }

问题是,由于中断,我们永远不会到达 exitRNGScope,因此当 RcppRNGs 调用 enterRNGScope 时,我们认为不需要调用 GetRNGstate 所以种子处理不当。

也许 loop_apply 应该调用 checkUserInterrupt,但我不确定这能否完全解决问题。