39

我正在使用 R 使用蒙特卡罗过程构建基于代理的模型。这意味着我得到了许多使用某种随机引擎的函数。为了获得可重复的结果,我必须修复种子。但是,据我了解,我必须在每次随机抽取或抽样之前设置种子。这是一个真正的颈部疼痛。有没有办法修复种子?

set.seed(123)
print(sample(1:10,3))
# [1] 3 8 4
print(sample(1:10,3))
# [1]  9 10  1
set.seed(123)
print(sample(1:10,3))
# [1] 3 8 4
4

6 回答 6

35

有多种选择,具体取决于您的具体需求。我怀疑第一个选项,最简单的还不够,但我的第二个和第三个选项可能更合适,第三个选项最自动化。

选项1

如果您事先知道使用/创建随机数的函数将始终绘制相同的数字,并且您不重新排序函数调用或在现有函数调用之间插入新调用,那么您需要做的就是设置一次种子。实际上,您可能不想继续重置种子,因为您将继续为每个函数调用获取相同的随机数集。

例如:

> set.seed(1)
> sample(10)
 [1]  3  4  5  7  2  8  9  6 10  1
> sample(10)
 [1]  3  2  6 10  5  7  8  4  1  9
> 
> ## second time round
> set.seed(1)
> sample(10)
 [1]  3  4  5  7  2  8  9  6 10  1
> sample(10)
 [1]  3  2  6 10  5  7  8  4  1  9

选项 2

如果您真的想确保一个函数使用相同的种子并且您只想设置一次,请将种子作为参数传递:

foo <- function(...., seed) {
  ## set the seed
  if (!missing(seed)) 
    set.seed(seed) 
  ## do other stuff
  ....
}

my.seed <- 42
bar <- foo(...., seed = my.seed)
fbar <- foo(...., seed = my.seed)

(其中....意味着您的函数的其他参数;这是伪代码)。

选项 3

如果您想更加自动化,那么您可能会滥用该options机制,如果您只是在脚本中执行此操作(对于包您应该使用自己的选项对象),这很好。然后你的函数可以寻找这个选项。例如

foo <- function() {
  if (!is.null(seed <- getOption("myseed")))
    set.seed(seed)
  sample(10)
}

然后在使用中我们有:

> getOption("myseed")
NULL
> foo()
 [1]  1  2  9  4  8  7 10  6  3  5
> foo()
 [1]  6  2  3  5  7  8  1  4 10  9
> options(myseed = 42)
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
> foo()
 [1] 10  9  3  6  4  8  5  1  2  7
于 2013-12-17T02:33:04.920 回答
28

我认为这个问题令人困惑。在示例中,为整个会话设置种子。但是,这并不意味着每次print(sample))在运行期间使用该命令时它都会产生相同的一组数字。这不会像一个随机过程,因为可以完全确定每次都会出现相同的三个数字。相反,实际发生的情况是,一旦设置了种子,每次运行脚本时,都会使用相同的种子来生成伪随机数字选择,即看起来像是随机但实际上是随机的数字使用您设置的种子通过可重复的过程产生。

如果您从头重新运行整个脚本,您将重现那些看起来随机但不是随机的数字。因此,在示例中,第二次将种子设置为 123,输出再次为 9、10 和 1,这正是您期望看到的,因为该过程又从头开始。如果您要通过编写 继续重现您的第一次运行print(sample(1:10,3)),那么第二组输出将再次为 3、8 和 4。

所以这个问题的简短回答是:如果你想设置一个种子来创建一个可重复的过程,那么做你已经做过的事情并设置一次种子;但是,您不应该每次随机抽奖之前设置种子,因为这将再次从头开始伪随机过程。

这个问题很老,但在搜索结果中仍然很高,似乎值得扩展 Spacedman 的答案。

于 2017-01-05T16:13:33.907 回答
9

如果您想始终从随机过程返回相同的结果,只需始终保持种子集:

addTaskCallback(function(...) {set.seed(123);TRUE})

现在每次的输出都是一样的:

print(sample(1:10,3))
# [1] 3 8 4
print(sample(1:10,3))
# [1] 3 8 4
于 2019-09-18T09:50:38.567 回答
3

没必要。尽管结果因样本而异(您几乎肯定想要,否则随机性非常值得怀疑),但每次运行的结果都是相同的。看,这是我机器的输出。

> set.seed(123)
> sample(1:10,3)
[1] 3 8 4
> sample(1:10,3)
[1]  9 10  1
于 2013-12-17T02:21:55.483 回答
2

你可以做一个包装函数,像这样:

> wrap.3.digit.sample <- function(x) {
+    set.seed(123)
+    return(sample(x, 3))
+ }
> wrap.3.digit.sample(c(1:10))
[1] 3 8 4
> wrap.3.digit.sample(c(1:10))
[1] 3 8 4

可能有一种更优雅的方式,我相信有人会加入它。但是,如果他们不这样做,这应该会让你的生活更轻松。

于 2013-12-17T02:22:45.673 回答
0

我建议您set.seed在调用 R 中的每个随机数生成器之前。我认为您需要的是蒙特卡洛模拟的可重复性。如果在一个for循环中,你可以set.seed(i)在调用之前sample,这保证是完全可重现的。在您的外部函数中,您可以指定一个参数seed=1,以便在for循环中使用set.seed(i+seed).

于 2013-12-17T02:29:29.053 回答