STL random_shuffle生成高度相关的序列

时间:2018-05-08 23:17:48

标签: c++ r rcpp

注意接受的答案指出问题在于重新种植。重播不是原因。没有重新种植的测试在发布前产生了很高的相关性。见注1。

我在R中生成1,000,000个统一随机数,对序列进行排序,并调用std::random_shuffle()来置换此序列的副本100次。 100个置换序列非常相关。但是,如果我不首先对统一数字进行排序,那么100个置换序列或多或少是不相关的。以下是代码。

// [[Rcpp::export]]
IntegerVector testRandomShuffle(IntegerVector x, int rd) // rd is the seed
{
  IntegerVector y(x.begin(), x.end()); // copy
  std::srand(rd); // seeding
  std::random_shuffle(y.begin(), y.end());
  return y;
}


/***R
v = runif(1000000)
vSorted = sort(v)
sqc = 1L : length(v) # indexes
rd = sample.int(length(v), 100) # random seeds


# Compute correlation matrices
corMatForUnsorted = cor(as.data.frame(lapply(rd, function(x) 
  v[testRandomShuffle(sqc, x)])))
corMatForSorted = cor(as.data.frame(lapply(rd, function(x) 
  vSorted[testRandomShuffle(sqc, x)])))


# plot histograms
par(mfrow = c(1, 2)) 
hist(corMatForUnsorted[abs(corMatForUnsorted) < 1], breaks = 200, xlab = 
  "Correlation for unsorted")
hist(corMatForSorted[abs(corMatForSorted) < 1], breaks = 200, xlab = 
  "Correlation for sorted")
*/

enter image description here

我做错了什么吗?我只是希望改组排序和未排序的序列产生或多或少相同的相关分布。这些相关性有多小是另一个故事。用于置换的R的本机函数sample.int()的相同实验在两种情况下产生低相关性。

谢谢!

注1:问题是我使用的是g ++ 4.9.3附带的Rtools 3.4。此版本的C ++库中的shuffle函数工作不正常。

注2:确认Rcpp::sample()在多线程中有效。一个小测试案例:

// [[Rcpp::depends(RcppParallel)]]
# include <RcppParallel.h>
# include <Rcpp.h>
using namespace Rcpp;


struct testSampleInPara: public RcppParallel::Worker
{
  IntegerVector tmp;
  List rst;


  void operator() (std::size_t st, std::size_t end)
  {
    if(st == 0)
    {
      // is tmp / rst a copy or a reference ?
      std::cout << std::to_string((std::size_t)&tmp[0]) + "\n";
      IntegerVector rst0 = Rcpp::sample(tmp, 5);
      rst[0] = rst0; // assume rst not a copy
    }
    else // if(st == 1)
    {
      std::cout << std::to_string((std::size_t)&tmp[0]) + "\n";
      IntegerVector rst1 = Rcpp::sample(tmp, 10);
      rst[1] = rst1;
    }
  }


  testSampleInPara(IntegerVector tmp, List rst):
    tmp(tmp), rst(rst)
  {
    RcppParallel::parallelFor(0, 2, *this);
  }
};


// [[Rcpp::export]]
List testIfSampleCopy(IntegerVector tmp)
{
  List rst(2);
  testSampleInPara(tmp, rst);
  return rst;
}

/***R
testIfSampleCopy(1L : 10L)
# printout:
# 356036792
# 356036792
# [[1]]
# [1] 10  5  9  7  8
# 
# [[2]]
#  [1] 10  3  7  6  2  1  8  4  9  5
*/

我对Rcpp容器的体验对多线程的性能有害。我通常创建指向Rcpp容器的起始元素的指针或指针数组,在线程之间共享这些指针和容器的大小。注意Rcpp::sample()接受并返回Rcpp个容器。

注3:通过阅读Rcpp源代码,最佳解决方案是在本机C ++中编写自定义sample()Rcpp::sample()的核心组成部分是unif_rand()。将unif_rand()整合到Fisher-Yates Shuffle的现代版本中。问题解决了。

注意4:在多线程环境中使用unif_rand()会大大降低线程的速度。我没有时间阅读Dirk Eddelbuettel建议的文档,但我猜R的源同步unif_rand()对我们来说是不可见的,例如malloc()中的C。最终解决方案是包含// [[Rcpp::plugins("cpp11")]]并使用std::random

2 个答案:

答案 0 :(得分:11)

std::random_shuffle(begin, end)经常使用std::rand,这已知是一个糟糕的随机数生成器。来自cppreference:

  

rand()不建议用于严重的随机数生成需求。建议使用C ++ 11的随机数生成工具来替换rand()

改为使用std::shuffle

// Note the lack of `int rd`. `std::random_device` is better for
// seeding purposes, but it is non-deterministic.
IntegerVector testShuffle(IntegerVector x)
{
  IntegerVector y(x.begin(), x.end()); // copy

  // std::mt19937 is a rather heavy type. As such, it's often recommended
  // to make it a static variable. If you will be calling this function
  // from multiple threads, you'd want to make it `thread_local` instead
  // of `static` (or otherwise avoid the data race on `engine`).
  static std::mt19937 engine = [] {
    // Using the Immediately Invoked Lambda Expression (IILE) idiom to
    // initialize the static variable.

    // Seed the RNG.
    std::random_device rd;

    // Note that there are better ways to seed the mersenne twister.
    // This way is flawed, as it can't possibly initialize all of the
    // mersenne twister's state, but it's the simplest way for
    // demonstration purposes
    std::mt19937 engine(rd());

    return engine;
  }();

  // You should be able to just use y.begin(), y.end()
  std::shuffle(y.begin(), y.end(), engine);
  return y;
}

如果您想要一个确定性的种子,请注意,单个int不足以完全为std::mt19937播种,但您仍可以使用它:

IntegerVector testShuffle(IntegerVector x, int seed)
{
  IntegerVector y(x.begin(), x.end());

  static std::mt19937 engine;

  // Not thread-friendly, but simple.
  // Also, note that you'll get bad results if you seed a mersenne twister
  // (or a lot of RNGs) with 0, so avoid that
  engine.seed(seed);

  std::shuffle(y.begin(), y.end(), engine);
  return y;
}

答案 1 :(得分:2)

您的统计直觉和随机数生成器的使用并不完全正确。如果我接受您的代码,请为Rcpp.hnamespace指令添加缺少的包含,只是注释掉重播,然后两个直方图按预期重叠。

enter image description here

以下代码。

#include <Rcpp.h>

using namespace Rcpp;

// [[Rcpp::export]]
IntegerVector testRandomShuffle(IntegerVector x, int rd) { // rd is the seed
  IntegerVector y(x.begin(), x.end()); // copy
  //std::srand(rd); // seeding
  std::random_shuffle(&y[0], &*y.end());
  return y;
}


/***R
#v = runif(1000000)
v = runif(10000)
vSorted = sort(v)
sqc = 1L : length(v) # indexes
rd = sample.int(length(v), 100) # random seeds


# Compute correlation matrices
corMatForUnsorted = cor(as.data.frame(lapply(rd, function(x) 
  v[testRandomShuffle(sqc, x)])))
corMatForSorted = cor(as.data.frame(lapply(rd, function(x) 
  vSorted[testRandomShuffle(sqc, x)])))


# plot histograms
par(mfrow = c(1, 2)) 
hist(corMatForUnsorted[abs(corMatForUnsorted) < 1], breaks = 200, xlab = 
  "Correlation for unsorted")
hist(corMatForSorted[abs(corMatForSorted) < 1], breaks = 200, xlab = 
  "Correlation for sorted")
*/

我还将N降低了两个数量级。够好了。

编辑:为了完整起见,只使用一个RNG的纯Rcpp版本可以在Rcpp工作的情况下工作,包括带有g++-4.9.3的Windows。

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
IntegerVector testRandomShuffle(IntegerVector x, int rd) { // rd is the seed
  IntegerVector y(x.begin(), x.end()); // copy
  std::random_shuffle(&y[0], &*y.end());
  return y;
}

// [[Rcpp::export]]
IntegerVector testRandomSample(IntegerVector x) { // rd is the seed
  IntegerVector y(x.begin(), x.end()); // copy
  return sample(y, y.size());
}

/***R
set.seed(123)  # now we're reproducible
v <- runif(10000)
vSorted <- sort(v)
sqc <- 1L : length(v) # indexes
rd <- sample.int(length(v), 100) # random seeds

# Compute correlation matrices
corMatForUnsorted = cor(as.data.frame(lapply(rd, function(x) 
  v[testRandomSample(sqc)])))
corMatForSorted = cor(as.data.frame(lapply(rd, function(x) 
  vSorted[testRandomSample(sqc)])))

# plot histograms
par(mfrow = c(1, 2)) 
hist(corMatForUnsorted[abs(corMatForUnsorted) < 1], breaks = 200, 
     xlab = "Correlation for unsorted", main="Unsorted")
hist(corMatForSorted[abs(corMatForSorted) < 1], breaks = 200, 
     xlab = "Correlation for sorted", main="Sorted")
*/

它仍然包含未使用的旧版本。结果图现在是

enter image description here

为了完整起见,在基准测试中,Rcpp的sample()例程也更快:

R> library(rbenchmark)
R> benchmark(testRandomShuffle(x, 1), testRandomSample(x))[,1:4]
                     test replications elapsed relative
2     testRandomSample(x)          100   1.402    1.000
1 testRandomShuffle(x, 1)          100   1.868    1.332
R>