C++でランダムフロートを生成する方法
整数Randを取り、それを何かで割ることができると思いましたが、それで十分でしょうか。
Rand()
は、C++で疑似乱数を生成するために使用できます。 Rand_MAX
と小さな数学を組み合わせることで、任意の任意の間隔で乱数を生成できます。これは学習目的や玩具プログラムには十分です。正規分布で本当に乱数が必要な場合は、もっと高度な方法を使う必要があります。
これにより、0.0から1.0までの数値が生成されます。
float r = static_cast <float> (Rand()) / static_cast <float> (Rand_MAX);
これは0.0から任意のfloat
、X
までの数を生成します。
float r2 = static_cast <float> (Rand()) / (static_cast <float> (Rand_MAX/X));
これは、任意のLO
から任意のHI
までの数を生成します。
float r3 = LO + static_cast <float> (Rand()) /( static_cast <float> (Rand_MAX/(HI-LO)));
本当に乱数が必要な場合、Rand()
関数では十分ではないことが多いことに注意してください。
Rand()
を呼び出す前に、まずsrand()
を呼び出して乱数ジェネレータを "シード"しなければなりません。これはRand()
を呼び出すたびにではなく、プログラムの実行中に一度行われるべきです。これはしばしば次のように行われます。
srand (static_cast <unsigned> (time(0)));
Rand
またはsrand
を呼び出すには、#include <cstdlib>
が必要です。
time
を呼び出すには、#include <ctime>
を指定する必要があります。
C++ 11では、 random
を使用して多くの新しいオプションが提供されます。このトピックに関する標準的な論文は N3551、C++ 11での乱数生成 です
Rand()
の使用が問題となる理由を確認するには、Stephan T. Lavavejによる Rand()考慮された有害 プレゼンテーション資料を参照してくださいGoingNative 2013イベント中に指定。スライドはコメントにありますが、これは 直接リンク です。
レガシーコードでもサポートが必要な場合があるため、boost
の使用とRand
についても説明します。
以下の例はcppreferenceサイトから抽出され、 std :: mersenne_twister_engine エンジンと std :: uniform_real_distribution を使用し、他のエンジンで[0,10)
間隔で数値を生成しますそしてディストリビューションはコメントアウトされました(ライブで見る):
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>
int main()
{
std::random_device rd;
//
// Engines
//
std::mt19937 e2(rd());
//std::knuth_b e2(rd());
//std::default_random_engine e2(rd()) ;
//
// Distribtuions
//
std::uniform_real_distribution<> dist(0, 10);
//std::normal_distribution<> dist(2, 2);
//std::student_t_distribution<> dist(5);
//std::poisson_distribution<> dist(2);
//std::extreme_value_distribution<> dist(0,2);
std::map<int, int> hist;
for (int n = 0; n < 10000; ++n) {
++hist[std::floor(dist(e2))];
}
for (auto p : hist) {
std::cout << std::fixed << std::setprecision(1) << std::setw(2)
<< p.first << ' ' << std::string(p.second/200, '*') << '\n';
}
}
出力は次のようになります。
0 ****
1 ****
2 ****
3 ****
4 *****
5 ****
6 *****
7 ****
8 *****
9 ****
出力は、選択した分布によって異なるため、平均との両方に対して std :: normal_distribution で2
の値を使用することにした場合stddev例dist(2, 2)
代わりに、出力は次のようになります(ライブ表示):
-6
-5
-4
-3
-2 **
-1 ****
0 *******
1 *********
2 *********
3 *******
4 ****
5 **
6
7
8
9
以下は、N3551
(ライブを見る)に示されているコードの一部を修正したものです。
#include <algorithm>
#include <array>
#include <iostream>
#include <random>
std::default_random_engine & global_urng( )
{
static std::default_random_engine u{};
return u ;
}
void randomize( )
{
static std::random_device rd{};
global_urng().seed( rd() );
}
int main( )
{
// Manufacture a deck of cards:
using card = int;
std::array<card,52> deck{};
std::iota(deck.begin(), deck.end(), 0);
randomize( ) ;
std::shuffle(deck.begin(), deck.end(), global_urng());
// Display each card in the shuffled deck:
auto suit = []( card c ) { return "SHDC"[c / 13]; };
auto rank = []( card c ) { return "AKQJT98765432"[c % 13]; };
for( card c : deck )
std::cout << ' ' << rank(c) << suit(c);
std::cout << std::endl;
}
結果は次のようになります。
5H 5S AS 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC QD JD AH JC AC KD 9D 5C 2H 4H 9C 8C JH 5D 4S 7C AD 3S 8S TS 2C 8D 3H 6C JS 7S 6S
ブースト
もちろん Boost.Random も常にオプションです。ここでは boost :: random :: uniform_real_distribution を使用しています:
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>
int main()
{
boost::random::mt19937 gen;
boost::random::uniform_real_distribution<> dist(0, 10);
std::map<int, int> hist;
for (int n = 0; n < 10000; ++n) {
++hist[std::floor(dist(gen))];
}
for (auto p : hist) {
std::cout << std::fixed << std::setprecision(1) << std::setw(2)
<< p.first << ' ' << std::string(p.second/200, '*') << '\n';
}
}
ランド()
Rand()
を使用する必要がある場合、C FAQに進み、 浮動小数点乱数を生成するにはどうすればよいですか? のガイドを参照できます。基本的に、間隔[0,1)
を生成するためのこれに似た例を示します。
#include <stdlib.h>
double randZeroToOne()
{
return Rand() / (Rand_MAX + 1.);
}
[M,N)
の範囲で乱数を生成するには:
double randMToN(double M, double N)
{
return M + (Rand() / ( Rand_MAX / (N-M) ) ) ;
}
Boost.Random を見てください。あなたはこのようなことをすることができます:
float gen_random_float(float min, float max)
{
boost::mt19937 rng;
boost::uniform_real<float> u(min, max);
boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > gen(rng, u);
return gen();
}
毎回新しいmt19937オブジェクトを構築するよりも、同じmt19937オブジェクトを渡すほうが良いかもしれませんが、うまくいけば、あなたはアイデアを得ます。
2つのfloat
値を使用してコードを呼び出すと、コードは任意の範囲で機能します。
float Rand_FloatRange(float a, float b)
{
return ((b - a) * ((float)Rand() / Rand_MAX)) + a;
}
CではなくC++を使用している場合は、テクニカルレポート1(TR1)およびC++ 0xドラフトで、ヘッダーファイルに乱数ジェネレータの機能が追加されていることを思い出してください。これはBoostと同じです。ランダムライブラリで、間違いなくCライブラリ関数のRandよりも柔軟で「現代的」です。
この構文はジェネレータを選択し( mersenne twister mt19937のように)、次に分布(正規分布、ベルヌーイ分布、二項分布など)を選択する機能を提供します。
構文は次のとおりです(恥知らずの このサイト から借用)。
#include <iostream>
#include <random>
...
std::tr1::mt19937 eng; // a core engine class
std::tr1::normal_distribution<float> dist;
for (int i = 0; i < 10; ++i)
std::cout << dist(eng) << std::endl;
現代のc++
では、<random>
に付属の c++11
ヘッダを使うことができます。
ランダムなfloat
を得るためにはstd::uniform_real_distribution<>
を使うことができます。
あなたは数を生成するために関数を使うことができます、そしてあなたがいつも数を同じにしたくないならば、エンジンと分布をに設定してください。 static
になります。
例:
float get_random()
{
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
return dis(e);
}
float
をstd::vector
などのコンテナに入れるのが理想的です。
int main()
{
std::vector<float> nums;
for (int i{}; i != 5; ++i) // Generate 5 random floats
nums.emplace_back(get_random());
for (const auto& i : nums) std::cout << i << " ";
}
出力例
0.0518757 0.969106 0.0985112 0.0895674 0.895542
いくつかのシステム(VCを持つWindowsは、現在頭に浮かぶ)で、Rand_MAX
はばかげて小さいです。 e。わずか15ビット。 Rand_MAX
で割ると、23ビットの代わりに15ビットの仮数しか生成されません。これはあなたにとって問題であるかもしれないしそうでないかもしれませんが、その場合いくつかの値を逃しています。
ああ、ちょうどその問題に対するコメントがすでにあることに気づいた。とにかく、これはあなたのためにこれを解決するかもしれないいくつかのコードです:
float r = (float)((Rand() << 15 + Rand()) & ((1 << 24) - 1)) / (1 << 24);
テストされていませんが、うまくいくかもしれません:-)
drand48(3)
はPOSIXの標準的な方法です。 GLibCはリエントラント版 drand48_r(3)
も提供します。
この関数はSVID 3で廃止されたと宣言されましたが、適切な代替手段は提供されていなかったので IEEE Std 1003.1-2013 にはまだ含まれてまもなく。
Windowsでは、標準的な方法は CryptGenRandom() です。
あなたの浮動小数点フォーマットが IEEE 754 (IntelやARMを含むほとんどすべての最近のCPU)であることを知っているなら、ビットごとの方法を使ってランダムな整数からランダムな浮動小数点数を構築できます。これは、C++ 11の random
または Boost.Random
にアクセスできない場合にのみ考慮する必要があります。
float Rand_float()
{
// returns a random value in the range [0.0-1.0)
// start with a bit pattern equating to 1.0
uint32_t pattern = 0x3f800000;
// get 23 bits of random integer
uint32_t random23 = 0x7fffff & (Rand() << 8 ^ Rand());
// replace the mantissa, resulting in a number [1.0-2.0)
pattern |= random23;
// convert from int to float without undefined behavior
assert(sizeof(float) == sizeof(uint32_t));
char buffer[sizeof(float)];
memcpy(buffer, &pattern, sizeof(float));
float f;
memcpy(&f, buffer, sizeof(float));
return f - 1.0;
}
これは、除算を使用したものよりも良い分布になります。
私は今のところ答えのどれにも満足していなかったので、私は新しいランダムなfloat関数を書きました。 floatデータ型についてビットごとの仮定をします。それでも少なくとも15のランダムビットを持つRand()関数が必要です。
//Returns a random number in the range [0.0f, 1.0f). Every
//bit of the mantissa is randomized.
float rnd(void){
//Generate a random number in the range [0.5f, 1.0f).
unsigned int ret = 0x3F000000 | (0x7FFFFF & ((Rand() << 8) ^ Rand()));
unsigned short coinFlips;
//If the coin is tails, return the number, otherwise
//divide the random number by two by decrementing the
//exponent and keep going. The exponent starts at 63.
//Each loop represents 15 random bits, a.k.a. 'coin flips'.
#define RND_INNER_LOOP() \
if( coinFlips & 1 ) break; \
coinFlips >>= 1; \
ret -= 0x800000
for(;;){
coinFlips = Rand();
RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
//At this point, the exponent is 60, 45, 30, 15, or 0.
//If the exponent is 0, then the number equals 0.0f.
if( ! (ret & 0x3F800000) ) return 0.0f;
RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
}
return *((float *)(&ret));
}
私の意見では、上記の答えは「ランダムな」フロートを与えますが、それらのどれもが真にランダムなフロートではありません(つまり、フロート表現の一部を見逃しています)。私の実装に突入する前に、まずフロートのANSI/IEEE標準フォーマットを見てみましょう。
|符号(1ビット)| e(8ビット) f(23ビット)
この単語で表される数は(-1 * sign)* 2 ^ e * 1.fです。
'e'番号は-127から126の範囲のバイアスされた(127のバイアスで)番号であることに注意してください。最も単純な(そして実際には最もランダムな)関数は単にintにランダムintのデータを書き込むことですしたがって
int tmp = Rand();
float f = (float)*((float*)&tmp);
float f = (float)Rand();
を実行すると、整数をfloatに変換します(つまり10は10.0になります)。
だから今、あなたが最大値を制限したいのであれば、あなたは何かこれをすることができます(これがうまくいくかどうかわからない)
int tmp = Rand();
float f = *((float*)&tmp);
tmp = (unsigned int)f // note float to int conversion!
tmp %= max_number;
f -= tmp;
しかし、フロートの構造を見ると、フロートの最大値は(約)2 ^ 127で、これはintの最大値(2 ^ 32)よりかなり大きいことがわかります。 floatで表現できる数値これが私の最後の実装です。
/**
* Function generates a random float using the upper_bound float to determine
* the upper bound for the exponent and for the fractional part.
* @param min_exp sets the minimum number (closest to 0) to 1 * e^min_exp (min -127)
* @param max_exp sets the maximum number to 2 * e^max_exp (max 126)
* @param sign_flag if sign_flag = 0 the random number is always positive, if
* sign_flag = 1 then the sign bit is random as well
* @return a random float
*/
float randf(int min_exp, int max_exp, char sign_flag) {
assert(min_exp <= max_exp);
int min_exp_mod = min_exp + 126;
int sign_mod = sign_flag + 1;
int frac_mod = (1 << 23);
int s = Rand() % sign_mod; // note x % 1 = 0
int e = (Rand() % max_exp) + min_exp_mod;
int f = Rand() % frac_mod;
int tmp = (s << 31) | (e << 23) | f;
float r = (float)*((float*)(&tmp));
/** uncomment if you want to see the structure of the float. */
// printf("%x, %x, %x, %x, %f\n", (s << 31), (e << 23), f, tmp, r);
return r;
}
この関数randf(0, 8, 0)
を使うと、0.0から255.0の間の乱数が返されます。
C++の場合、dist
変数で指定された範囲内で実数を生成できます。
#include <random> //If it doesnt work then use #include <tr1/random>
#include <iostream>
using namespace std;
typedef std::tr1::ranlux64_base_01 Myeng;
typedef std::tr1::normal_distribution<double> Mydist;
int main() {
Myeng eng;
eng.seed((unsigned int) time(NULL)); //initializing generator to January 1, 1970);
Mydist dist(1,10);
dist.reset(); // discard any cached values
for (int i = 0; i < 10; i++)
{
std::cout << "a random value == " << (int)dist(eng) << std::endl;
}
return (0);
}
#include <cstdint>
#include <cstdlib>
#include <ctime>
using namespace std;
/* single precision float offers 24bit worth of linear distance from 1.0f to 0.0f */
float getval() {
/* Rand() has min 16bit, but we need a 24bit random number. */
uint_least32_t r = (Rand() & 0xffff) + ((Rand() & 0x00ff) << 16);
/* 5.9604645E-8 is (1f - 0.99999994f), 0.99999994f is the first value less than 1f. */
return (double)r * 5.9604645E-8;
}
int main()
{
srand(time(NULL));
...
私は2つの答えを投稿することができませんでした、それでこれが2番目の解決策です。 log2乱数、0.0fへの大規模なバイアスが、それは本当にランダムな1.0fから0.0fまでのfloatです。
#include <cstdint>
#include <cstdlib>
#include <ctime>
using namespace std;
float getval () {
union UNION {
uint32_t i;
float f;
} r;
/* 3 because it's 0011, the first bit is the float's sign.
* Clearing the second bit eliminates values > 1.0f.
*/
r.i = (Rand () & 0xffff) + ((Rand () & 0x3fff) << 16);
return r.f;
}
int main ()
{
srand (time (NULL));
...
Rand()は、0からRand_MAXまでの整数を返します。 0.0と1.0の間の乱数を取得するには、最初にRand()によって返されたintをfloatにキャストし、次にRand_MAXで除算します。