36template <
class Engine = std::mt19937>
48 std::vector<size_t> sampleWithoutReplacement(
49 size_t numSamples,
const std::vector<double>& weights) {
52 const size_t n = weights.size();
54 throw std::runtime_error(
55 "numSamples must be smaller than weights.size()");
59 std::vector<size_t> result(numSamples);
60 if (numSamples == 0)
return result;
67 std::priority_queue<std::pair<double, size_t> > reservoir;
69 static const double kexp1 = std::exp(1.0);
70 for (
auto it = weights.begin(); it != weights.begin() + numSamples; ++it) {
71 const double k_i = kexp1 / *it;
72 reservoir.push(std::make_pair(k_i, it - weights.begin() + 1));
80 const std::pair<double, size_t>& T_w = reservoir.top();
83 for (
auto it = weights.begin() + numSamples; it != weights.end(); ++it) {
86 const double X_w = kexp1 / T_w.first;
94 for (; it != weights.end(); ++it) {
100 if (it == weights.end())
break;
105 const double t_w = -T_w.first * *it;
106 std::uniform_real_distribution<double> randomAngle(std::exp(t_w), 1.0);
107 const double e_2 = std::log(randomAngle(*engine_));
108 const double k_i = -e_2 / *it;
113 reservoir.push(std::make_pair(k_i, it - weights.begin() + 1));
117 for (
auto iret = result.end(); iret != result.begin();) {
120 if (reservoir.empty()) {
121 throw std::runtime_error(
122 "Reservoir empty before all elements have been filled");
125 *iret = reservoir.top().second - 1;
129 if (!reservoir.empty()) {
130 throw std::runtime_error(
131 "Reservoir not empty after all elements have been filled");
Global functions in a separate testing namespace.
Definition: chartTesting.h:28
Definition: WeightedSampler.h:37
WeightedSampler(Engine *engine)
Construct from random number generation engine We only store a pointer to it.
Definition: WeightedSampler.h:46