使用R的replicate()调用Rcpp函数生成随机数时出现重复值异常
你遇到的问题不是replicate()的bug,而是你的C++代码里随机数生成的逻辑有问题,我来帮你拆解清楚:
问题根源
看一下你的getRan()函数:每次调用它的时候,都会重新创建一个随机数生成器,并以当前系统时间的时间戳作为种子。
问题出在replicate()的执行速度上——它会在极短的时间内连续调用getRan()十次,这十次调用的时间戳几乎完全相同(哪怕chrono的精度很高,replicate的循环速度还是快到让时间戳没变化),所以每次生成的种子都是一样的,自然会得到完全相同的随机数。
而你单独手动调用getRan()的时候,每次调用之间有明显的时间间隔,时间戳不一样,种子不同,所以随机数就不同了。
解决方案
方案1:修正C++代码,复用随机生成器和分布
把随机数生成器(generator)和分布(distribution)定义为静态变量,这样它们只会在第一次调用getRan()时初始化一次,后续调用会复用同一个生成器,自然就能生成不同的随机数了:
#include <Rcpp.h> #include <chrono> #include <random> using namespace Rcpp; // [[Rcpp::export]] double getRan(){ // 静态变量,仅初始化一次 static unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); static std::default_random_engine generator(seed); static std::normal_distribution<double> distribution(0.0, 1.0); double epi = distribution(generator); return epi; }
修改后再运行replicate(10, getRan()),就能得到10个不同的随机数了。
方案2:使用R自带的随机数生成器(更推荐)
因为你是在R环境中使用Rcpp,更稳妥的方式是直接调用R本身的随机数生成函数,这样可以和R的全局随机数状态保持同步(比如你在R中设置set.seed()也会影响这个函数的输出),代码更简洁,也避免了自己管理随机数种子的问题:
#include <Rcpp.h> using namespace Rcpp; // [[Rcpp::export]] double getRan(){ // 调用R的正态分布随机数生成函数 return R::rnorm(0.0, 1.0); }
这个版本的getRan()不管是单独调用还是用replicate()批量调用,都会生成不同的随机数,而且和R的随机数系统完全兼容,更适合你后续做基于随机数的复杂计算的场景。
额外优化建议
如果你需要生成多个随机数,其实可以直接在C++函数里一次性生成向量返回,效率会比用replicate()多次调用更高,比如:
// [[Rcpp::export]] NumericVector getRanVec(int n){ NumericVector result(n); for(int i=0; i<n; i++){ result[i] = R::rnorm(0.0, 1.0); } return result; }
然后在R中直接调用getRanVec(10)就能得到10个不同的随机数,比replicate更高效。
内容的提问来源于stack exchange,提问作者John

