追記:
自分のアホでミスっていただけでした。結論から言うとresultをunsigned intにしているのに気付いていないままコードを書いていただけでした…お騒がせしました…
よって、この記事は情報が0に近い記事になってしまったわけですが、同じようなうっかりさんが居るかもしれないんのでこのまま残しておきます。
色々あって精度のそれなりな乱数を使いたいなって事でC++11から入った
コードの規模を小さくして検証してみると、どうもdistributionの動きが僕が思ってるのと違うっぽい?
以下0~100の範囲で乱数を100回生成させるようなコードなのですが、これはまあ思った通りに動く。 <コード>
#include<iostream> #include<iomanip> #include<random> int main() { std::random_device rand_seed;//非決定論的乱数生成器 std::mt19937 mt_obj(rand_seed());//メルセンヌ・ツイスタで乱数を生成する std::uniform_int_distribution<int>::param_type uniform_parameter(0, 100);//整形する幅を決める std::uniform_int_distribution<int> uniform(uniform_parameter);//生成された乱数を指定された幅の一様分布に整形する static int calc_num = 0; unsigned int result = uniform(mt_obj); for (int i = 0; i < 100; i++) { if (i != 0) {//初回は飛ばす result = uniform(mt_obj); } calc_num++; //表示の幅やレイアウトについて指定して出力している std::cout << std::setw(5) << std::left << calc_num << ":result = " << std::setw(5) << std::right << result << std::endl; } return 0; }
<実行結果>
1 :result = 77 2 :result = 64 3 :result = 63 4 :result = 87 5 :result = 22 6 :result = 3 7 :result = 48 8 :result = 62 9 :result = 0 10 :result = 42 11 :result = 78 12 :result = 52 13 :result = 64 14 :result = 37 15 :result = 51 16 :result = 94 17 :result = 14 18 :result = 24 19 :result = 86 20 :result = 16 21 :result = 25 22 :result = 53 23 :result = 30 24 :result = 45 25 :result = 48 26 :result = 47 27 :result = 17 28 :result = 54 29 :result = 85 30 :result = 99 31 :result = 76 32 :result = 18 33 :result = 100 34 :result = 15 35 :result = 18 36 :result = 17 37 :result = 37 38 :result = 58 39 :result = 77 40 :result = 59 41 :result = 50 42 :result = 4 43 :result = 54 44 :result = 27 45 :result = 66 46 :result = 41 47 :result = 57 48 :result = 63 49 :result = 43 50 :result = 75 51 :result = 10 52 :result = 58 53 :result = 24 54 :result = 3 55 :result = 6 56 :result = 97 57 :result = 37 58 :result = 8 59 :result = 74 60 :result = 35 61 :result = 46 62 :result = 88 63 :result = 66 64 :result = 30 65 :result = 93 66 :result = 33 67 :result = 50 68 :result = 43 69 :result = 19 70 :result = 88 71 :result = 93 72 :result = 63 73 :result = 57 74 :result = 42 75 :result = 87 76 :result = 51 77 :result = 44 78 :result = 62 79 :result = 19 80 :result = 88 81 :result = 10 82 :result = 11 83 :result = 40 84 :result = 12 85 :result = 38 86 :result = 90 87 :result = 11 88 :result = 18 89 :result = 42 90 :result = 78 91 :result = 38 92 :result = 2 93 :result = 35 94 :result = 44 95 :result = 92 96 :result = 92 97 :result = 25 98 :result = 0 99 :result = 18 100 :result = 74
ところが、distributionの範囲にマイナスを含むようにすると動きが少しおかしくなる。
以下は-100~100の範囲で乱数を100回生成するコードだと… <コード>
#include<iostream> #include<iomanip> #include<random> int main() { std::random_device rand_seed;//非決定論的乱数生成器 std::mt19937 mt_obj(rand_seed());//メルセンヌ・ツイスタで乱数を生成する std::uniform_int_distribution<int>::param_type uniform_parameter(-100, 100);//整形する幅を決める std::uniform_int_distribution<int> uniform(uniform_parameter);//生成された乱数を指定された幅の一様分布に整形する static int calc_num = 0; unsigned int result = uniform(mt_obj); for (int i = 0; i < 100; i++) { if (i != 0) {//初回は飛ばす result = uniform(mt_obj); } calc_num++; //表示の幅やレイアウトについて指定して出力している std::cout << std::setw(5) << std::left << calc_num << ":result = " << std::setw(5) << std::right << result << std::endl; } return 0; }
<実行結果>
1 :result = 100 2 :result = 72 3 :result = 13 4 :result = 12 5 :result = 4294967282 6 :result = 9 7 :result = 67 8 :result = 91 9 :result = 25 10 :result = 39 11 :result = 4294967232 12 :result = 5 13 :result = 67 14 :result = 45 15 :result = 4294967221 16 :result = 4294967211 17 :result = 4294967266 18 :result = 35 19 :result = 4294967293 20 :result = 4294967256 21 :result = 4294967258 22 :result = 4294967197 23 :result = 68 24 :result = 46 25 :result = 83 26 :result = 71 27 :result = 4294967256 28 :result = 1 29 :result = 63 30 :result = 4294967235 31 :result = 97 32 :result = 4294967291 33 :result = 4294967202 34 :result = 45 35 :result = 4294967272 36 :result = 17 37 :result = 4294967287 38 :result = 33 39 :result = 4294967288 40 :result = 3 41 :result = 4294967271 42 :result = 52 43 :result = 31 44 :result = 16 45 :result = 4294967278 46 :result = 4294967295 47 :result = 4294967204 48 :result = 9 49 :result = 4294967270 50 :result = 44 51 :result = 4294967273 52 :result = 88 53 :result = 4294967221 54 :result = 36 55 :result = 4294967250 56 :result = 29 57 :result = 98 58 :result = 58 59 :result = 4294967277 60 :result = 4294967246 61 :result = 86 62 :result = 66 63 :result = 82 64 :result = 4294967212 65 :result = 60 66 :result = 4294967268 67 :result = 3 68 :result = 83 69 :result = 5 70 :result = 4294967291 71 :result = 4294967263 72 :result = 4294967288 73 :result = 73 74 :result = 88 75 :result = 4294967236 76 :result = 6 77 :result = 39 78 :result = 3 79 :result = 62 80 :result = 4294967212 81 :result = 4294967200 82 :result = 17 83 :result = 4294967224 84 :result = 75 85 :result = 5 86 :result = 4294967289 87 :result = 29 88 :result = 4294967213 89 :result = 59 90 :result = 4294967253 91 :result = 0 92 :result = 10 93 :result = 4294967289 94 :result = 49 95 :result = 37 96 :result = 94 97 :result = 45 98 :result = 4294967293 99 :result = 4294967259 100 :result = 4294967221
と、実行結果がdistributionで定めた範囲の一様分布から外れている。これは負の値はdistributionの範囲に指定できないということなのだろうか?
ただ、std::uniform_int_distribution<int>::param_type
のコンストラクタでは幅の最小最大についてunsigned intではなくintを引数に取っているし、param_type
を使わずにstd::uniform_int_distribution<int>
のコンストラクタもunsigned intではなくintを引数に取っている。
仮にこのdistributionが負の値を範囲指定出来ないなら、この辺がunsigned intで引数に渡せと言われそうなものだけど、これは本当に負の値を範囲指定できないという解釈で良いのだろうか。
もちろん、欲しい乱数の幅をズラすのは、得られた範囲にズラしたい分の幅を足したり引いたりすればいい(例えばdistributionの幅を0~200にしておいて、result = uniform(mt_obj);
をresult = uniform(mt_obj) - 100;
というようにして、得られた値を-100すれば、-100~100の範囲で乱数は得られる)
でもこんなの標準で出来るようにしておいて欲しい、という願望もあるので、何かできる方法は無いのかなあと思うものの、やはりよくわからない。 `
誰か知っていたら教えて下さい。