Here’s my approach:
http://ideone.com/PBEL1xWhile determining the portion, the fractional component is carried over to the next bin.
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <cmath>
#include <array>
#include <iterator>
int main(int, char*[]){
using namespace std;
srand(time(0));
// setup bins
int total = 5000;
const int MAX_BINS = 25;
array<int,MAX_BINS> bins;
// determine weighted distribution
array<float,MAX_BINS> weights;
generate(weights.begin(), weights.end(), [](){
return rand()*1.f/RAND_MAX;
});
float weight_sum = accumulate(weights.begin(), weights.end(), 0.f);
// determine portions
// use "extra" as an accumulator to determine when to add an extra 1 to the bin
float extra = 0;
int used = 0;
for ( int idx = 0; idx < MAX_BINS; ++idx ) {
float actual = weights[idx]/weight_sum * total + extra;
int count = (int)actual;
extra = fmodf(actual, 1.f);
bins[idx] = count;
used += count;
}
// any accumulation error is added to the first bin
int error = error;
bins[0] += total - used;
// output results
cout << "total:\t" << total
<< "\nbins:\t";
copy(bins.begin(), bins.end(), ostream_iterator<int>(cout, "\t"));
cout << "\nerror:" << error
<< "\nextra:" << total - accumulate(bins.begin(),bins.end(),0) << '\n';
}
Output:
total: 5000
bins: 119 24 253 186 176 121 283 85 332 51 350 34 304 7 50 371 338 52 333 363 280 372 92 140 284
error: 0
extra: 0
And is flexible where there isn’t much to distribute:
total: 3
bins: 0 0 0 1 0 1 0 0 0 1
error: 0
extra: 0
EDIT:
Actually, I just realized that this isn’t the best approach for small numbers over numerous bins, as the accumulator will favor bins toward the end.