web-dev-qa-db-ja.com

乱数の乱数生成

C++でランダムフロートを生成する方法

整数Randを取り、それを何かで割ることができると思いましたが、それで十分でしょうか。

246
hasen

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()を呼び出して乱数ジェネレータを "シード"しなければなりません。これはRand()を呼び出すたびにではなく、プログラムの実行中に一度行われるべきです。これはしばしば次のように行われます。

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

Randまたはsrandを呼び出すには、#include <cstdlib>が必要です。

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

346
John Dibling

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_distribution2の値を使用することにした場合stddevdist(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) ) ) ;  
}
119
Shafik Yaghmour

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オブジェクトを渡すほうが良いかもしれませんが、うまくいけば、あなたはアイデアを得ます。

60
rlbond

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

float Rand_FloatRange(float a, float b)
{
    return ((b - a) * ((float)Rand() / Rand_MAX)) + a;
}
23
Ivan Prodanov

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;
19
Rick

現代の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);
}

floatstd::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
19
Andreas DM

いくつかのシステム(VCを持つWindowsは、現在頭に浮かぶ)で、Rand_MAXはばかげて小さいです。 e。わずか15ビット。 Rand_MAXで割ると、23ビットの代わりに15ビットの仮数しか生成されません。これはあなたにとって問題であるかもしれないしそうでないかもしれませんが、その場合いくつかの値を逃しています。

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

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

テストされていませんが、うまくいくかもしれません:-)

5
Joey

drand48(3) はPOSIXの標準的な方法です。 GLibCはリエントラント版 drand48_r(3) も提供します。

この関数はSVID 3で廃止されたと宣言されましたが、適切な代替手段は提供されていなかったので IEEE Std 1003.1-2013 にはまだ含まれてまもなく。

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

4
ivan_pozdeev

あなたの浮動小数点フォーマットが 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;
}

これは、除算を使用したものよりも良い分布になります。

2
Mark Ransom

私は今のところ答えのどれにも満足していなかったので、私は新しいランダムな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));
}
2
iNFiNiTyLoOp

私の意見では、上記の答えは「ランダムな」フロートを与えますが、それらのどれもが真にランダムなフロートではありません(つまり、フロート表現の一部を見逃しています)。私の実装に突入する前に、まずフロートの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の間の乱数が返されます。

2
user2546926

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
Marco167
#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));
...
0
Mike Mestnik

Rand()は、0からRand_MAXまでの整数を返します。 0.0と1.0の間の乱数を取得するには、最初にRand()によって返されたintをfloatにキャストし、次にRand_MAXで除算します。

0
James Curran