質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

Q&A

解決済

2回答

2127閲覧

std::randomクラス

BeatStar

総合スコア4958

C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

0グッド

0クリップ

投稿2017/06/29 02:48

C/C++でやっています。

C++11 で追加された std::random をクラス化して使いやすくしています。

( そのまま使う方法もありますが、乱数生成する方法だけでもかなりあって面倒なので。 )

C++

1#include<random> 2#include<cfloat> 3// それ以外のヘッダで使うやつもインクルード。 4 5class CRandom{ 6 public: 7 CRandom( double min = 0, double max = 0, size_t seed = time( nullptr ) ); 8 ~CRandom(); 9 10 double RandDouble( void ); 11 int RandInt( void ); 12 private: 13 std::mt19937 gen; 14 private: 15 double min; 16 double max; 17}; 18 19 20using namespace std; 21 22 23/************************************************************** 24* 25* クラス名: CRandom 26* 27* 種 類: コンストラクタ 28* 29* 目 的: 乱数を生成する 30* 31* 引 数: 32* double min := 最小値 ( デフォルト: 0 ) 33* double max := 最大値 ( デフォルト: 0 ) 34* size_t seed := シード値 ( デフォルト: time(nullptr) ) 35* 36* 参 ・ 引: 37* 38* 39* 備 考: 40* 41* 42**************************************************************/ 43 44CRandom::CRandom( double min, double max, size_t seed ){ 45 mt19937 temp( seed ); 46 47 // コピー 48 gen = temp; 49 50 this->min = min; 51 52 this->max = max; 53} 54 55 56 57/************************************************************** 58* 59* クラス名: CRandom 60* 61* 種 類: デストラクタ 62* 63* 目 的: 64* 65* 引 数: 66* 67* 68* 参 ・ 引: 69* 70* 71* 備 考: 72* 73* 74**************************************************************/ 75 76CRandom::~CRandom(){ 77 78} 79 80 81 82/************************************************************** 83* 84* クラス名: CRandom 85* 86* 関 数 名: RandDouble 87* 88* 目 的: double型の乱数発行 89* 90* 引 数: 91* void 92* 93* 戻 り 値: 94* 乱数 : double 95* 96* 使 用 例: 97* double d = CRandom.RandDouble(); 98* 99* 参 ・ 引: 100* 101* 102* 備 考: 103* 104* 105**************************************************************/ 106 107double CRandom::RandDouble( void ){ 108 109 if( DoubleCmp( min, max ) == 0 ){ 110 // ここが困っている部分。 111 112 /* 113 // 方法1: RandIntメンバ関数のときと同じ gen() で生成し、それをdoubleにキャスト 114 return (double)gen(); 115 */ 116 117 /* 118 // 方法2: 最大値と最小値を(別の)乱数で考える 119 double r = gen(); 120 this->min = (double)(r * (-1)); // -1 をかければ 負の数になる 121 this->max = (double)r; // 最大値はそのまま 122 */ 123 124 /* 125 // 方法3: (最小値と最大値の)決め打ち 126 this->min = 000000000000000000.222507; // 適当? 127 this->max = 1797000000000000; // 適当? 128 */ 129 130 /* 131 // 方法4: cfloat ヘッダにある doubleの有効範囲定数を使用する 132 this->min = DBL_MIN; 133 this->max = DBL_MAX; 134 */ 135 136 uniform_real_distribution<> dist( min, max ); 137 138 /* 139 // 方法5: uniform_real_distribution<> の引数無しでやってみる 140 uniform_real_distribution<> dist; 141 */ 142 143 return dist(gen); 144 }else{ 145 // 少数として生成する 146 uniform_real_distribution<> dist( min, max ); 147 return dist( gen ); 148 } 149} 150 151 152 153/************************************************************** 154* 155* クラス名: CRandom 156* 157* 関 数 名: RandInt 158* 159* 目 的: int型の乱数発行 160* 161* 引 数: 162* void 163* 164* 戻 り 値: 165* 乱数 : int 166* 167* 使 用 例: 168* int i = CRandom.RandInt(); 169* 170* 参 ・ 引: 171* 172* 173* 備 考: 174* 175* 176**************************************************************/ 177 178int CRandom::RandInt( void ){ 179 180 if( DoubleCmp( min, max ) == 0 ){ 181 return gen(); 182 }else{ 183 // 整数として生成する 184 uniform_int_distribution<> dist( (int)min, (int)max ); 185 return dist( gen ); 186 } 187}

のようにしています。

ソースコード上には、DoubleCmp(...) というのがありますが、

これは別のファイルで double型データを比較する関数だとします。

strcmp関数みたいに 同じであれば 0 を返します。

つまり、上記コードの if( DoubleCmp( ... ) == 0 ) の部分は、

「引数として与えられたmin, max が 同じであるとき」という意味です。

一応機能するのですが、

問題は (0,0) や (1,1) のように min = max の状態 であるときの RandDoubleメンバ関数 が返す値です。

main関数でためしに

C++

1CRandom Random; // デフォルト引数を使用。 2 3for( int i = 0; i < 10; i++ ){ 4 cout << Random.RandDouble() << endl; 5}

とやってみると、

1.12043e+015 1.63118e+009 -7.702e+008 -2.74263e+009 9.6332e+008 2.72246e+009 2.6308e+009 -1.82902e+009 2.18942e+009 1.27056e+009

のようになります。

表示データを見ると、

1.12043e+015 という風に、科学的記数法というのでしょうか?

これになってしまいます。

ちゃんとした引数を与えて ( ただし min != maxであること。例えば、(0.53, 100.35) 等。 ) やってみると、

82.3097 68.0833 90.1891 24.8984 53.918 97.9953 71.7978 60.8671 47.8723 28.3752

という風になります。

私がやりたいのは、1.63118e+009 みたいな乱数データ取得ではなく、82.3097 のような 乱数データ取得です。

これって不可能なのでしょうか?

一応思いついた 方法1~方法5まではすべて試してみました。

しかし、同じでした。 ( もちろん、乱数なので "完全に同じ" ではないですが。 )

何か方法があるのでしょうか?

[情報]
言語 : C/C++ ( C++11 )
コンパイラ: MinGW ( VC++特有の関数とか使えない... )

宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

min == maxの場合、素直に考えれば、同じ値を返し続けるのが正しい挙動ではないかと思うのですが…

つまりは、初期値のmin=0max=0が不適当ではないか、ということです。

投稿2017/06/29 02:52

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2017/07/01 04:09

ご回答ありがとうございます。 >> 同じ値を返し続ける とありますが、 ● min == max なので (どっちでもいいが) double num = max として その値を返す ( 最大値か最小値のいずれかを定数状態で返す ) ● min, max が同じであるとき, (何でもいいが ここは とりあえず 0 として) 0 を定数 のようにして返す ● それ以外 のどれなのでしょうか? (私の読解力が低すぎて...)
maisumakun

2017/07/01 05:19

min == max なので、仮にランダムだとしても取れる値が1つしかない、つまりはmin(でもmaxでもいいけど)をそのまま返す、という意味合いです。
BeatStar

2017/07/01 05:50

なるほど。 つまり定数として返せば? ということですね。
guest

0

ベストアンサー

こんにちは。

まず、maisumakunさんの指摘は妥当と思います。
そして、その仕様ではなく、min=maxの時はmin/max無指定として動作する仕様のようですね。
min/max無指定の時の振る舞いとして5種類の方法を試されているようです。
提示されている結果がそのどの結果なのか分からないので厳密な回答はできません。

さて、double型は浮動小数点型ですので、巨大な値から0に非常に近い値まで表現できます。それらを例えば、2384712934862.38のように表現すると見づらいですし、更にもっと大きな値の場合、238471293486238000000000000000や、もっと0に近い値の場合、0.00000000000000000002384712934862のようなことになります。まじで見づらいと思います。

この問題に対処できる有効数字 x 10のn乗と言う表現を数学で習うと思います。その表現をコンピュータの通常の文字で表現したものが、1.12043e+015です。

double型の一般的な有効桁数は10進数の場合、概ね16桁ですので、この範囲に限定することで10の指数表現にしないことが狙えます。例えばデフォルト値をmin=-9999999999999999max=9999999999999999にするなど。

しかし、std::streamの標準では16桁もの有効数字を表示しませんので、16桁の精度で表示するよう指示することで指数表現させないことが可能です。<iomanip>にあるstd::setprecision()を使います。

C++

1#include <iostream> 2#include <iomanip> 3 4int main() 5{ 6 std::cout << 2384712934862.38 << "\n"; 7 std::cout << std::setprecision(16) << 2384712934862.38 << "\n"; 8} 9// 実行結果(g++) 10// 2.38471e+12 11// 2384712934862.38

ちゃんとした引数を与えて ( ただし min != maxであること。例えば、(0.53, 100.35) 等。 ) やってみると、

この場合は、minとmaxがmin=-9999999999999999max=9999999999999999の範囲に入り、かつ、std::ostreamのデフォルトの有効桁数(たぶん6桁)に入っているので指数表現にならなかっただけです。

投稿2017/06/29 04:17

編集2017/06/29 06:04
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2017/07/01 04:09

丁寧な説明、ありがとうございます。 なるほど。 つまり、 ● 基本的に double を使う場合は そのまま(科学的記数法) 使う (有効範囲に入っていれば私が言う"通常の数値"になる) ● double で一般的に有効範囲は16桁。しかしstd::streamではiomanipヘッダのstd::setprecisionで16桁であることを示す必要がある ● 質問でのCRandomクラスに値(min,max)を与える( ただし, min != max ) 場合は「たまたま有効桁数の範囲に収まっていた」ために 科学的記数法 ではなく、私がいう"通常の数値" であった でしょうか? その解釈でいいなら、 科学的記数法のまま使い、表示する場合はstd::setprecision で指定して使うことになりますね。
Chironian

2017/07/01 05:25

> 科学的記数法のまま使い、表示する場合はstd::setprecision で指定して使うことになりますね。 う~ん、ちょっと違うと思います。「科学的記数法」と呼ばれている表現は単に数値を人が読めるようにするための記述方式です。コンピュータが内部で記録しているフォーマットとはあまり関係ありません。 double型で演算する時、コンピュータは「科学的記数法」ではなく、IEEE754等の内部表現のまま演算します。
BeatStar

2017/07/01 05:54

> double型で演算す... これって、コンピュータが勝手に解釈するってことでしょうか? 例えば、C言語風? の char は 実際には 数値 ( 範囲はあれだけど。 ) だけど、 コンピュータがcharは 文字として解釈する...みたいな感じでいいのですか? それなら楽そうですが。 ( そうじゃないなら、 数値 <=> 科学的記法 と変換する必要がありますよね? )
Chironian

2017/07/01 06:28 編集

> これって、コンピュータが勝手に解釈するってことでしょうか? double型については、ハードウェアで直接演算できるCPUもあります。PCのCPUは大抵そうです。 しかし、そうでないものも少なくないです。例えば洗濯機に入っているCPUとか。そのようなCPUで浮動小数点演算をさせようとする場合、数値計算ライブラリを使う場合が多いです。 どちらのケースでも、途中でいちいち10の指数表現へ変換して計算することはありません。 科学的記法(10の指数表現)は文字列ですから、コンピュータにとって計算しづらい表現形式です。 > 例えば、C言語風? の char は 実際には 数値 ( 範囲はあれだけど。 ) だけど、 コンピュータがcharは 文字として解釈する...みたいな感じでいいのですか? コンピュータのハードウェアにとってはchar型もint型も同じく単なる数値ですよ。ビット数が違うだけ。 文字として解釈しているのは、標準ライブラリやアプリケーション・プログラムです。 > ( そうじゃないなら、 数値 <=> 科学的記法 と変換する必要がありますよね? ) std::istreamやstd::ostreamはそのような変換機能を持っています。 だから、本来2進数で記録されている各種の値を10進数形式の文字列で表示できます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問