【问题标题】:Generating a random bit stream in Rcpp efficiently在 Rcpp 中有效地生成随机比特流
【发布时间】:2019-08-19 06:29:52
【问题描述】:

我目前正在构建的 R 包中有一个名为 rbinom01 的辅助函数。请注意,它调用random(3)

int rbinom01(int size) {
  if (!size) {
    return 0;
  }

  int64_t result = 0;
  while (size >= 32) {
    result += __builtin_popcount(random());
    size -= 32;
  }

  result += __builtin_popcount(random() & ~(LONG_MAX << size));

  return result;
}

R CMD check my_package时,我收到以下警告:

* checking compiled code ... NOTE
File ‘ my_package/libs/my_package.so’:
  Found ‘_random’, possibly from ‘random’ (C)
    Object: ‘ my_function.o’

Compiled code should not call entry points which might terminate R nor
write to stdout/stderr instead of to the console, nor use Fortran I/O
nor system RNGs.

See ‘Writing portable packages’ in the ‘Writing R Extensions’ manual.

我前往the Document,它说我可以使用*_rand 函数之一,以及distribution functions 家族。这很酷,但我的包只需要一个随机位流而不是一个随机的double。最简单的方法是使用random(3) 或从/dev/urandom 读取,但这会使我的包“不可移植”。

This post 建议使用sample,但不幸的是它不适合我的用例。对于我的应用程序,生成随机位显然对性能至关重要,所以我不希望它浪费任何时间调用unif_rand,将结果乘以N 并四舍五入。无论如何,我使用 C++ 的原因是利用位级并行性。

当然,我可以手动滚动自己的 PRNG 或复制并粘贴最先进的 PRNG 的代码,例如 xoshiro256**,但在此之前,我想看看是否有更简单的替代方案。

顺便说一句,有人可以将 Rcpp 的简短教程链接给我吗? 编写 R 扩展 内容全面且很棒,但我需要数周时间才能完成。我正在寻找一个更简洁的版本,但最好它应该比调用Rcpp.package.skeleton 提供更多信息。


正如@Ralf Stubner 的回答所建议的那样,我重新编写了原始代码,如下所示。但是,我每次都得到相同的结果。我怎样才能正确地播种它,同时保持我的代码“便携”?

int rbinom01(int size) {
  dqrng::xoshiro256plus rng;

  if (!size) {
    return 0;
  }

  int result = 0;
  while (size >= 64) {
    result += __builtin_popcountll(rng());
    Rcout << sizeof(rng()) << std::endl;
    size -= 64;
  }

  result += __builtin_popcountll(rng() & ((1LLU << size) - 1));

  return result;
}

【问题讨论】:

  • 您可以通过dqrng包获取xoshiro256+和xoroshiro128+。

标签: c++ r random rcpp r-package


【解决方案1】:

有不同的 R 包使 PRNG 可以作为 C++ 标头库使用:

  • BH: 一切来自boost.random
  • sitmo: 各种 Threefry 版本
  • dqrng:PCG 系列,xoshiro256+ 和 xoroshiro128+
  • ...

您可以通过将LinkingTo 添加到您的包的DECRIPTION 来使用其中的任何一个。通常,这些 PRNG 是在 C++11 random 标头之后建模的,这意味着您必须自己控制它们的生命周期和播种。在单线程环境中,我喜欢使用匿名命名空间进行生命周期控制,例如:

#include <Rcpp.h>
// [[Rcpp::depends(dqrng)]]
#include <xoshiro.h>
// [[Rcpp::plugins(cpp11)]]

namespace {
dqrng::xoshiro256plus rng{};
}

// [[Rcpp::export]]
void set_seed(int seed) {
  rng.seed(seed);
}

// [[Rcpp::export]]
int rbinom01(int size) {
  if (!size) {
    return 0;
  }

  int result = 0;
  while (size >= 64) {
    result += __builtin_popcountll(rng());
    size -= 64;
  }

  result += __builtin_popcountll(rng() & ((1LLU << size) - 1));

  return result;
}

/*** R
set_seed(42)
rbinom01(10)
rbinom01(10)
rbinom01(10)
*/

但是,使用runif 并不全是坏事,而且肯定比访问/dev/urandom 更快。在dqrng 中有一个convenient wrapper 用于此。

至于教程:除了 WRE,Rcpp package 小插图是必读的。 R Packages by Hadley Wickham 还有一章是关于“编译代码”的,如果你想走devtools-way。

【讨论】:

  • 四个 :) RcppZiggurat.
  • @Dirk RcppZiggurat 是否允许访问底层比特流?我认为它“仅”提供了超快的正态变量。
  • 是的。原始论文也有指数,但我不涉及。
  • dqrng 看起来像一个不错的包,但我认为它不会将底层xoshiro128+ 导出给我。刚刚提到的参考手册有 dqrunif 之类的东西,它不返回比特流。您是否建议我使用 C++ 中的generateSeedVectors?如果是这样的话,请给我一个最小的例子。
  • 我想我自己已经想通了,但在播种方面仍然存在一些问题。请看一下我编辑的问题。谢谢!
猜你喜欢
  • 2011-06-28
  • 2012-01-06
  • 2018-01-10
  • 1970-01-01
  • 1970-01-01
  • 2020-09-09
  • 2021-04-14
  • 2014-10-07
  • 1970-01-01
相关资源
最近更新 更多