On 4 May 2008, at 00:57, Marshall Clow wrote:
This should be pretty easy to write. I would be tempted to
pass in a "const boost::array < std::pair, N>",
which would give return values and probabilities; and use a
std::vector<double> to calculate cumulative probabilities, and for
each call to the RNG, do a binary search in the vector to find the
right spot, and then return the associated value.
That's not quite, but almost what I did, after mailing yesterday. I
allow the probabilities (but not the objects) to be changed.
For what I'm actually doing, I need to alter the probabilities quite a
lot so it seemed silly to have to construct a new object.
I tried to write it so it fits in as much as possible with boost/
random so it should be useful for someone else in same situation.
Thanks,
Kevin Martin
templateclass relative_frequency
{
public:
typedef DataType result_type; //for number generator
typedef int input_type; //for random distribution
typedef boost::array data_array;
typedef boost::array rf_array;
typedef boost::array f_array;
private:
data_array mData; //the items
rf_array mProb; //cumulative frequencies
public:
//we are completely independent in invocations
inline void reset() {}
//Find the cf we are in and return the appropriate object.
template<class Engine>result_type operator() const (
Engine &urng) {
double val = urng()/double(urng.max()-urng.min());
int pos = std::upper_bound(mProb.begin(), mProb.end(), val) -
mProb.begin();
//if there is some kind of nasty double based rounding error,
we may have gone right
//to the end, in which case we choose the last element
if(pos == NumData) --pos;
return *(mData.begin() + pos);
}
//construct with data and rfreq or data and freq
relative_frequency_distribution(const data_array &data, const
rf_array &rfreq)
: mData(data) {
SetRFrequencies(rfreq);
}
relative_frequency_distribution(const data_array &data, const
f_array &freq)
: mData(data) {
SetFrequencies(freq);
}
//set the frequencies and relative frequencies
void SetFrequencies(const f_array &freq) {
//Construct a rf array and call SetRFrequencies
rf_array rfreq;
double total = accumulate(freq.begin(), freq.end(), 0);
for(int i=0;i!=rfreq.size();++i) {
rfreq[i] = double(freq[i])/total;
}
SetRFrequencies(rfreq);
}
void SetRFrequencies(const rf_array &rfreq) {
//We just sum this up to get the cdf
std::partial_sum(rfreq.begin(), rfreq.end(), mProb.begin());
}
};