乱数に再現性を

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

乱数に再現性を

投稿記事 by GRAM » 12年前

この間、とあるプログラムで乱数を次のような形で使う必要が出てきた。

・・・ある乱数列のi番目から先を再現したい。

つまるところ例えば1~1000までの数字をランダムに並べた乱数列
101, 49, 430, 221, ・・・・のうち、
例えば1回目に表示した乱数列の120番目から先をもう一度再現したいといった具合だ。
もちろん初期値を保存して、120番目まで進めればそれで終わりなのだが、もうちょっとスマートにできないかと考えてみた。

よくよく考えてみると、乱数の取り出し方というのはイテレーターのそれに似ていると思う。
取り出すたびに値が変わってしまうのがたとえば、rand関数なわけだが
適当なクラスでラップして
①operator*で値の取り出しを行う
②operator++で次の乱数を得る
というようなつくりにすれば、これはforward_iteratorのように使えるのではないか・・・?

とはいえ、イテレーターを1から実装するのは面倒臭い。
ということでboost::iterator_facadeを使って実装してみた。
もちろんC++11ではメルセンヌツイスタや、範囲指定に使いやすいuniform_int_distribution等が用意されているので、
それらが使えるような実装を考える。

CODE:

#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_;
};
こいつはiteratorとしてある程度使えるようにするための面倒なtypedefやoperator等を勝手に作ってくれる偉い奴らしい。
以下のようなコードを書いてみたら、どうやら大体思っていたものができたみたい。

CODE:

#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)) ) );
のようにすれば、さくっと実数の範囲で使えるようにもできる

最近作ったガラクタの中では結構使えそうな予感。(最近外乱モデル等で乱数ばっか使ってるから・・・)
・・・でもたぶんもっといい実装がありそう

追記:
どう見てもクラスの名前が長すぎるから

CODE:

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);
・・・
みたいな関数を作ったほうがよいか。
最後に編集したユーザー GRAM on 2013年1月18日(金) 23:09 [ 編集 2 回目 ]

コメントはまだありません。