Well, the task is neither new nor unsolved. I was particularly interested in the fancy possibility to use a map container for binary search. Of course, boost offers better (read: faster and shorter) ways. Both of them can be implemented in a few lines.
#include <map>
#include <boost/random.hpp>
#define CHOICES 10000
#define SPINS 10000000
using namespace std;
struct wheelcomp {
bool operator()(const pair<double, double> &a, const pair<double, double> &b) {
if (b.first == -1) {
if (b.second >= a.first && b.second < a.second)
return false;
return (b.second >= a.second);
}
if (a.first == -1) {
if (a.second >= b.first && a.second < b.second)
return false;
return (a.second >= b.second);
}
return a.first < b.first;
}
};
class wheel {
double sum;
map<pair<double, double>, int, wheelcomp> ranges;
public:
wheel(double * p, int l) {
sum = 0;
for (int i = 0; i < l; i++) {
ranges[pair<double, double>(sum, p[i]+sum)] = i;
sum += p[i];
}
}
int spin(double random) {
return ranges.find(pair<double, double>(-1, random*sum))->second;
}
};
int main(void) {
// init random numbers
boost::mt19937 mt;
boost::uniform_real<> uniform(0,1);
boost::variate_generator<boost::mt19937&,boost::uniform_real<> > rng(mt, uniform);
// build_probabilities
vector<double> p(CHOICES, 0);
for (int i = 0; i < CHOICES; i++)
p[i] = rng();
// perform selections
int k;
wheel w(&p[0], CHOICES);
for (int i = 0; i < SPINS; i++)
k = w.spin(rng());
// the boost way
boost::random::discrete_distribution<> dist(p);
for (int i = 0; i < SPINS; i++)
k= dist(mt);
return k;
}
Without any further optimization, the code has been compiled with gcc. The boost version takes two seconds for ten million selections, whereas the std::map version needs 6.1 seconds for the same amount of selections.