Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialising std::discrete_distribution in VS2013

I have a std::vector<float> weights; containing the list of weights. I don't know what will be in this list until some stage in running the program. I would like to do

std::discrete_distribution<> dist(weights.begin(),weights.end());

but VS2013 doesn't appear to have a constructor for std::discrete_distribution that accepts an iterator range. Is there any workaround?

like image 720
pighead10 Avatar asked Feb 22 '14 19:02

pighead10


1 Answers

Compare cppreference.com and the Microsoft reference for std::discrete_distribution:

These are the constructors provided by VS2013:

discrete_distribution();
explicit discrete_distribution(
    const param_type& par0
);
discrete_distribution(
    initializer_list<double> IList
);
template<class Fn>
    discrete_distribution(
        size_t count,
        double low, 
        double high, 
        Fn func
);

There is one important constructor missing, probably because the Microsoft developers didn't have the time to implement it:

template< class InputIt >
 discrete_distribution( InputIt first, InputIt last );

That means, unless the documentation is incomplete, you simply can't use an iterator-based constructor for this class. Switch to another compiler (like clang or g++), or wait until this feature is implemented.

Now for a workaround you can use right now:

std::size_t i(0);
assert( !weights.empty() ); // No weights would be very weird.
std::discrete_distribution<> dist(weights.size(),
                                  0.0, // dummy!
                                  0.0, // dummy!
                                  [&weights,&i](double)
{
   auto w = weights[i];
   ++i;
   return w;
 });

I hope that at least lambdas are supported ;-) The important thing is to capture i by reference, such that it gets properly incremented. Demo: http://ideone.com/nIBUts

Why does this work? The constructor we are using here is:

template< class UnaryOperation >
discrete_distribution( std::size_t count, double xmin, double xmax,
                       UnaryOperation unary_op );

The documentation on cppreference tells us that the count (in our case weights.size()), as well as the xmin and xmax is used to create the weights using the UnaryOperation.

We are ignoring xmin and xmax on purpose. As the UnaryOperation we use the lambda

[&weights,&i](double)
{
   auto w = weights[i];
   ++i;
   return w;
 }

or

[&weights,&i](double)
{
   return weights[i++];
}

if you prefer.

Now, we are ignoring the input value for that operator and just return the i^th element of our vector. We capture both the vector and the index by reference to avoid copies.

like image 179
stefan Avatar answered Nov 04 '22 07:11

stefan