・・・ある乱数列のi番目から先を再現したい。
つまるところ例えば1~1000までの数字をランダムに並べた乱数列
101, 49, 430, 221, ・・・・のうち、
例えば1回目に表示した乱数列の120番目から先をもう一度再現したいといった具合だ。
もちろん初期値を保存して、120番目まで進めればそれで終わりなのだが、もうちょっとスマートにできないかと考えてみた。
よくよく考えてみると、乱数の取り出し方というのはイテレーターのそれに似ていると思う。
取り出すたびに値が変わってしまうのがたとえば、rand関数なわけだが
適当なクラスでラップして
①operator*で値の取り出しを行う
②operator++で次の乱数を得る
というようなつくりにすれば、これはforward_iteratorのように使えるのではないか・・・?
とはいえ、イテレーターを1から実装するのは面倒臭い。
ということでboost::iterator_facadeを使って実装してみた。
もちろんC++11ではメルセンヌツイスタや、範囲指定に使いやすいuniform_int_distribution等が用意されているので、
それらが使えるような実装を考える。
#include
template
class random_number_iterator
: public boost::iterator_facade,
typename const Distribution::result_type,
boost::forward_traversal_tag
>
{
public:
random_number_iterator()
{
increment();
}
random_number_iterator( Distribution distribution, Engine engine )
:buf_( distribution( engine ) ),
distribution_( distribution ),
engine_( engine )
{
}
void increment()
{
buf_ = distribution_( engine_ );
}
bool equal(const random_number_iterator& other) const {
return buf_ == other.buf_ && engine_ == other.engine_
&& distribution_.min() == other.distribution_.min() && distribution_.max() == other.distribution_.max();
}
const typename Distribution::result_type& dereference() const
{
return buf_;
}
private:
friend class boost::iterator_core_access;
typename Distribution::result_type buf_;
Distribution distribution_;
Engine engine_;
};
以下のようなコードを書いてみたら、どうやら大体思っていたものができたみたい。
#include
#include
#include
#include
//クラスのところは省略
using namespace std;
int main()
{
random_number_iterator,std::mt19937>
itr( std::uniform_int_distribution(0,100), std::mt19937( static_cast(time(0)) ) );
auto itr2(itr);
cout ,std::mt19937>
itr( std::uniform_real_distribution(0,100.0), std::mt19937( static_cast(time(0)) ) );
最近作ったガラクタの中では結構使えそうな予感。(最近外乱モデル等で乱数ばっか使ってるから・・・)
・・・でもたぶんもっといい実装がありそう
追記:
どう見てもクラスの名前が長すぎるから
template
random_number_iterator
create_random_number_iterator( Distribution distribution, Engine engine )
{
return random_number_iterator( distribution, engine );
}
using namespace std;
int main()
{
std::mt19937 engine( static_cast(time(0)) );
std::uniform_real_distribution distribution(0,100.0);
auto itr = create_random_number_iterator( distribution , engine );
auto itr2(itr);
・・・