C++ 在Rcpp中高效地生成随机比特流

C++ 在Rcpp中高效地生成随机比特流,c++,r,random,rcpp,r-package,C++,R,Random,Rcpp,R Package,我目前正在构建的R包中有一个名为rbinom01的辅助函数。注意,它调用random3 我去了,它说我可以使用一个*_-rand函数,还有一个函数族。这很酷,但我的包只需要一个随机比特流,而不是一个随机双精度。最简单的方法是使用random3或者从/dev/uradom中读取,但这会使我的包不可移植 建议使用示例,但不幸的是,它不适合我的用例。对于我的应用程序,生成随机位显然对性能至关重要,因此我不希望浪费时间调用unif_rand,将结果乘以N并进行四舍五入。无论如何,我使用C++的原因是利用

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

我去了,它说我可以使用一个*_-rand函数,还有一个函数族。这很酷,但我的包只需要一个随机比特流,而不是一个随机双精度。最简单的方法是使用random3或者从/dev/uradom中读取,但这会使我的包不可移植

建议使用示例,但不幸的是,它不适合我的用例。对于我的应用程序,生成随机位显然对性能至关重要,因此我不希望浪费时间调用unif_rand,将结果乘以N并进行四舍五入。无论如何,我使用C++的原因是利用位级并行性。 当然,我可以手动滚动我自己的PRNG或复制并粘贴最先进的PRNG代码,但在这样做之前,我想看看是否有更简单的替代方案

顺便问一下,有人能给我链接一个很好的Rcpp简短教程吗?编写R扩展既全面又令人敬畏,但我需要几个星期才能完成。我正在寻找一个更简洁的版本,但最好它应该比调用Rcpp.package.skeleton提供更多信息

根据的回答,我重新编写了原始代码,如下所示。然而,我每次都得到相同的结果。我怎样才能正确地为它设定种子,同时保持代码的可移植性

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;
}

有不同的R包使PRNG成为C++头文件库:

:从boost.random :各种版本 :PCG系列,xoshiro256+和xoroshiro128+ ... 您可以通过将LinkingTo添加到包的说明中来使用其中的任何一个。通常,这些PRNG是按照C++11随机头建模的,这意味着您必须自己控制它们的生命周期和种子。在单线程环境中,我喜欢使用匿名命名空间进行生命周期控制,例如:

#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/uradom更快。在dqrng中,对此有一个明确的定义


至于教程:除了WER,这个小插曲也是必读的。Hadley Wickham也有一个关于编译代码的章节,如果你想去DeVoTo.E.P/>< P>有不同的R包使PRNG成为C++头库:

:从boost.random :各种版本 :PCG系列,xoshiro256+和xoroshiro128+ ... 您可以通过将LinkingTo添加到包的说明中来使用其中的任何一个。通常,这些PRNG是按照C++11随机头建模的,这意味着您必须自己控制它们的生命周期和种子。在单线程环境中,我喜欢使用匿名命名空间进行生命周期控制,例如:

#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/uradom更快。在dqrng中,对此有一个明确的定义


至于教程:除了WER,这个小插曲也是必读的。Hadley Wickham也有一章介绍编译代码,如果你想使用devtools的话。

你可以通过软件包获得xoshiro256+和xoroshiro128+。你可以通过软件包获得xoshiro256+和xoroshiro128+。四:RcppZiggurat。@Dirk RcppZiggurat是否允许访问底层比特流?我以为它只提供了超快速的正态变量。原始文件也有指数,但我没有涵盖。dqrng看起来像一个像样的软件包,但我不认为它正在向我输出底层xoshiro128+。参考手册刚刚提到它有一些东西,比如dqrunif,它不返回比特流。你是在建议我使用C++中的广义向量吗?如果是这样的话,请给我举一个最简单的例子。我想我自己已经明白了,但是仍然有一些关于播种的问题。请看我编辑的问题。谢谢四:RcppZiggurat。@Dirk RcppZiggurat是否允许访问底层位流?我以为它只提供了超快速的正态变量。原始文件也有指数,但我没有涵盖。dqrng看起来像一个像样的软件包,但我不认为它正在向我输出底层xoshiro128+。参考手册刚刚提到它有一些东西,比如dqrunif,它不返回比特流。你是在建议我使用C++中的广义向量吗?如果是这样的话,请给我举一个最简单的例子。我想我自己已经明白了,但是仍然有一些关于播种的问题。请看我编辑的问题。谢谢
#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)
*/