108

指定された間隔 [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 プラットフォームを使用しています。一様分布で乱数を生成する他の方法はありますか?

4

18 に答える 18

184

なぜ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';

そして、これが実行例です。

テンプレート機能が役立つ場合があります:

template<typename T>
T random(T range_from, T range_to) {
    std::random_device                  rand_dev;
    std::mt19937                        generator(rand_dev());
    std::uniform_int_distribution<T>    distr(range_from, range_to);
    return distr(generator);
}

その他の乱数発生器

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

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

Boost.Random

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

于 2013-11-22T03:04:49.137 に答える
63

[編集]警告: 統計、シミュレーション、暗号化、または深刻なものには使用しないでください。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が優れており、実装が非常に簡単であることを発見しました。その特性は問題ありません。

于 2008-11-14T00:24:00.697 に答える
18

Angry Shoe と peterchen の優れた回答を、2015 年の最新技術の概要で補足したいと思います。

いくつかの良い選択

randutils

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

最小サンプル: サイコロ

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

ライブラリに興味がなくても、Web サイト ( http://www.pcg-random.org/ ) には、乱数生成全般、特に C++ ライブラリに関する興味深い記事がたくさんあります。

Boost.Random

Boost.Random (ドキュメンテーション)は、インターフェースの多くを共有する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';
}

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

  • make mt19937a thread_local: ジェネレーターは非常に充実しており (> 2 KB)、スタックに割り当てないほうがよい
  • mt19937複数の整数を持つシード: メルセンヌ ツイスターには大きな状態があり、初期化中により多くのエントロピーを利用できます

あまり良くない選択肢

C++11 ライブラリ

ライブラリは最も慣用的なソリューション<random>ですが、基本的なニーズに対してさえ、そのインターフェースの複雑さと引き換えに多くを提供するものではありません. 欠陥は次のとおりです。標準は、その出力に最小限の品質を義務付けておらず ( を返すstd::random_device限り)、2015 年現在、MinGW (最も使用されているコンパイラではありませんが、ほとんど難解な選択ではありません) は常に最小限のサンプルに出力されます。entropy()04

最小サンプル: サイコロ

#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 のソリューションと同等であり、同じ提案が適用されます。

ゴドーの解

最小サンプル: サイコロ

#include <iostream>
#include <random>

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

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

悪いほうが良い解決策

最小サンプル: サイコロ

#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 は回避されます。このプログラムは、実際には未定義の動作を引き起こします。

于 2015-12-16T16:13:15.453 に答える
10

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

int BigRand()
{
    assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
    return rand() * (RAND_MAX+1) + rand();
}
于 2008-11-13T23:44:16.533 に答える
9

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

uniform_intあなたがしたいことをするべきです。

于 2008-11-14T00:58:25.277 に答える
8

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

暗号化アルゴリズム ( AESなど) を使用して独自のものを作成することもできます。シードとIVを選択し、暗号化関数の出力を継続的に再暗号化します。OpenSSL を使用する方が簡単ですが、男性的ではありません。

于 2008-11-13T23:33:53.387 に答える
5

RAND_MAX特定のコンパイラ/環境を確認する必要があります。rand()がランダムな 16 ビット数を生成する場合、これらの結果が表示されると思います。(32ビットの数値になると想定しているようです)。

これが答えだと約束することはできませんが、 の値とRAND_MAX、環境についてもう少し詳しく投稿してください。

于 2008-11-13T23:36:47.063 に答える
3

システムにあるものを確認RAND_MAXしてください -- おそらく 16 ビットしかないと思いますが、範囲が大きすぎます。

その上で、次の議論を参照してください: Generating Random Integers within a Desired RangeとC rand() 関数の使用 (または使用しない) に関する注意事項。

于 2008-11-13T23:33:16.650 に答える
2

[low, high)これにより、範囲全体が RAND_MAX 未満である限り、float を使用せずに範囲全体に均一な分布が提供されます。

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 が行う方法です。

于 2015-06-09T16:58:28.510 に答える
2

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

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

于 2008-11-13T23:32:03.703 に答える
0

1 から 10 までの数値に対してman 3 randが与える解は次のとおりです。

j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));

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

j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));

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

于 2008-11-14T00:08:13.613 に答える
0

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

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

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

于 2008-11-13T23:38:48.993 に答える
-3

これはインターネットで見つけたばかりです。これはうまくいくはずです:

DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));
于 2008-11-13T23:56:53.060 に答える