ランダムな浮動小数点数の生成


271

C ++でランダムなフロートを生成するにはどうすればよいですか?

私は整数のランドを取り、それを何かで割ることができると思いました、それで十分でしょうか?


2
それはむしろあなたが数を何のために欲しいのか、そしてどれほどランダムであるかに依存します。通常、rand()は15ビットのランダム性を提供しますが、floatは23ビットの精度を持っているため、一部の値が失われます。
ピートカーカム

1
回答を更新して、利用可能なすべての主要なオプションを含めrandomました。C++ 11で追加されたヘッダーに焦点を合わせるという私の選択は、標準ドキュメントN3924:C ++ 14でのrand()をさらに強化します。rand()主に歴史的な考慮事項の回答に含めますが、レガシーアプリケーションの実現も存在します。
Shafik Yaghmour 2014年

私の答えには、<random>ヘッダーで毎回同じ番号を取得しないようにする方法が含まれています
Andreas DM

回答:


381

rand()C ++で疑似乱数を生成するために使用できます。RAND_MAXと少しの数学を組み合わせることで、選択した任意の間隔で乱数を生成できます。これは、学習目的とおもちゃプログラムには十分です。正規分布の真の乱数が必要な場合は、より高度な方法を採用する必要があります。


これにより、0.0から1.0までの数値が生成されます。

float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);

これは、0.0からいくつかの任意の番号を生成しますfloatX

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()。これは、プログラムの実行中に1回実行する必要がありますrand()。これは多くの場合、次のように行われます。

srand (static_cast <unsigned> (time(0)));

を呼び出すためにrandsrandあなたはする必要があり#include <cstdlib>ます。

を呼び出すにはtime、あなたがする必要があり#include <ctime>ます。


22
最初にシードすることを忘れないでください!
クライム2009年

14
両方の制限が含まれていることに注意してください。
dmckee ---元モデレーターの子猫

14
この答えは誤解を招くものです。Going Native 2013で先週取り上げられました。rand()は非常に詳細な説明のために有害、 channel9.msdn.com / Events / GoingNative / 2013 /…と見なされます。
Ade Miller

14
なぜこれほど多くの人がこの回答に賛成したのか理解できません。数学的に正しくありません。RAND_MAXは非常に小さい数値です(通常は2 ^ 16)。つまり、浮動小数点の23ビットから15だけをランダムにします。他はおそらくゼロになります。実際、一様な分布で乱数を取得しますが、精度は低くなります。たとえば、ランダムジェネレーターは0.00001と0.00002を生成できますが、0.000017は生成できません。したがって、均一な分布になりますが、精度は低くなります(実際の浮動小数点より256倍精度が低くなります)。
DanielHsH 2014

10
@DanielHsH:OPは、を使用してランダムな浮動小数点数を生成するために使用できるメカニズムを具体的に尋ねましたrand()。この質問と私の回答は、基本を学ぶことに特に焦点が当てられており、高度な精度については懸念されていませんでした。走ることを学ぶ前に、歩くことを学ぶ必要があります。
John Dibling 2014

137

C ++ 11は、で多くの新しいオプションを提供しrandomます。このトピックに関する標準的な論文は、N3551、C ++ 11での乱数生成です。

使用rand()が問題になる可能性がある理由を確認するには、GoingNative 2013イベント中に提供された、Stephan T. Lavavejによるrand()考案された有害なプレゼンテーション資料を参照してください。スライドはコメントにありますが、ここに直接リンクがあります。

レガシーコードはまだサポートを必要とする可能性があるので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平均stddevの2両方の値で使用することを決定した場合、たとえば、出力は次のようになります(実際に表示されます)。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()

使用する必要がある場合は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) ) ) ;  
}

1
あなたのrandMToNpls を修正できますか?であることに注意するか、上記のを[M,N]追加してください。->これを次のように呼び出すことを考えて+ 1.randZeroToOnerandMToN(0.0, 1.0);
ください。– BeyelerStudios

1
でのゼロによる除算にも注意してください(N-M)。このエラーに対処するための良い方法はここに発見された:stackoverflow.com/questions/33058848/...
博士ベコ

61

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オブジェクトを渡す方がよいでしょう。


1
uniform_realは、ハーフオープン間隔[min、max)を使用します。つまり、最小値を取得しますが、最大値に到達することはありません。これは考慮すべきことですが、何らかの方法で丸めれば、この問題を回避できます。
TehWan

20
これは現在C ++ 11の一部です。
Tomas Andrle

@Wolfは実際のアプリケーションでは、特定の浮動小数点値にヒットする確率が非常に低いため、エンドポイントが含まれているか除外されているかは問題ではありません。必要でmaxあるが制限のない制限を使用できる場合はmin、間隔を簡単に逆にすることができますreturn min + max - gen();
Mark Ransom 2015年

26

最近で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

std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1技術的に正しくありません。1.0は 生成されません。en.cppreference.comTo create a distribution over the closed interval [a,b], std::nextafter(b, std::numeric_limits<RealType>::max()) may be used as the second parameter.
w /

1
これは受け入れられる答えであるはずです、それは2020
アレックス

25

2つのfloat値でコードを呼び出します。コードは任意の範囲で機能します。

float rand_FloatRange(float a, float b)
{
    return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}

これは、C99またはC ++ 11でのfmaf()(またはfma()C ++でのfloat オーバーロード)の潜在的な使用例であり、より精度を維持できることに言及する価値があります。のようにfmaf((float)rand() / RAND_MAX, b - a, a)
TimČas2017

22

CではなくC ++を使用している場合は、テクニカルレポート1(TR1)およびC ++ 0xドラフトで、ヘッダーファイルに乱数ジェネレーターの機能が追加されていることを思い出してください。これはBoostと同じだと思います。ランダムライブラリであり、Cライブラリ関数randよりも確実に柔軟で「モダン」です。

この構文は、ジェネレーター(メルセンヌツイスター 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;

2
これは現在C ++ 11にあり、distは最小値と最大値で初期化できます。
エティエンヌ

初期化子に最小値と最大値を入れて、値を取得するときにジェネレーターを提供するのは私には奇妙に思えます。逆の場合は、まあ。
ヨーヨー

6

一部のシステム(VCを搭載したWindowsが現在のところ頭に浮かびます)では、RAND_MAX途方もなく小さいです。e。15ビットのみ。で除算するRAND_MAXと、23ビットではなく15ビットの仮数のみが生成されます。これは問題となる場合と問題にならない場合がありますが、その場合はいくつかの値が欠落しています。

ああ、ちょうどその問題についてのコメントがすでにあることに気づきました。とにかく、これはあなたのためにこれを解決するかもしれないいくつかのコードです:

float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);

テストされていませんが、動作する可能性があります:-)


float r =(float)((rand()<< 9)| rand())/ RAND_MAXはどうですか?(これもテストされていません)
トラップ

ああ、申し訳ありませんが、RAND_MAXで除算しても、どこにも行けません。このトリックの要点は、RAND_MAXよりも大きいものを使用することでした...私もそれを修正しました。
ジョーイ

2
理論なしで乱数を作成する場合は注意してください... rand()の連続呼び出しは完全に独立しているとは限りません。ヒント:そのA線形合同法、連続した通話に低いビットを見れば:0と1の間、それを交互
RBerteig

知っている。ただし、一部のアプリケーションではこれで十分な場合があります。しかし、はい、この場合は、おそらく2つ以上の呼び出しを使用する必要があります。この場合、特効薬はありません。LCGであるとは限りません。他のPRNGは弱い上位ビットを持っています。ここでは、Boostソリューションが最適です。
ジョーイ

(nb:MSVCでrandによって返される下位ビットはRNG状態の最下位ビットではありません。100rand()呼び出しの場合、次の結果が得られます:1100100000111111101010010010011010101110110110111010011111100100000000010100011011000000100101100011。Javaは48ビットLCGを使用し、32ビットのみを使用します。VCは同様にそれを行うには)
Joey

4

drand48(3)POSIX標準の方法です。GLibCは、再入可能なバージョンも提供しますdrand48_r(3)

関数はSVID 3で廃止されたと宣言されましたが、適切な代替手段が提供されなかったため、IEEE Std 1003.1-2013にはまだこの関数が含まれており、すぐにどこかで実行されるというメモはありません。

Windowsでは、標準的な方法はCryptGenRandom()です。


2

これまでのところ、どの回答にも満足できなかったため、新しいランダムフロート関数を作成しました。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));
}

7
興味深いアプローチ、私はupvoteしたいけど、私は本当に何が起こっているのか理解していない
HASEN

2

私の意見では、上記の答えは「ランダムな」フロートを与えますが、それらはどれも真にランダムなフロートではありません(つまり、フロート表現の一部を欠いています)。実装に取り​​掛かる前に、フロートのANSI / IEEE標準形式を最初に見てみましょう。

|符号(1ビット)| e(8ビット)| f(23ビット)|

この単語で表される数は(-1 *記号)* 2 ^ e * 1.f

'e'の数値はバイアスがかけられた(127のバイアスの)数値なので、-127から126の範囲であることに注意してください。最も単純な(実際には最もランダムな)関数は、ランダムな整数のデータを浮動小数点数に書き込むだけです。したがって

int tmp = rand();
float f = (float)*((float*)&tmp);

これを行うfloat f = (float)rand();と、整数が浮動小数点に変換されます(したがって、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の乱数が返されます。


1
間違いがあります。MAX_RANDは通常(1 << 23)よりも低いため、rand()%frac_modは機能しません。
DanielHsH 2013

MAX_RANDの正確なサイズがわからないことを認めざるを得ません。それでもなお機能するでしょうが、おそらく役に立たないかもしれませんが、機能するでしょう。8%10 = 8で十分ですが、MAX_RANDが常に(1 << 23)より小さい場合は、実際に削除できます。
user2546926 2013

2
いいえ、あなたは少し間違っています。RandMaxは通常〜65,000です。つまり、23ビットからは15だけがランダムになります。他はおそらくゼロになります。実際に乱数を取得しますが、精度は低くなります。たとえば、ランダムジェネレーターは0.001と0.002を生成できますが、0.0017は生成できません。したがって、均一な分布になりますが、精度は低くなります(フロートより256倍精度が低くなります)。
DanielHsH 2013

この回答には2つの誤りがあります。指数範囲の修正:int e = (rand() % (max_exp - min_exp)) + min_exp_mod;仮数:int f = (int)(frac_mod * (float)rand() / RAND_MAX);上記のそれぞれの行を置き換えます。仮数の間違いは重大であることに注意してください。RAND_MAX小さい1 << 23場合は、下位ビットのみをランダム化し、常に上位ビットの0を取得します。
BeyelerStudios 2016

2

浮動小数点形式がIEEE 754(IntelやARMを含むほとんどすべての最新のCPU)であることがわかっている場合は、ビット単位の方法を使用してランダムな整数からランダムな浮動小数点数を構築できます。これは、C ++ 11にアクセスできないrandomBoost.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;
}

これは、除算を使用するよりも良い分布を与えます。


8
なぜこれが「より良いディストリビューション」をもたらすとあなたが言っているのか、私にはわかりません。実際、これはとまったく同じ分布になりreturn (float)random23 / (1 << 23)ます。(はい、私はこれをテストしました。関数を変更random32してパラメーターとして受け取り、ゼロからまでのすべての値に対して実行し(1 << 23)-1ます。そして、はい、あなたのメソッドは確かにによる除算とまったく同じ結果を与えます1 << 23。)
Ilmari Karonen

1

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);
}

1
この回答からコードをコピーして貼り付けましたか? stackoverflow.com/a/1118739/1538531
デレク

実際は違います。彼らがどれだけ似ているかを見て、私は少し驚いています!しかし、1970年1月1日、エンジンジェネレータを初期化しました。
Marco167 2013年

けっこうだ。ジェネレータをエポックに初期化したことに気づきましたが、コードは似ています!
Derek

TR1の例を示すのは少し変だと思いますが、C ++ 11とは対照的に、誰かがTR1を使用しなければならない場合について説明できますか?
Shafik Yaghmour 2014

0

rand()は0〜RAND_MAXの整数を返します。0.0と1.0の間の乱数を取得するには、最初にrand()によるintの戻り値をfloatにキャストしてから、RAND_MAXで除算します。


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までのランダムな浮動小数点です。

#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));
...
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.