使函数设置随机种子独立

时间:2015-06-26 01:22:26

标签: r random random-sample random-seed

有时我想编写一个随机函数,它总是为特定输入返回相同的输出。我总是通过在函数顶部设置随机种子然后继续实现它。考虑以这种方式定义的两个函数:

sample.12 <- function(size) {
  set.seed(144)
  sample(1:2, size, replace=TRUE)
}
rand.prod <- function(x) {
  set.seed(144)
  runif(length(x)) * x
}

sample.12返回从集{1, 2}中随机抽样的指定大小的向量,rand.prod将指定向量的每个元素乘以从[0, 1]统一选择的随机值。通常情况下,我希望x <- sample.12(10000) ; rand.prod(x)[0, 1]范围内的pdf 3/4和[1, 2]范围内的1/4进行“步进”分布,但由于我不幸选择相同的上面的随机种子我看到了不同的结果:

x <- sample.12(10000)
hist(rand.prod(x))

enter image description here

我可以通过将其中一个函数中的随机种子更改为其他值来解决此问题。例如,set.seed(10000)中的rand.prod我得到了预期的分布:

enter image description here

Previously on SO这种使用不同种子的解决方案已被接受为生成独立随机数流的最佳方法。但是,我发现解决方案并不令人满意,因为具有不同种子的流可能彼此相关(甚至可能highly related to one another);实际上,根据?set.seed

,它们甚至可能产生相同的流
  

不能保证种子的不同值会以不同的方式为RNG播种,尽管任何例外情况都非常罕见。

有没有办法在R中实现一对随机函数:

  1. 始终为特定输入返回相同的输出,
  2. 通过不仅仅使用不同的随机种子来增强其随机性来源之间的独立性?

1 个答案:

答案 0 :(得分:9)

我已经挖掘了更多内容,看起来rlecuyer包提供了独立的随机流:

  

提供随机数生成器的C实现的接口,其具有由L&#39; Ecuyer等人(2002)开发的多个独立流。该软件包的主要目的是在并行R应用程序中使用此随机数生成器。

第一步是独立流的全局初始化:

library(rlecuyer)
.lec.CreateStream(c("stream.12", "stream.prod"))

然后需要修改每个函数以将适当的流重置为其开始状态(.lec.RestartStartStream),将R随机数生成器设置为适当的流(.lec.CurrentStream),然后设置R随机数生成器返回到调用函数之前的状态(.lec.CurrentStreamEnd)。

sample.12 <- function(size) {
  .lec.ResetStartStream("stream.12")
  .lec.CurrentStream("stream.12")
  x <- sample(1:2, size, replace=TRUE)
  .lec.CurrentStreamEnd()
  x
}
rand.prod <- function(x) {
  .lec.ResetStartStream("stream.prod")
  .lec.CurrentStream("stream.prod")
  y <- runif(length(x)) * x
  .lec.CurrentStreamEnd()
  y
}

这满足&#34;总是在给定相同输入的情况下返回相同的输出&#34;要求:

all.equal(rand.prod(sample.12(10000)), rand.prod(sample.12(10000)))
# [1] TRUE

在我们的示例中,流似乎也独立运行:

x <- sample.12(10000)
hist(rand.prod(x))

enter image description here

请注意,这不会在我们的脚本运行中提供一致的值,因为每次调用.lec.CreateStream都会给出不同的初始状态。为了解决这个问题,我们可以注意每个流的初始状态:

.lec.GetState("stream.12")
# [1] 3161578179 1307260052 2724279262 1101690876 1009565594  836476762
.lec.GetState("stream.prod")
# [1]  596094074 2279636413 3050913596 1739649456 2368706608 3058697049

然后我们可以将脚本开头的流初始化更改为:

library(rlecuyer)
.lec.CreateStream(c("stream.12", "stream.prod"))
.lec.SetSeed("stream.12", c(3161578179, 1307260052, 2724279262, 1101690876, 1009565594, 836476762))
.lec.SetSeed("stream.prod", c(596094074, 2279636413, 3050913596, 1739649456, 2368706608, 3058697049))

现在对sample.12的调用和rand.prod将匹配对脚本的调用。