I have the following C++ code snippet that randomly samples a single row in an array called pop
with num_specs
columns and perms
rows. In addition, K
= 1. The triply-nested for
loop uses a pointer for referencing.
Some of the below syntax (such as IntegerVector
) is from Rcpp, an R package to integrate C++ code with R code.
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::depends(RcppProgress)]]
#define ARMA_DONT_PRINT_OPENMP_WARNING
#include <RcppArmadillo.h>
#include <RcppArmadilloExtensions/sample.h>
#include <set>
using namespace Rcpp;
int sample_one(int n) {
return n * unif_rand();
}
int sample_n_distinct(const IntegerVector& x,
int k,
const int * pop_ptr) {
IntegerVector ind_index = RcppArmadillo::sample(x, k, false);
std::set<int> distinct_container;
for (int i = 0; i < k; i++) {
distinct_container.insert(pop_ptr[ind_index[i]]);
}
return distinct_container.size();
}
// [[Rcpp::export]]
arma::Cube<int> accumulate(const arma::Cube<int>& pop,
const IntegerVector& specs,
int perms,
int K) {
int num_specs = specs.size();
arma::Cube<int> res(perms, num_specs, K);
IntegerVector specs_C = specs - 1;
const int * pop_ptr;
int i, j, k;
for (i = 0; i < K; i++) {
for (k = 0; k < num_specs; k++) {
for (j = 0; j < perms; j++) {
pop_ptr = &(pop(sample_one(perms), 0, sample_one(K)));
res(j, k, i) = sample_n_distinct(specs_C, k + 1, pop_ptr);
}
}
}
return res;
}
While loops in compiled languages aren't bad, it is possible to write slow code.
I'm not a native C++ programmer, so I don't know all the tricks of the trade.
Is there a way to reduce the number of levels in the triply-nested for
loop above, possibly by employing modular arithmetic in order to see a gain in speed for large input values?
The R code is below:
## Set up container to hold the identity of each individual from each permutation ##
num.specs <- N
## Create an ID for each tag ##
tags <- 1:h
## Assign individuals (N) ##
specs <- 1:num.specs
## Generate permutations. Assume each permutation has N individuals, and sample those
# individuals' tags from the probabilities ##
gen.perms <- function() {
sample(tags, size = num.specs, replace = TRUE, prob = probs)
}
pop <- array(dim = c(perms, num.specs, K))
for (i in 1:K) {
pop[,, i] <- replicate(perms, gen.perms())
}
## Perform accumulation ##
HAC.mat <- accumulate(pop, specs, perms, K)
## Example
K <- 1
N <- 100
h <- 5
probs <- rep(1/h, h)
perms <- 100