将局部环境的随机性与全局 R 过程隔离

2024-01-06

我们可以用set.seed()在 R 中设置随机种子,这具有全局效果。这是一个最小的例子来说明我的目标:

set.seed(0)
runif(1)
# [1] 0.8966972

set.seed(0)
f <- function() {
  # I do not want this random number to be affected by the global seed
  runif(1)
}
f()
# [1] 0.8966972

基本上我希望能够避免全局随机种子的影响(即,.Random.seed)在本地环境(例如 R 函数)中,这样我就可以实现用户无法控制的某种随机性。例如,即使用户有set.seed(),每次调用这个函数时他仍然会得到不同的输出。

现在有两种实现方式。第一个依赖于set.seed(NULL)每次我想要获得一些随机数时,让 R 重新初始化随机种子:

createUniqueId <- function(bytes) {
  withPrivateSeed(
    paste(as.hexmode(sample(256, bytes, replace = TRUE) - 1), collapse = "")
  )
}
withPrivateSeed <- function(expr, seed = NULL) {
  oldSeed <- if (exists('.Random.seed', envir = .GlobalEnv, inherits = FALSE)) {
    get('.Random.seed', envir = .GlobalEnv, inherits = FALSE)
  }
  if (!is.null(oldSeed)) {
    on.exit(assign('.Random.seed', oldSeed, envir = .GlobalEnv), add = TRUE)
  }
  set.seed(seed)
  expr
}

你可以看到,即使我将种子设置为 0,我也会得到不同的 id 字符串,并且全局随机数流仍然是可重现的:

> set.seed(0)
> runif(3)
[1] 0.8966972 0.2655087 0.3721239
> createUniqueId(4)
[1] "83a18600"
> runif(3)
[1] 0.5728534 0.9082078 0.2016819

> set.seed(0)
> runif(3)  # same
[1] 0.8966972 0.2655087 0.3721239
> createUniqueId(4)  # different
[1] "77cb3d91"
> runif(3)
[1] 0.5728534 0.9082078 0.2016819

> set.seed(0)
> runif(3)
[1] 0.8966972 0.2655087 0.3721239
> createUniqueId(4)
[1] "c41d61d8"
> runif(3)
[1] 0.5728534 0.9082078 0.2016819

可以找到第二种实现here https://github.com/rstudio/shiny/pull/434/files在 Github 上。它比较复杂,基本思想是:

  • 在包启动期间使用以下命令初始化随机种子set.seed(NULL) (in .onLoad())
  • 将随机种子存储在单独的环境中(.globals$ownSeed)
  • each time when we want to generate random numbers:
    1. 将局部种子分配给全局随机种子
    2. 生成随机数
    3. 将新的全局种子(由于步骤 2 已更改)分配给本地种子
    4. 将全局种子恢复为其原始值

现在我的问题是这两种方法在理论上是否等效。第一种方法的随机性依赖于当前时间和进程IDcreateUniqueId()被调用,第二种方法似乎依赖于包加载时的时间和进程ID。对于第一种方法,是否有可能两次调用createUniqueId()在同一个 R 进程中同时发生,以便它们返回相同的 id 字符串?

Update

在下面的回答中,罗伯特·克日扎诺夫斯基 (Robert Krzyzanowski) 提供了一些经验证据:set.seed(NULL)可能会导致严重的 ID 冲突。我做了一个简单的可视化 https://i.stack.imgur.com/2teJA.png for it:

createGlobalUniqueId <- function(bytes) {
  paste(as.hexmode(sample(256, bytes, replace = TRUE) - 1), collapse = "")
}
n <- 10000
length(unique(replicate(n, createGlobalUniqueId(5))))
length(unique(x <- replicate(n, createUniqueId(5))))
# denote duplicated values by 1, and unique ones by 0
png('rng-time.png', width = 4000, height = 400)
par(mar = c(4, 4, .1, .1), xaxs = 'i')
plot(1:n, duplicated(x), type = 'l')
dev.off()

当该线到达图的顶部时,这意味着生成了重复值。但是,请注意这些重复项并不是连续出现的,即any(x[-1] == x[-n])通常是FALSE。可能存在与系统时间相关的重复模式。由于我对基于时间的随机种子的工作原理缺乏了解,我无法进一步调查,但您可以查看相关的 C 源代码片段here https://github.com/wch/r-source/blob/5b6690cb7d1b459cb0610706b2feec96184c038c/src/main/times.c#L149 and here https://github.com/wch/r-source/blob/da62cf194897d5fe035affa6f88d96f36a18fcda/src/main/RNG.c#L526.


我认为在函数内有一个独立的 RNG 会很好,它不受全局种子的影响,但有自己的种子。事实证明,randtoolbox提供此功能:

library(randtoolbox)
replicate(3, {
  set.seed(1)
  c(runif(1), WELL(3), runif(1))
})   
#            [,1]      [,2]      [,3]
#[1,] 0.265508663 0.2655087 0.2655087
#[2,] 0.481195594 0.3999952 0.9474923
#[3,] 0.003865934 0.6596869 0.4684255
#[4,] 0.484556709 0.9923884 0.1153879
#[5,] 0.372123900 0.3721239 0.3721239

顶行和底行受种子影响,而中间行是“真正随机的”。

基于此,这是您的函数的实现:

sample_WELL <- function(n, size=n) {
  findInterval(WELL(size), 0:n/n)
}

createUniqueId_WELL <- function(bytes) {
  paste(as.hexmode(sample_WELL(256, bytes) - 1), collapse = "")
}

length(unique(replicate(10000, createUniqueId_WELL(5))))
#[1] 10000

# independency on the seed: 
set.seed(1)
x <- replicate(100, createGlobalUniqueId(5))
x_WELL <- replicate(100, createUniqueId_WELL(5))
set.seed(1)
y <- replicate(100, createGlobalUniqueId(5))
y_WELL <- replicate(100, createUniqueId_WELL(5))
sum(x==y)
#[1] 100
sum(x_WELL==y_WELL)
#[1] 0

Edit

要理解为什么我们会得到重复的键,我们应该看看当我们调用时会发生什么set.seed(NULL)。所有RNG相关的代码都是用C编写的,所以我们应该直接进入svn.r-project.org/R/trunk/src/main/RNG.c http://svn.r-project.org/R/trunk/src/main/RNG.c并参考函数do_setseed. If seed = NULL然后清楚地TimeToSeed叫做。有一条评论指出它应该位于 datetime.c 中,但是,它可以在svn.r-project.org/R/trunk/src/main/times.c http://svn.r-project.org/R/trunk/src/main/times.c.

浏览 R 源代码可能很困难,因此我将函数粘贴到此处:

/* For RNG.c, main.c, mkdtemp.c */
attribute_hidden
unsigned int TimeToSeed(void)
{
    unsigned int seed, pid = getpid();
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
    {
    struct timespec tp;
    clock_gettime(CLOCK_REALTIME, &tp);
    seed = (unsigned int)(((uint_least64_t) tp.tv_nsec << 16) ^ tp.tv_sec);
    }
#elif defined(HAVE_GETTIMEOFDAY)
    {
    struct timeval tv;
    gettimeofday (&tv, NULL);
    seed = (unsigned int)(((uint_least64_t) tv.tv_usec << 16) ^ tv.tv_sec);
    }
#else
    /* C89, so must work */
    seed = (Int32) time(NULL);
#endif
    seed ^= (pid <<16);
    return seed;
}

所以每次我们打电话set.seed(NULL), R 执行以下步骤:

  1. 以秒和纳秒为单位获取当前时间(如果可能,此处的平台依赖性#if defined blocks)
  2. 将位移位应用于纳秒并将位“异或”结果与秒
  3. 对 pid 应用位移位,并将其与之前的结果进行位“异或”
  4. 将结果设置为新种子

好吧,现在很明显,当生成的种子发生碰撞时,我们会得到重复的值。我的猜测是,当两个调用在 1 秒内发生时,就会发生这种情况,因此 tv_sec 是恒定的。为了证实这一点,我引入了一个滞后:

createUniqueIdWithLag <- function(bytes, lag) {
  Sys.sleep(lag)
  createUniqueId(bytes)
}
lags <- 1 / 10 ^ (1:5)
sapply(lags, function(x) length(unique(replicate(n, createUniqueIdWithLag(5, x)))))
[1] 1000 1000  996  992  990

令人困惑的是,即使延迟与纳秒相比相当大,我们仍然会发生碰撞!让我们进一步挖掘它,我为种子编写了一个“调试模拟器”:

emulate_seed <- function() {
  tv <- as.numeric(system('echo $(($(date +%s%N)))', intern = TRUE))
  pid <- Sys.getpid()
  tv_nsec <- tv %% 1e9
  tv_sec <- tv %/% 1e9
  seed <- bitwXor(bitwShiftL(tv_nsec, 16), tv_sec)
  seed <- bitwXor(bitwShiftL(pid, 16), seed)
  c(seed, tv_nsec, tv_sec, pid)
}

z <- replicate(1000, emulate_seed())
sapply(1:4, function(i) length(unique(z[i, ])))
# unique seeds, nanosecs, secs, pids:
#[1]  941 1000   36    1

这确实令人困惑:纳秒都是唯一的,并且不能保证最终种子的唯一性。为了演示这种效果,下面是其中一个副本:

#            [,1]        [,2] 
#[1,] -1654969360 -1654969360
#[2,]   135644672   962643456
#[3,]  1397894128  1397894128 
#[4,]        2057        2057
bitwShiftL(135644672, 16)
#[1] -973078528
bitwShiftL(962643456, 16)
#[1] -973078528

最后注意:这两个数字的二进制表示和移位是

00001000000101011100011000000000 << 16 => 1100011000000000 + 16 zeroes
00111001011000001100011000000000 << 16 => 1100011000000000 + 16 zeroes

所以,是的,这确实是一次不必要的碰撞。

好了,说了这么多,最后的结论是:set.seed(NULL)容易受到高负载的影响,并且在处理多个连续调用时不保证不发生冲突!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将局部环境的随机性与全局 R 过程隔离 的相关文章

  • 保存 d3heatmap 生成的热图

    我正在尝试保存由d3heatmap https github com rstudio d3heatmap转换为 pdf 文件 但文件总是损坏 library d3heatmap pdf file heat pdf d3heatmap mtc
  • 使用 ``magrittr::`%>%` `` 时 magrittr 管道出错

    不管出于什么原因我在玩magrittr管道语法 并遇到一个奇怪的错误 当您 scope 显式限定调用时发生 gt 我知道使用下面的语法会破坏管道的用途 但我很好奇为什么会发生错误 第一次致电sum按预期工作并输出1 第二次调用会导致错误 E
  • 按钮:带滚动条的下载按钮仅下载几行

    我正在处理超过 100 000 行的表并使用DT包裹 开发版本0 1 56 在 Shiny App 中将其可视化 此外我正在使用DT扩展如下 Buttons 下载不同格式的数据 然而虽然Scroller扩展程序也已激活 我只能下载几行 不是
  • 如何在Shiny中引用ui.R中的反应元素

    我正在使用 ShinyDND 包制作一个具有拖放功能的应用程序 我想将输入中的列表作为 DragSetUI 的参数传递 该函数需要在 ui R 中运行 我尝试了renderUI和uiOutput 它几乎可以工作 但是拖动的元素无法放置在放置
  • 使用 google 查询邮政编码距离

    我有两个邮政编码列表 R 语言 其中一个是孩子的地址及其学业成绩 另一个是学校的地址 我希望能够为每个孩子找到最近的学校 所以大概需要通过转换为长和纬度值来计算邮政编码之间的距离 然后我希望能够在谷歌地图上绘制每所学校的所有孩子 并看看住在
  • 如何使用Shiny中的下载按钮?

    我想下载一个csv使用 Shiny 中的下载按钮下载文件 该文件将使用辅助 r 脚本中的参数创建 SERVER output downloadData lt downloadHandler filename function paste d
  • 根据 B 列中的一系列值获取 A 列的平均值

    我的数据框有几列 如下所示 df1 lt data frame A c 1 2 4 B c 1 3 1 C c 1 1 3 我有两个条件来获取 A 列的平均值 条件1 我想在B为1时获得A列的平均值 即只对row1和row2进行平均 条件2
  • 如何为每个条形图制作具有定义水平边框的堆叠条形图

    我有一些数据想以一种我不知道如何在条形图中显示的方式 希望你能帮我解决这个问题 我的表由 4 列组成 簇 0 6 IgG Status mild high mild low Severe High 患者 1 16 和值 每个簇的标准化值 这
  • 加载 plyr 包时出现问题

    我使用 R 2 13 1 但未能成功尝试在 R 中加载包 plyr 1 6 我已将其手动安装到目录 R library 中 我的代码是 libPaths R library library plyr 我收到消息 库 plyr 中的错误 pl
  • 将缺失的行添加到数据表中

    我有一个数据表 library data table f lt data table id1 c 1 2 3 1 2 3 id2 as factor c a a b c b d v 1 6 key c id1 id2 id1 id2 v 1
  • R中的预测和预测函数之间的区别

    两者之间有什么区别吗predict and forecast R 中的函数 如果是 在哪些具体情况下应该使用它们 Intro predict 适用于多种 R 对象 模型 基础库的一部分 forecast 对于时间序列 预测包的一部分 参见示
  • 如何禁用“保存工作区图像?” R 中的提示?

    当我退出交互式 R shell 时 它每次都会显示一个烦人的提示 gt gt Save workspace image y n c n 我总是对此回答 不 因为如果我想保存我的工作 我就会这么做before试图退出 如何去掉这个提示呢 No
  • 获取非零数据的列意味着

    R 可以获得数据帧非零值的 colMeans 吗 data lt data frame col1 c 1 0 1 0 3 3 col2 c 5 0 5 0 7 7 colMeans data 1 33 4 我想要这样的东西 mean dat
  • spplot 的图例范围和颜色分布问题

    我的绘图和图例中的颜色范围是否正确存在问题 这是我使用的代码 data ch4 lt read csv2 v42 CH4 1970 TOT txt skip 3 stringsAsFactors FALSE header F num dat
  • 成对散点图;一对多[重复]

    这个问题在这里已经有答案了 有没有一种简洁的方法来创建pairs仅将一个变量与许多其他变量进行比较的图 换句话说 我可以只绘制标准的一行或一列吗 pairs不使用循环的散点图矩阵 融化你的数据 然后使用带有facet的ggplot libr
  • 使用shinyjs通过javascript在闪亮的应用程序中操作现有的Leaflet地图

    我有一个闪亮的应用程序 其中包含现有的传单地图 我希望能够在渲染后使用自定义 javascript 通过shinyjs包裹 一个最小的例子如下 app R packages library dplyr library leaflet lib
  • 使用 ggplot 绘制函数,相当于 curve()

    是否有使用绘制函数的等效方法ggplot to the curve 基础图形中使用的命令 我想另一种选择是创建一个函数值向量并绘制一条连接线 但我希望有更简单的东西 Thanks 您可以使用以下命令添加曲线stat function ggp
  • C/C++ 中随机数生成器的实现[重复]

    这个问题在这里已经有答案了 我对 C 中随机数生成器的实现有点困惑 它也与 C 中的明显不同 如果我理解正确 对 srand seed 的调用会以某种方式初始化可通过 rand 访问的隐藏变量 种子 该变量又将函数指向预先生成的序列 例如例
  • 在R中使用plotly在轴标题中换行和下标

    我刚开始使用plotly对于 R 中的一些交互式散点图 并且在轴标签上遇到困难 通常我设计我的情节ggplot2然后使用ggplotly函数来转换它们 但这有时由于某种原因非常慢 所以我想直接在中创建我的图plotly 我现在尝试更改轴标题
  • 是否有 R 函数可以将这些数据从长形重塑为宽形?

    数据现在看起来如何 Coach ID Student score 1 A 8 1 B 3 2 A 5 2 B 4 2 C 7 看起来像这样 Coach ID Student score student 2 score 2 student 3

随机推荐

  • Python OpenCv,只读取部分图像

    我有数千张大型 png 图像 屏幕截图 我正在使用 opencv 对每个图像的一小部分进行图像识别 我目前正在做 image cv2 imread path x y w h bounds image image y y h x x w 分析
  • Python(看门狗)-等待文件正确创建

    我是 Python 新手 我正在尝试实现一个良好的 文件创建 检测 如果我不放一个time sleep x 我的文件以错误的方式详细说明 因为它们仍在文件夹中 创建 缓冲区不为空 我怎样才能绕过这个事情而不等待x每次创建文件的时间是多少秒
  • WPF 超链接中的文本换行

    在我的 WPF 应用程序中 我有这个
  • 使用 NCO 连接每日 TRMM netCDF 文件时如何添加时间维度?

    我下载了几天的每日 TRMM 3B42 数据https disc gsfc nasa gov datasets https disc gsfc nasa gov datasets 文件名的形式为3B42 Daily yyyymmdd 7 n
  • 计算两个充满地理点的表之间的距离

    我正在使用 SQL Server 我有两个这样的表 Table1 Column1 Column2 Column3 GeoLoc a b c 0xE61 Table2 Column1 Column2 Column3 GeoLoc a b c
  • 在代码执行完成之前,标签不会改变颜色

    有很多不相关的代码需要查看 但它几乎发送一个数据包并侦听一个数据包作为返回 如果我注释掉在发送数据包结束时调用 ReceiveAuthPacket 方法的部分 它将起作用并且标签将变成蓝色 但否则它永远不会激活将标签变成蓝色 而是将标签变成
  • C++ 范围是否支持视图中的投影?

    我知道算法 例如sort 在范围内支持投影 但在我看来 没有办法获得视图的该功能 我对吗 作为一个例子 考虑以下工作代码 https godbolt org z j4Psb14sE include
  • 如何在我的 Nuxt 应用程序中正确设置 bootstrap-vue?

    我很新Nuxt js应用程序 我正在尝试使用创建一个网络应用程序Nuxt js and Vue js 在创建项目期间使用Nuxt cli我已经添加了Bootstrap vue 我面临一些问题Bootstrap modal创建因此我想删除Bo
  • 切换 A2DP 设备 (Android)

    我有两个配对的蓝牙设备 用于电话音频的汽车主机和用于 A2DP 的独立蓝牙接收器 我的手机上有一个 用于媒体音频 复选框 我必须手动切换该复选框才能将 A2DP 输出发送至汽车扬声器 我的目标是以编程方式切换此功能 我尝试将 AudioMa
  • 从 R 中的通用名称开始将多个变量选择到模型中

    与在 SAS 中一样 我们可以使用带有起始名称的冒号 选项来启动多个变量 我想在 R 中做同样的事情来建模 有什么建议么 可能有很多方法可以做到这一点 这是一个带有正则表达式的正则表达式 它并不完全符合您的要求 但可能可以解决问题 x1 r
  • 使用 Java 流以不可变的方式更改数据

    考虑这段代码 Function
  • Android NDK:未找到本机 xxxxxx 的实现

    我做了一个使用 Tess two 的项目 我想使用该方法pixConvertRGBToLuminance 但总是出现错误 No implementation found for native Lcom googlecode leptonic
  • 如何防止 Visual Studio 提示从 app.config 更新 .settings 文件

    我们使用 settings 文件来存储应用程序设置 并使用其中的默认值 这样我们就不需要配置每个设置 这通常都很好 然而 在开发过程中 我们会经常更改app config中的值 这意味着当我们打开 settings文件时 我们总是会得到提示
  • 无法找到newEventHub函数

    我是超级账本结构的新手 我已经下载了 Fabric v1 2 0 并尝试从 Fabric sample 文件夹中运行 fabcar 示例 我对链码的查询进展顺利 但是当我尝试调用链码时 我从 cmd 收到以下错误 这是我对链码的请求 var
  • jQuery:是否可以同时使用slideUp和附加文本?

    当用户将鼠标悬停在图片上时 我想要slideUp描述 以便出现新文本 当用户鼠标移开时 描述将slideDown 这是我到目前为止所尝试过的 pic1 hover function var text1 div Price1 100 div
  • 我正在尝试在 PowerShell 中使用 Python

    我正在努力追随Zed Shaw https en wikipedia org wiki Zed Shaw的指南艰难地学习Python https en wikipedia org wiki Zed Shaw Books 我需要在 Power
  • 调整大小时 SVG 图像质量下降

    正如你在上图中看到的 这两个svg图像质量有点下降 在Assets的属性检查器 他们的Scale属性设置为Single Scale 这些是按钮的 imageView 调整大小以填充按钮 button setImage UIImage nam
  • 在 jQuery 加载之前阻止链接

    如何在加载 jQuery 之前阻止点击事件上的链接 原因是我几乎没有通过 jQuery ajax 函数进行 AJAX 调用的链接 如果用户在加载 jQuery 框架之前单击它们 浏览器将无法触发 jQuery ajax 函数 并将跟踪链接h
  • connect-mongo MongoStore 会话实际上是如何保存的?

    我使用以下方法实现了会话Passport 但是为了存储会话我尝试过connect mongo用一个mongoose联系 这是我的代码 会话部分 var express require express var mongodb require
  • 将局部环境的随机性与全局 R 过程隔离

    我们可以用set seed 在 R 中设置随机种子 这具有全局效果 这是一个最小的例子来说明我的目标 set seed 0 runif 1 1 0 8966972 set seed 0 f lt function I do not want