random_range_generator

ある一意な確率で出現する範囲の乱数を得るために variate_generator を使って乱数を取り出してたんだけど、variate_generator は一度作ったら範囲を変更できない。
なので、

// [first, last)
int rnd = boost::variate_generator<boost::mt19937, boost::uniform_int<int> >(mt_, boost::uniform_int<int>(first, last - 1))();

こんな感じに書くことになるんだけど毎回こんなの書いてられないので自前で何か作ろうと思ったんだけど、一応 boost の中身を見てみたら random_number_generator というのがあった。

// どっかで作っておく
boost::random_number_generator<boost::mt19937> random(mt_);
...
// [first, last)
int rnd = random(last - first) + first;

こんな感じになるのかな。
これでもいいんだけど、もっと直接的に範囲が取れた方が嬉しいので、ちょっと改良してみた。

#include <boost/config.hpp>
#include <boost/limits.hpp>
#include <boost/static_assert.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/variate_generator.hpp>

template<class UniformRandomNumberGenerator, class IntType = long>
class random_range_generator
{
public:
  typedef UniformRandomNumberGenerator base_type;
  typedef IntType argument_type;
  typedef IntType result_type;
  random_range_generator(base_type& rng) : _rng(rng)
  {
    // 必ずチェックする方向で。
    BOOST_STATIC_ASSERT(std::numeric_limits<result_type>::is_integer);
  }

  result_type operator()(argument_type f, argument_type l)
  {
    typedef uniform_int<IntType> dist_type;
    return variate_generator<base_type&, dist_type>(_rng, dist_type(f, l-1))();
  }

private:
  base_type& _rng;
};
// どっかで作っておく
random_range_generator<boost::mt19937> random(mt_);
...
// [first, last)
int rnd = random(first, last);

UniformRandomNumberGenerator はコピーするか参照するか迷ったんだけど、random_range_generator みたいな小物(?)が UniformRandomNumberGenerator を独占するのはいかがなものかと思ってコピーするのをやめた。