为什么 withr::with_seed 和 R.utils::withSeed 在异步代码中使用时会产生不同的结果?

Why do withr::with_seed and R.utils::withSeed generate different results when used in async code?

我正在尝试使用 R 学习异步编程,以便我可以实现一个需要使用指定种子生成随机数的应用程序(始终 使用指定种子)。为此,我一直在使用 R.utils::withSeed,但我知道 withr::with_seed 也存在,所以我想我可以检查一下。

我知道随机数生成很棘手,所以我一直在尝试 运行 简单的示例来尝试理解其工作原理。我需要:

在下面的代码中,我定义了两个函数来生成随机数,将种子设置为 withr::with_seedR.utils::withSeed

不过,多个 运行 的答案似乎是一致的。

我的问题是:为什么?这是 R.utils::withSeed 中的错误,还是我误解了什么?


代码


library(future)
library(promises)

plan(multisession)


s0_R = function(seed = 1, n = 1){
  R.utils::withSeed(expr = {
    rnorm(n)
  }, seed = seed)
}

s0_w = function(seed = 1, n = 1){
  withr::with_seed(
     seed = seed,
     code = {
       rnorm(n)
     })
}


s_R = function(seed = 1, n = 1){
  future_promise(
    {
    Sys.sleep(5)
    s0_R(seed, n)
    }, 
    seed = TRUE
  )
} 

s_w = function(seed = 1, n = 1){
  future_promise(
    {
      Sys.sleep(5)
      s0_w(seed, n)
    }, 
    seed = TRUE
  )
} 

s0_R(123) %>%
  paste(" (R.utils::withSeed)\n") %>%
  cat()
# -0.560475646552213  (R.utils::withSeed)

s0_w(123) %>%
  paste(" (withr::with_seed)\n") %>%
  cat()
# -0.560475646552213  (withr::with_seed)

s_R(123) %...>%
  paste(" (async, R.utils::withSeed)\n") %...>%
  cat()

s_w(123) %...>%
  paste(" (async, withr::with_seed)\n") %...>%
  cat()

# Results occur later...
# -0.968592726552943  (async, R.utils::withSeed)
# -0.560475646552213  (async, withr::with_seed)

future 软件包将默认 RNG 类型设置为 L'Ecuyer-CMRG,而 R 的默认类型是 Mersenne-Twister。 withr::with_seed 将 RNG 类型重置为 "default"(即 Mersenne-Twister),除非在 .rng_kind 参数中明确指定。 R.utils::withSeed,另一方面,默认情况下不对 RNG 种类做任何事情,但可以使用传递给 set.seed... 参数列表指定 RNG 种类。在您的示例中,可以按如下方式修改 s0_R 以获得承诺内外的相同结果。

s0_R = function(seed = 1, n = 1){
  R.utils::withSeed(expr = {
    rnorm(n)
  }, seed = seed, kind = "default")
}