有效地在Rcpp中生成随机位流

时间:2019-03-28 13:04:51

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

我正在构建的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;
}

1 个答案:

答案 0 :(得分:4)

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

  • BHboost.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小插图也是必读的。如果您想走devtools的路途,Hadley Wickham的R Packages也有一章关于“编译的代码”。