web-dev-qa-db-ja.com

C ++から0と1の間に均一に分布するランダムなダブルをどのように生成しますか?

C++から0と1の間で均一に分布したランダムなダブルをどのように生成しますか?

もちろん、いくつかの答えを考えることはできますが、標準的なプラクティスが何であるかを知りたいのです。

  • 優れた標準準拠
  • ランダム性が良い
  • 良いスピード

(私のアプリケーションにとって、速度はランダム性よりも重要です)。

どうもありがとう!

PS:重要な場合、ターゲットプラットフォームはLinuxとWindowsです。

58
static_rtti

C++ 11およびC++ 14では、 ランダムヘッダー を使用したより優れたオプションがあります。プレゼンテーション Rand()有害と見なされる byStephan T. Lavavejは、C++でRand()の使用を避けるべき理由を説明しています。 randomヘッダと N3924:C++ 14のRand()を落胆させる により、この点がさらに強化されます。

以下の例は、cppreferenceサイトのサンプルコードの修正版であり、 std :: mersenne_twister_engine エンジンと std :: uniform_real_distribution を使用して_[0,1)_ range(ライブで見る):

_#include <iostream>
#include <iomanip>
#include <map>
#include <random>

int main()
{
    std::random_device rd;


    std::mt19937 e2(rd());

    std::uniform_real_distribution<> dist(0, 1);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::round(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 *************************
_

投稿では速度が重要であると述べたので、異なる乱数エンジンを記述するcppreferenceセクションを検討する必要があります(emphasis mine):

使用するエンジンの選択には多くのトレードオフが関係します*:**線形合同エンジンは適度に高速であり、状態のストレージ要件は非常に小さくなります。 遅延フィボナッチジェネレーターは、高度な算術命令セットを持たないプロセッサーでも非常に高速ですが、状態の保存が大きくなり、スペクトル特性が望ましくない場合があります。 Mersenneツイスターはより遅く、より大きな状態保存要件を持っていますが、適切なパラメーターを使用すると、最も望ましいスペクトル特性を持つ最長の非反復シーケンスがあります(望ましい定義を与えられた)。

したがって、より高速なジェネレータが必要な場合は、おそらく ranlux24_base または ranlux48_basemt19937 よりも優れた選択肢です。

ランド()

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) ) ) ;  
}
_
31
Shafik Yaghmour

次のような古い学校のソリューション:

double X=((double)Rand()/(double)Rand_MAX);

すべての基準(ポータブル、標準、高速)を満たす必要があります。明らかに、生成された乱数をシードする必要があり、標準的な手順は次のようなものです。

srand((unsigned)time(NULL));
55
Elemental
6
Vijay Mathew

C++ TR1 を使用していた場合の方法を次に示します。

5
John D. Cook

速度が主な関心事である場合は、単に

double r = (double)Rand() / (double)Rand_MAX;
3
suszterpatt

C++ 11標準ライブラリには、きちんとしたフレームワークといくつかのサービス可能なジェネレーターが含まれています。これは、宿題の割り当てやカフスの使用に十分です。

ただし、プロダクショングレードのコードの場合、すべてのジェネレーターには注意事項があるため、使用する前に、さまざまなジェネレーターの特定のプロパティを正確に把握する必要があります。また、それらはどれも、TestU01のようなPRNGの標準テストに合格しません。ただし、寛大な贅沢要素を使用した場合のranluxジェネレーターは例外です。

安定した再現可能な結果が必要な場合は、独自のジェネレーターを用意する必要があります。

移植性が必要な場合は、独自のジェネレーターを用意する必要があります。

制限された移植性で生活できる場合は、boost、または独自のジェネレーターと組み合わせてC++ 11フレームワークを使用できます。

詳細-優れた品質と豊富なリンクを備えたシンプルで高速なジェネレーターのコードを含む-は、同様のトピックに対する私の回答に記載されています

プロの統一浮動小数点偏差については、考慮すべき問題がさらに2つあります。

  • オープン範囲とハーフオープン範囲とクローズ範囲、つまり(0,1)、[0、1)または[0,1]
  • 整数から浮動小数点への変換方法(精度、速度)

変換の方法は0と1の包含/除外を処理するため、実際は両方が同じコインの2つの側面です。ハーフオープンインターバルの3つの異なる方法を次に示します。

_// exact values computed with bc

#define POW2_M32   2.3283064365386962890625e-010
#define POW2_M64   5.421010862427522170037264004349e-020

double random_double_a ()
{
   double lo = random_uint32() * POW2_M64;
   return lo + random_uint32() * POW2_M32;
}

double random_double_b ()
{
   return random_uint64() * POW2_M64;
}

double random_double_c ()
{
   return int64_t(random_uint64()) * POW2_M64 + 0.5;
}
_

random_uint32()およびrandom_uint64()は実際の関数のプレースホルダーであり、通常はテンプレートパラメーターとして渡されます)

メソッドaは、より低い値の過剰な精度に偏らない均一な逸脱を作成する方法を示します。 64ビットのコードは単純で、11ビットをマスクするだけなので、表示されていません。分布はすべての関数で均一ですが、このトリックがないと、他の場所よりも0に近い領域の値が異なります(ulpが変化するため、グリッド間隔が狭くなります)。

メソッドcは、FPUが符号付き64ビット整数型のみを知っている特定の一般的なプラットフォームで、均一な偏差をより速く取得する方法を示します。最もよく見られるのはメソッドbですが、そこでは、符号なしのセマンティクスを保持するために、コンパイラが内部で多くの余分なコードを生成する必要があります。

これらの原則を組み合わせて、独自のソリューションを作成してください。

これはすべて、ユルゲン・ドアニックの優れた論文で説明されています 高周期乱数の浮動小数点への変換

3
DarthGizka

最初にstdlib.hをインクルードします

#include<stdlib.h>

次に、Cプログラミング言語の範囲間でランダムな倍数を生成する関数を使用できます。

double randomDouble() {
    double lowerRange = 1.0;
    double upperRange = 10.0;
    return ((double)Rand() * (upperRange - lowerRange)) / (double)Rand_MAX + lowerRange;
}

ここで、Rand_MAXはstdlib.hで定義されています

2
Amarjit Datta

私が見るように、これには3つの方法があります。

1)簡単な方法。

_double Rand_easy(void)
{       return (double) Rand() / (Rand_MAX + 1.0);
}
_

2)安全な方法(標準準拠)。

_double Rand_safe(void)
{
        double limit = pow(2.0, DBL_MANT_Dig);
        double denom = Rand_MAX + 1.0;
        double denom_to_k = 1.0;
        double numer = 0.0;

        for ( ; denom_to_k < limit; denom_to_k *= denom )
           numer += Rand() * denom_to_k;

        double result = numer / denom_to_k;
        if (result == 1.0)
           result -= DBL_EPSILON/2;
        assert(result != 1.0);
        return result;
}
_

3)カスタム方法。

Rand()を削除することにより、特定のバージョンの特異性について心配する必要がなくなり、独自の実装に余裕ができます。

注:ここで使用される発電機の周期は≅1.8e + 19です。

_#define RANDMAX (-1ULL)
uint64_t custom_lcg(uint_fast64_t* next)
{       return *next = *next * 2862933555777941757ULL + 3037000493ULL;
}

uint_fast64_t internal_next;
void seed_fast(uint64_t seed)
{       internal_next = seed;
}

double Rand_fast(void)
{
#define SHR_BIT (64 - (DBL_MANT_Dig-1))
        union {
            double f; uint64_t i;
        } u;
        u.f = 1.0;
        u.i = u.i | (custom_lcg(&internal_next) >> SHR_BIT);
        return u.f - 1.0;
}
_

選択が何であれ、機能は次のように拡張できます。

_double Rand_dist(double min, double max)
{       return Rand_fast() * (max - min) + min;
}

double Rand_open(void)
{       return Rand_dist(DBL_EPSILON, 1.0);
}

double Rand_closed(void)
{       return Rand_dist(0.0, 1.0 + DBL_EPSILON);
}
_

最終ノート:高速バージョン-Cで記述されていますが、C++で_std::generate_canonical_の代わりとして使用するように適合させることができ、十分に重要な値を出力するジェネレーターで動作しますビット。

ほとんどの64ビットジェネレーターは、その全幅を利用するため、これを変更せずに使用できます(シフト調整)。例えばこれは、_std::mt19937_64_エンジンでそのまま機能します。

1
Johnny Cage

シンプルさと速度を主な基準と考えて、次のような小さな汎用ヘルパーを追加できます。

  // C++ Rand generates random numbers between 0 and Rand_MAX. This is quite a big range
  // Normally one would want the generated random number within a range to be really
  // useful. So the arguments have default values which can be overridden by the caller
  int nextRandomNum(int low = 0, int high = 100) const {
    int range = (high - low) + 1;
    // this modulo operation does not generate a truly uniformly distributed random number
    // in the span (since in most cases lower numbers are slightly more likely), 
    // but it is generally a good approximation for short spans. Use it if essential
    //int res = ( std::Rand() % high + low );
    int res = low + static_cast<int>( ( range * std::Rand() / ( Rand_MAX + 1.0) ) );
    return res;
  }

乱数生成はよく研究された複雑で高度なトピックです。他の回答で言及されているものとは別に、ここではいくつかのシンプルだが便利なアルゴリズムを見つけることができます:

永遠の混乱

0
Abhay