web-dev-qa-db-ja.com

範囲全体で一様に乱数を生成します

指定した間隔[max; min]内で乱数を生成する必要があります。

また、乱数は特定のポイントに配置されるのではなく、間隔全体に均一に分散される必要があります。

現在、私は次のように生成しています:

for(int i=0; i<6; i++)
{
    DWORD random = Rand()%(max-min+1) + min;
}

私のテストから、乱数は1つのポイントのみで生成されます。

Example
min = 3604607;
max = 7654607;

生成された乱数:

3631594
3609293
3630000
3628441
3636376
3621404

以下の回答から:OK、Rand_MAXは32767です。私はC++ Windowsプラットフォームを使用しています。一様分布で乱数を生成する他の方法はありますか?

81
anand

Randが悪い考えである理由

ここで得た答えのほとんどは、Rand関数とモジュラス演算子を使用しています。その方法 数値を一様に生成しない可能性がありますRand_MAXの範囲と値に依存します)。したがって、推奨されません。

C++ 11と範囲にわたる生成

C++ 11では、他の複数のオプションが増えています。範囲内の乱数を生成するための要件に合うものの1つは、非常にうまく std::uniform_int_distribution です。以下に例を示します。

const int range_from  = 0;
const int range_to    = 10;
std::random_device                  Rand_dev;
std::mt19937                        generator(Rand_dev());
std::uniform_int_distribution<int>  distr(range_from, range_to);

std::cout << distr(generator) << '\n';

そして here は実行例です。

その他のランダムジェネレーター

<random>ヘッダー は、ベルヌーイ、ポアソン、正規分布を含むさまざまな種類の分布を持つ無数の他の乱数ジェネレーターを提供します。

コンテナをシャッフルするにはどうすればよいですか?

標準では std::shuffle が提供されており、次のように使用できます。

std::vector<int> vec = {4, 8, 15, 16, 23, 42};

std::random_device random_dev;
std::mt19937       generator(random_dev());

std::shuffle(vec.begin(), vec.end(), generator);

アルゴリズムは、線形の複雑さで要素をランダムに並べ替えます。

ブーストランダム

C++ 11 +コンパイラにアクセスできない場合の別の方法は、 Boost.Random を使用することです。そのインターフェイスは、C++ 11のインターフェイスに非常に似ています。

132
Shoe

[編集]警告:統計、シミュレーション、暗号化などにRand()を使用しないでください深刻です。

急いでいる典型的な人間にとっては、数字をランダムに見せるだけで十分です。

より良いオプションについては @ Jefffreyの返信 を、暗号化された乱数については この回答 をご覧ください。


一般に、上位ビットは下位ビットよりも優れた分布を示すため、単純な目的で範囲の乱数を生成する推奨方法は次のとおりです。

((double) Rand() / (Rand_MAX+1)) * (max-min+1) + min

:Rand_MAX + 1がオーバーフローしないことを確認してください(Demiに感謝)!

除算は、間隔[0、1)で乱数を生成します。これを必要な範囲に「ストレッチ」します。 max-min + 1がRand_MAXに近づいたときのみ、Mark Ransomが投稿したような「BigRand()」関数が必要です。

これにより、モジュロによるスライスの問題が回避され、数値がさらに悪化する可能性があります。


組み込みの乱数ジェネレーターは、統計シミュレーションに必要な品質を保証していません。数字が人間にとって「ランダムに見える」ことは問題ありませんが、深刻なアプリケーションでは、何かを改善する必要があります-または少なくともそのプロパティを確認する必要があります)。 Knuthには、乱数ジェネレータに関する優れた(読みにくい場合)論文があり、最近 [〜#〜] lfsr [〜#〜] が優れており、実装が非常に簡単であることがわかりました。そのプロパティはあなたに問題ありません。

58
peterchen

2015年の最新技術の簡単な概要で、Angry Shoeとpeterchenの優れた答えを補完したいと思います。

いくつかの良い選択

randutils

randutilsライブラリ (presentation) は興味深い単純さで、シンプルなインターフェイスと(宣言された)堅牢なランダム機能を提供します。それはあなたのプロジェクトへの依存を追加するという欠点があり、新しくて、広範囲にテストされていません。とにかく、無料(MITライセンス)でヘッダーのみなので、試してみる価値があると思います。

最小限のサンプル:ダイスロール

_#include <iostream>
#include "randutils.hpp"
int main() {
    randutils::mt19937_rng rng;
    std::cout << rng.uniform(1,6) << "\n";
}
_

ライブラリに興味がない場合でも、ウェブサイト( http://www.pcg-random.org/ )は、一般的な乱数生成のテーマとC++ライブラリに関する多くの興味深い記事を提供しています特に。

ブーストランダム

Boost.Random (documentation) は、C++ 11の_<random>_に影響を与えたライブラリで、多くのインターフェイスを共有しています。理論的には外部の依存関係でもありますが、Boostは今では「準標準」ライブラリの状態にあり、そのRandomモジュールは高品質の乱数生成の古典的な選択肢と見なすことができます。 C++ 11ソリューションに関して2つの利点があります。

  • より移植性が高く、C++ 03のコンパイラサポートが必要です。
  • その_random_device_は、システム固有のメソッドを使用して、高品質のシードを提供します

唯一の小さな欠点は、_random_device_を提供するモジュールがヘッダー専用ではなく、_boost_random_をコンパイルおよびリンクする必要があることです。

最小限のサンプル:ダイスロール

_#include <iostream>
#include <boost/random.hpp>
#include <boost/nondet_random.hpp>

int main() {
    boost::random::random_device                  Rand_dev;
    boost::random::mt19937                        generator(Rand_dev());
    boost::random::uniform_int_distribution<>     distr(1, 6);

    std::cout << distr(generator) << '\n';
}
_

最小限のサンプルはうまく機能しますが、実際のプログラムは次の改善点を使用する必要があります。

  • make _mt19937_ a _thread_local_:ジェネレータはかなりふっくら(> 2 KB)であり、スタックに割り当てられない方が良い
  • 複数の整数を持つシード_mt19937_:メルセンヌツイスターは大きな状態を持ち、初期化中により多くのエントロピーを活用できます。

あまり良くない選択肢

C++ 11ライブラリ

最も慣用的なソリューションである一方で、_<random>_ライブラリーは、基本的なニーズであってもそのインターフェースの複雑さと引き換えに多くを提供しません。欠陥は_std::random_device_にあります:標準は(entropy()が_0_を返す限り)出力の最低品質を要求しません。コンパイラを使用しましたが、ほとんど選択できません)、常に最小サンプルで_4_を出力します。

最小限のサンプル:ダイスロール

_#include <iostream>
#include <random>
int main() {
    std::random_device                  Rand_dev;
    std::mt19937                        generator(Rand_dev());
    std::uniform_int_distribution<int>  distr(1, 6);

    std::cout << distr(generator) << '\n';
}
_

実装が腐敗していない場合、このソリューションはBoostのものと同等であり、同じ提案が適用されます。

Godotのソリューション

最小限のサンプル:ダイスロール

_#include <iostream>
#include <random>

int main() {
    std::cout << std::randint(1,6);
}
_

これは、シンプルで効果的かつきちんとしたソリューションです。唯一の欠陥は、コンパイルに時間がかかります。C++ 17が時間通りにリリースされ、実験的なrandint関数が新しい標準に承認された場合、約2年かかります。たぶんその時までに、播種品質の保証も改善されるでしょう。

worse-is-better ソリューション

最小限のサンプル:ダイスロール

_#include <cstdlib>
#include <ctime>
#include <iostream>

    int main() {
        std::srand(std::time(nullptr));
        std::cout << (std::Rand() % 6 + 1);
    }
_

古いCソリューションは有害であると考えられており、十分な理由があります(他の回答を参照するか、 この詳細な分析 )。それでも、それには利点があります:シンプルで、移植性があり、高速で、正直であり、取得する乱数はまともではないことがわかっているため、深刻な目的で使用することはできません。

会計トロールソリューション

最小限のサンプル:ダイスロール

_#include <iostream>

int main() {
    std::cout << 9;   // http://dilbert.com/strip/2001-10-25
}
_

9は通常のダイスロールではやや珍しい結果ですが、このソリューションの優れた品質の優れた組み合わせを賞賛する必要があります。これは最速で、最もシンプルで、最もキャッシュにやさしく、ポータブルです。 9を4に置き換えることで、シンボルを含む値1、2、および3を回避しながら、あらゆる種類のダンジョンとドラゴンの死に完璧なジェネレーターを取得できます。唯一の小さな欠陥は、このプログラムは、実際には未定義の動作を引き起こします。

14
Alberto M

Rand_MAXが32767の場合、ビット数を簡単に2倍にできます。

int BigRand()
{
    assert(INT_MAX/(Rand_MAX+1) > Rand_MAX);
    return Rand() * (Rand_MAX+1) + Rand();
}
11
Mark Ransom

可能であれば、 Boost を使用します。私は彼らの ランダムライブラリ で幸運に恵まれました。

uniform_intはあなたが望むことをすべきです。

9
Jeff Thomas

速度ではなくランダム性を心配する場合は、安全な乱数生成方法を使用する必要があります。これを行うにはいくつかの方法があります... OpenSSL'sRandom Number Generator を使用するのが最も簡単な方法です。

暗号化アルゴリズム( [〜#〜] aes [〜#〜] など)を使用して独自のコードを作成することもできます。シードと [〜#〜] iv [〜#〜] を選択し、暗号化機能の出力を継続的に再暗号化します。 OpenSSLを使用する方が簡単ですが、それほど男らしくありません。

8
SoapBox

特定のコンパイラ/環境については、Rand_MAXをご覧ください。 Rand()がランダムな16ビット数を生成している場合、これらの結果が表示されると思います。 (あなたはそれが32ビット数になると仮定しているようです)。

これが答えだとは約束できませんが、Rand_MAXの価値と、環境についてもう少し詳しく説明してください。

5
abelenky

Rand_MAXがシステム上にあるかどうかを確認してください-わずか16ビットであり、範囲が大きすぎると推測しています。

さらに、この議論を参照してください: 望ましい範囲内でランダムな整数を生成する および C Rand()関数 を使用する(または使用しない)に関する注意。

3
Rob Walker

これはコードではありませんが、このロジックが役立つ場合があります。

static double rnd(void)
{
return (1.0/(Rand_MAX+1.0)*((double)(Rand())) );
}

static void InitBetterRnd(unsigned int seed)
{
register int i;
srand( seed );
for( i=0; i<POOLSIZE; i++){
pool[i]= rnd();
}
}

 static double rnd0_1(void)
 {  // This function returns a number between 0 and 1
static int i=POOLSIZE-1;
double r;

i = (int)(POOLSIZE*pool[i]);
r=pool[i];
pool[i]=rnd();
return (r);
}
2
user3503711

数値を範囲全体に均一に分布させる場合は、範囲を必要なポイントの数を表す多数の等しいセクションに分割する必要があります。次に、各セクションの最小値/最大値を持つ乱数を取得します。

別の注意として、実際に乱数を生成するのはあまり得意ではないので、おそらくRand()を使用しないでください。実行しているプラ​​ットフォームがわかりませんが、おそらくrandom()のように呼び出すことができるより良い関数があります。

2
Jason Coco

これにより、範囲[low, high)全体の範囲がRand_MAX未満である限り、フロートを使用しません。

uint32_t Rand_range_low(uint32_t low, uint32_t high)
{
    uint32_t val;
    // only for 0 < range <= Rand_MAX
    assert(low < high);
    assert(high - low <= Rand_MAX);

    uint32_t range = high-low;
    uint32_t scale = Rand_MAX/range;
    do {
        val = Rand();
    } while (val >= scale * range); // since scale is truncated, pick a new val until it's lower than scale*range
    return val/scale + low;
}

rand_MAXより大きい値の場合は、次のようなものが必要です

uint32_t Rand_range(uint32_t low, uint32_t high)
{
    assert(high>low);
    uint32_t val;
    uint32_t range = high-low;
    if (range < Rand_MAX)
        return Rand_range_low(low, high);
    uint32_t scale = range/Rand_MAX;
    do {
        val = Rand() + Rand_range(0, scale) * Rand_MAX; // scale the initial range in Rand_MAX steps, then add an offset to get a uniform interval
    } while (val >= range);
    return val + low;
}

これはおおよそ、std :: uniform_int_distributionの処理方法です。

1
benf

その性質上、乱数の小さなサンプルは均一に分布する必要はありません。結局のところ、それらはランダムです。乱数ジェネレーターが一貫してグループ化されているように見える数値を生成している場合、おそらく何か問題があることに同意します。

ただし、ランダム性は必ずしも均一ではないことに注意してください。

編集:明確にするために「小さなサンプル」を追加しました。

0
Kluge

man 3 Rand で与えられる1から10までの数の解は:

j = 1 + (int) (10.0 * (Rand() / (Rand_MAX + 1.0)));

あなたの場合、それは次のようになります:

j = min + (int) ((max-min+1) * (Rand() / (Rand_MAX + 1.0)));

もちろん、他のメッセージが指摘しているように、これは完全なランダム性や均一性ではありませんが、ほとんどの場合これで十分です。

0
calandoa

もちろん、次のコードは乱数ではなく擬似乱数を提供します。次のコードを使用してください

#define QUICK_Rand(m,n) m + ( std::Rand() % ( (n) - (m) + 1 ) )

例えば:

int myRand = QUICK_Rand(10, 20);

電話する必要があります

srand(time(0));  // Initialize random number generator.

そうしないと、番号はほぼランダムになりません。

0

@ Solution((double) Rand() / (Rand_MAX+1)) * (max-min+1) + min

警告:ストレッチングと精度エラーの可能性があることを忘れないでください(Rand_MAXが十分に大きかったとしても)、均等に分散されたものしか生成できません「ビン」および[最小、最大]のすべての数値ではありません。


@ソリューション:Bigrand

警告:これはビットを2倍にしますが、一般的にあなたの範囲内のすべての数値を生成することはできないことに注意してください。 BigRand()がその範囲内のすべての数値を生成することはtrueです。


情報:Rand()の範囲が間隔範囲を超え、Rand()が「均一」である限り、アプローチ(モジュロ)は「良好」です。 。最大で最初の最大-最小数のエラーは、1 /(Rand_MAX +1)です。

また、C++ 11の新しい ランダムパッケージ eに切り替えることをお勧めします。これは、Rand()よりも優れた多様な実装を提供します。

0
Ritualmaster