C ++で乱数を生成する方法は?


150

私はサイコロでゲームを作ろうとしています、そしてその中に乱数を入れる必要があります(サイコロの側面をシミュレートするために。1と6の間でそれを作る方法を知っています)。使用する

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

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

プログラムを数回実行すると、次のような出力が得られるため、うまく機能しません。

6
1
1
1
1
1
2
2
2
2
5
2

したがって、5回続けて同じものではなく、毎回異なる乱数を生成するコマンドが必要です。これを行うコマンドはありますか?


54
分布の問題はさておき、乱数を使用すると、同じ結果が連続して数回得られる可能性があることに注意してください。同じ数値が2回続けて取得されないことが保証されている場合、結果は実際にはランダムではありませんか?
cdhowie 2012年

5
これらの数値はランダムではないと思いますか?実際にサイコロを投げれば、あなたはその結果を得ることができます。スローごとに異なることが保証されている場合、実際にはランダムではありません。
mattjgalloway 2012年

2
また、eternallyconfuzzled.com / arts / jsw_art_rand.aspxも読んでください。なぜ、モジュラス演算子を使用するのが適切ではないことが多いのですか。
ベンジャミンバニエ

4
コメントや回答に収まらないほど多くのことを誤解しています。疑似乱数ジェネレータ、シード、真にランダムなシードを選択することの重要性、および一様分布について、個別に学習する必要があります。
Kerrek SB、

20
時間をかけて種をまくとき。これは、プログラムを1秒間に2回以上実行した場合にも同じ数が得られることを意味します。
マーティンヨーク

回答:


79

テストアプリケーションの最も基本的な問題は、1回srand呼び出してからrand1回呼び出して終了することです。

srand関数の全体の要点は、疑似乱数のシーケンスをランダムシードで初期化することです

つまり、同じ値srand2つの異なるアプリケーションに(同じsrand/ rand実装で)渡すと、両方のアプリケーションでまったく同じrand()値のシーケンスが読み取られます。

ただし、例のアプリケーションでは、疑似ランダムシーケンスは1つの要素(現在のsecond精度の時間に等しいシードから生成された疑似ランダムシーケンスの最初の要素)のみで構成されています。次に、出力で何を期待しますか?

明らかに同じアプリケーションを同じ秒で実行した場合、同じシード値を使用しているため、結果はもちろん同じです(Martin Yorkは既に質問へのコメントで言及しています)。

実際には、srand(seed)一度呼び出してからrand() 何度も呼び出し、そのシーケンスを分析する必要があります-ランダムに見えるはずです。

編集:

わかりました。どうやら言葉による説明だけでは不十分です(おそらく言語の壁か何か... :))。

OK。srand()/rand()/time()質問で使用されたのと同じ関数に基づく昔ながらのCコードの例:

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    {
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
        { /* bad value retrieved so get next one */ }

        printf( "%d,\t%d\n", n, n % 6 + 1 );
    }

    return 0;
}

^^^ THATプログラムの単一の実行からのシーケンスがランダムに見えるようになっています。

EDIT2:

CまたはC ++標準ライブラリを使用する場合、現時点では実際にはランダムなデータを決定的に生成する単一の標準関数またはクラスがないことを理解することが重要です(標準によって保証されています)。この問題に対処する唯一の標準ツールはstd :: random_deviceですが、残念ながら実際のランダム性は保証されていません。

アプリケーションの性質に応じて、本当に本当にランダムな(予測できない)データが本当に必要かどうかを判断する必要があります。真のランダム性最も確実に必要となる注目すべきケースは、情報セキュリティです。たとえば、対称キー、非対称秘密キー、ソルト値、セキュリティトークンなどを生成します。

ただし、セキュリティグレードの乱数は、別の記事に値する別の業界です。

ほとんどの場合、疑似乱数ジェネレーターで十分です-科学シミュレーションやゲームなど。場合によっては、一貫して定義された疑似ランダムシーケンスが必要になることもあります。たとえば、ゲームでは、実行時にまったく同じマップを生成して、大量のデータを保存しないようにすることができます。

元の質問と繰り返し発生する同一/類似の質問(およびそれらに対する多くの誤った「答え」さえ)は、何よりもまず、乱数を疑似乱数から区別し、疑似乱数シーケンスが何であるかを理解することが重要であることを示しています。そもそもAND疑似乱数ジェネレータは、真の乱数ジェネレータと同じように使用されていないことを理解するために。

直感的に乱数を要求した場合-返される結果は以前に返された値に依存してはならず、誰かが以前に何かを要求したかどうかに依存したり、いつ、どのプロセス、どのコンピューター、どのジェネレーター、どのジェネレーターに依存したりしてはなりません。それが要求された銀河。それが結局のところ「ランダム」という言葉の意味です-予測不可能であり、何からも独立しています-そうでなければ、もうランダムではありませんよね?この直感では、可能なコンテキストでそのような乱数を取得するためにキャストする魔法の呪文をWebで検索するのは自然なことです。

^^^このような直感的な期待は非常に間違っており疑似乱数ジェネレータを含むすべてのケースで有害です -真の乱数には妥当ですが。

「乱数」という意味のある概念は存在しますが、「疑似乱数」というものはありません。擬似乱数ジェネレータは、実際に擬似乱数生成シーケンスを

専門家がPRNGの品質について話すとき、彼らは実際には生成されたシーケンス(およびその注目のサブシーケンス)の統計的特性について話します。たとえば、2つの高品質のPRNGを両方同時に使用して組み合わせると、結果として悪いシーケンスが生成される可能性があります-それらがそれぞれ別々に良いシーケンスを生成するにもかかわらず(これらの2つの良いシーケンスは単純に相互に関連しているため、うまく組み合わせられない可能性があります)。

疑似ランダムシーケンスは、実際には常に決定論的です(そのアルゴリズムと初期パラメーターによって事前に決定されます)。つまり、実際にはランダムではありません。

具体的にrand()/ srand(s)機能の対は、非スレッドセーフ特異ごとのプロセスを提供する(!)実装定義されたアルゴリズムを用いて生成された擬似乱数列。関数rand()は範囲内の値を生成します[0, RAND_MAX]

C11標準からの引用:

このsrand関数は、引き数を、以降のの呼び出しで返される疑似乱数の新しいシーケンスのシードとして使用しますrandsrand次に同じシード値で呼び出された場合 、一連の疑似乱数が繰り返されます。場合randへの呼び出しの前に呼び出されsrand行われている、同じシーケンスをするときのように生成されなければならないsrand最初の1のシード値と呼ばれています。

多くの人々は、それrand()0からの範囲の一連の半独立した均一に分布した数を生成することを合理的に期待していRAND_MAXます。まあそれは間違いなく(そうでなければ役に立たない)すべきですが、残念ながら標準はそれを必要としません- 「生成されたランダムシーケンスの品質に関して保証がない」と明記する明示的な免責事項さえあります。いくつかの歴史的な例ではrand/ srand実装は確かに非常に悪い品質でした。最近の実装ではそれで十分ですが、信頼は壊れており、簡単に回復することはできません。その非スレッドセーフの性質に加えて、マルチスレッドアプリケーションでの安全な使用は、トリッキーで制限されています(まだ可能です-1つの専用スレッドから使​​用するだけでもかまいません)。

新しいクラステンプレートstd :: mersenne_twister_engine <>(およびその便利なtypedefs-std::mt19937 / std::mt19937_64と適切なテンプレートパラメータの組み合わせ)は、C ++ 11標準で定義されたオブジェクトごとの疑似乱数ジェネレータを提供します。同じテンプレートパラメーターと同じ初期化パラメーターを使用すると、C ++ 11準拠の標準ライブラリで構築されたアプリケーションのコンピューターで、異なるオブジェクトがまったく同じオブジェクトごとの出力シーケンスを生成します。このクラスの利点は、予測可能な高品質の出力シーケンスと、実装全体での完全な一貫性です。

また、C ++ 11標準-std :: linear_congruential_engine <>srand/rand一部のC標準ライブラリ実装で公正品質アルゴリズムとして歴史的に使用されていました)およびstd :: subtract_with_carry_engine <>で定義されたPRNGエンジンがさらにあります。また、完全に定義されたパラメータ依存のオブジェクトごとの出力シーケンスも生成します。

上記の廃止されたCコードの現代のC ++ 11の例の置き換え:

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    {
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
        { /* bad value retrieved so get next one */ }

        std::cout << n << '\t' << n % 6 + 1 << '\n';
    }

    return 0;
}

std :: uniform_int_distribution <>を使用する以前のコードのバージョン

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution<unsigned> distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    {
        std::cout << distrib(gen) << ' ';
    }

    std::cout << '\n';
    return 0;
}

私はここのリンクで同様の質問をしましたが、まだ明確な答えはまだ見つかりませんでした。「実際にはsrand(seed)を1回呼び出してから、rand()を呼び出す必要があります」というコードをコードで示していただけませんか。
bashburak

2
@bashburakこの回答のポイントを完全に逃したようです。なぜ正確に私の見積もりをカットしたのですか?「実際には、srand(seed)を1回呼び出してから、rand()を何度も呼び出して、そのシーケンスを分析する必要があります。ランダムに見えるはずです。」srand(...)を1回呼び出した後、rand()を何度も呼び出す必要があることに気付きましたか?リンクの質問は、この質問とまったく同じであり、誤解されています。
Serge Dundich、

これは古い回答ですが、「C ++乱数生成」をググると表示されます。それはあなたが使用して助言ので、C ++プログラマのための貧弱なアドバイスであるrand()srand()。更新できますか?
Yakk-アダムNevraumont

@ Yakk-AdamNevraumont実際にrand()and を使用することはお勧めしませんsrand()。実際、提供された説明で質問に答えるだけです。(rand/ を使用するsrand)説明から明らかなように、疑似乱数生成の基本的な概念は、疑似乱数シーケンスとそのシードのまさにその意味のように説明する必要があります。私はそれを正確に行い、最もシンプルで使い慣れたrand/ srand組み合わせを使用しようとしています。面白いことに、他のいくつかの回答は、たとえ非常に大きな評価であっても、質問の作成者と同じ誤解に悩まされています。
セルジュダンディッチ

@ Yakk-AdamNevraumont私はあなたの助言を受け、最新のC ++追加に関するいくつかの情報で私の答えを修正しました。私はこれを少しずれたトピックだと考えていますが、あなたの提案と他のいくつかの回答は、std :: mersenne_twister_engine <>のstd::rand/std::srandような古き良き新機能のC ++ライブラリ機能と、多数のランダム分布の両方が何らかの説明を必要とすることを示しています。std::random_device<>
セルジュダンディッチ

214

モジュロを使用すると、乱数ジェネレータによっては、乱数にバイアスが生じる場合があります。詳細については、この質問を参照してください。もちろん、ランダムな順序で繰り返し番号を取得することは完全に可能です。

より良い配布のためにいくつかのC ++ 11機能を試してください:

#include <random>
#include <iostream>

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]

    std::cout << dist6(rng) << std::endl;
}

C ++ 11乱数の詳細については、この質問/回答を参照してください。上記はこれを行う唯一の方法ではありませんが、1つの方法です。


7
を使用することによって導入されるバイアスの量%6は非常に少ないです。ラスベガスで使用するクラップスゲームを作成している場合はおそらく重要ですが、他のほとんどのコンテキストでは重要ではありません。
Hot Licks、2014年

9
HotLicks:同意しましたがrandom_devicemt19937すでにサポートしているバージョンのC ++を使用している場合は、文字通りすべてを使用して標準を使用しない理由はありませuniform_int_distributionん。
Quuxplusone 2017

4
ディビジョンを使用し、数百のクロックサイクルを要し、アプリケーションのタイミングを台無しにしたり、バッテリーの電力を大量に消費したりする可能性があるため、すべてのプログラマは、ペストのようなモジュロを回避するように人々にアドバイスする必要があります。

3
「範囲」のrngですか?
Christoffer

4
@ChristofferHjärtström:それはのためだR andom nはアンバーグラム enerator。
Cornstalks 2018年

11

ブーストライブラリを使用している場合は、次の方法でランダムジェネレーターを取得できます。

#include <iostream>
#include <string>

// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using namespace std;
using namespace boost;

int current_time_nanoseconds(){
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_nsec;
}

int main (int argc, char* argv[]) {
    unsigned int dice_rolls = 12;
    random::mt19937 rng(current_time_nanoseconds());
    random::uniform_int_distribution<> six(1,6);

    for(unsigned int i=0; i<dice_rolls; i++){
        cout << six(rng) << endl;
    }
}

関数current_time_nanoseconds()は、シードとして使用される現在の時間をナノ秒単位で示します。


以下は、範囲内のランダムな整数と日付を取得するためのより一般的なクラスです。

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer {
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() {
        if(debug_mode==true){
            this->rng_ = random::mt19937();
        }else{
            this->rng_ = random::mt19937(current_time_nanoseconds());
        }
    };

    int current_time_nanoseconds(){
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    }

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance(){
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    }
    bool method() { return true; };

    int rand(unsigned int floor, unsigned int ceil){
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    }

    // Is not considering the millisecons
    time_duration rand_time_duration(){
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    }


    date rand_date_from_epoch_to_now(){
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    }

    date rand_date_from_epoch_to_ceil(date ceil_date){
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    }

    date rand_date_in_interval(date floor_date, date ceil_date){
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    }

    ptime rand_ptime_from_epoch_to_now(){
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    }

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    }

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    }
};

1
規格の一部としてランダムになっているので、本当に古いコンパイラを使用しているのでない限り、ブーストバージョンの使用はお勧めしません。
マーティンヨーク

9
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand(time(NULL));
    int random_number = std::rand(); // rand() return a number between ​0​ and RAND_MAX
    std::cout << random_number;
    return 0;
}

http://en.cppreference.com/w/cpp/numeric/random/rand


質問作成者のコードとの違いは何ですか?(ただし、使用しないことを除きます%6。)Cライブラリ関数のstd::randC ++ API を使用する場合は、C ++スタイルの一貫性を保つために、randなぜ使用std::timeしないのstd::srandですか?
セルジュダンディッチ

4

いっぱいになることができます Randomerここから乱数を生成するクラスコードをます!

プロジェクトのさまざまな部分で乱数が必要な場合は、別のクラスRandomerを作成して、プロジェクトrandom内のすべてのものをカプセル化できます。

そんな感じ:

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */ 

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

このようなクラスは後で便利です。

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}

このリンクを例にして、このようなRandomerクラスを使用してランダムな文字列を生成する方法を確認できます。Randomer必要に応じて使用することもできます。


すべてのRandomerオブジェクトにジェネレータを再利用したくないですか?特に、初期化を作成してその状態を維持することは比較的コストがかかるためです。
マーティンヨーク

3

6回続けて同じものではなく、毎回異なる乱数を生成します。

ユースケースシナリオ

私は予測可能性の問題を、それぞれに0から5までの値が書かれた6ビットの紙の袋にたとえました。新しい値が必要になるたびに、バッグから紙が取り出されます。バッグが空の場合、番号はバッグに戻されます。

...これから、ある種のアルゴリズムを作成できます。

アルゴリズム

バッグは通常Collectionです。私が選んだbool[](ブール配列、ビットプレーン、ビットマップとも呼ばれます)を選択して、バッグの役割を引き受けました。

私がaを選択した理由bool[]は、各アイテムのインデックスがすでに各紙の値であるためです。論文がそれらに書かれた何か他のものを必要とするなら、私はDictionary<string, bool>代わりにを使用したでしょう。ブール値は、数値がまだ描画されているかどうかを追跡するために使用されます。

呼び出されたカウンターRemainingNumberCountは初期化5され、乱数が選択されるとカウントダウンします。これにより、新しい数字を描くたびに残っている紙の枚数を数える必要がなくなります。

次のランダムな値を選択するにはfor..loop、インデックスのバッグ全体をスキャンするためにa を使用し、indexfalse呼び出されたときにカウントするためにカウンターを使用しますNumberOfMoves

NumberOfMoves次の使用可能な番号を選択するために使用されます。NumberOfMoves最初の間のランダムな値に設定される05、我々はバッグを通じて行うことができます0..5可能なステップがあるので、。次の反復でNumberOfMovesは、0との間のランダムな値に設定されています4。これは、バッグを介して実行できる0..4ステップがあるためです。数値が使用されると、使用可能な数値が減るため、代わりにを使用rand() % (RemainingNumberCount + 1)しての次の値を計算しますNumberOfMoves

ときにNumberOfMovesカウンタがゼロになる、for..loopとして次の必要があります。

  1. 現在の値をfor..loopのインデックスと同じになるように設定します。
  2. バッグのすべての数字をに設定しfalseます。
  3. からの脱出for..loop

コード

上記のソリューションのコードは次のとおりです。

(次の3つのブロックをメインの.cppファイルに次々と入れます)

#include "stdafx.h"
#include <ctime> 
#include <iostream>
#include <string>

class RandomBag {
public:
    int Value = -1;

    RandomBag() {
        ResetBag();

    }

    void NextValue() {
        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        int NumberOfMoves = rand() % (RemainingNumberCount + 1);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            if (BagOfNumbers[i] == 0) {
                NumberOfMoves--;

                if (NumberOfMoves == -1)
                {
                    Value = i;

                    BagOfNumbers[i] = 1;

                    break;

                }

            }



        if (RemainingNumberCount == 0) {
            RemainingNumberCount = 5;

            ResetBag();

        }
        else            
            RemainingNumberCount--; 

    }

    std::string ToString() {
        return std::to_string(Value);

    }

private:
    bool BagOfNumbers[6]; 

    int RemainingNumberCount;

    int NumberOfMoves;

    void ResetBag() {
        RemainingNumberCount = 5;

        NumberOfMoves = rand() % 6;

        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            BagOfNumbers[i] = 0;

    }

};

コンソールクラス

出力のリダイレクトを簡単にするため、このConsoleクラスを作成します。

以下のコードで...

Console::WriteLine("The next value is " + randomBag.ToString());

...に置き換えることができます...

std::cout << "The next value is " + randomBag.ToString() << std::endl; 

...その後Console、必要に応じてこのクラスを削除できます。

class Console {
public:
    static void WriteLine(std::string s) {
        std::cout << s << std::endl;

    }

};

主な方法

次の使用例:

int main() {
    srand((unsigned)time(0)); // Initialise random seed based on current time

    RandomBag randomBag;

    Console::WriteLine("First set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nSecond set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nThird set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nProcess complete.\n");

    system("pause");

}

出力例

プログラムを実行すると、次の出力が得られました。

First set of six...

The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1

Second set of six...

The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5

Third set of six...

The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1

Process complete.

Press any key to continue . . .

おわりに

このプログラムはVisual Studio 2017を使用して記述され、私はそれVisual C++ Windows Console Applicationを使用してプロジェクトにすることを選択しました.Net 4.6.1

ここでは特に何もしていませんので、コードは以前のバージョンのVisual Studioでも動作するはずです。


これがVS 2017の場合、標準ライブラリの最新バージョンen.cppreference.com/w/cpp/numeric/randomを使用する必要があります。現在、この例ではCランダムライブラリ関数と「生成されたランダムシーケンスの品質について保証はありません」を使用しています。
Robert Andrzejuk

3

random number generationC ++プログラミング言語で基本的なWeb検索を行う場合は常に、この質問が最初に表示されます。Webでこの同じ質問を必然的に検索する将来のコーダーのために、C ++ での疑似乱数生成の概念をより明確に理解できるように、私は帽子をかざします!

基礎

疑似乱数の生成には、プロパティが乱数に似た一連の数値を生成する決定論的アルゴリズムを利用するプロセスが含まれます。真のランダム性は数学とコンピュータサイエンスではかなりとらえどころのない謎なので、私はほぼ似ていると言います。したがって、なぜ疑似ランダムという用語はがより正確に理解されるように利用されているのはですか。

実際にPRNGを使用する前に、つまり、pseudo-random number generatorしばしばシードとも呼ばれる初期値をアルゴリズムに提供する必要があります。ただし、シードは、アルゴリズム自体を使用する前に一度 だけ設定する必要があります。

/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
  PRNG( seed ) /// Will work as expected

/// Wrong way!
for( x in rang( 0, 10 ) ):
  seed( 1234 ) /// Seed reset for ten iterations!
  PRNG( seed ) /// Output will be the same...

したがって、適切な数列が必要な場合は、PRNGに十分なシードを提供する必要があります。

オールドCウェイ

C ++が持つ下位互換性のあるCの標準ライブラリは、ヘッダーファイルにある線形合同ジェネレーターと呼ばれるものを使用しcstdlibます。このPRNGは、モジュラー演算を利用する不連続な区分的関数、つまりを使用するのが好きなクイックアルゴリズムを通じて機能しますmodulo operator '%'。以下は、@ Predictabilityによって尋ねられた元の質問に関して、このPRNGの一般的な使用法です。

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

int main( void )
{
  int low_dist  = 1;
  int high_dist = 6;
  std::srand( ( unsigned int )std::time( nullptr ) );
  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
  return 0;
}

CのPRNGの一般的な使用法には、次のような多くの問題があります。

  1. の全体的なインターフェイスはstd::rand()、特定の範囲間で擬似乱数を適切に生成するために非常に直感的ではありません。
  2. の一般的な使用法は、ピジョンホール原理により、疑似乱数が均一に分布std::rand()する可能性を排除します。ます。
  3. 制限付きタイプと見なされるため、技術的std::rand()にシードされる一般的な方法はstd::srand( ( unsigned int )std::time( nullptr ) )正しくありません。したがって、からへの変換は保証されません!time_ttime_tunsigned int

CのPRNGの使用に関する全体的な問題、およびそれらを回避する方法の詳細については、「rand()(C / C ++)の使用」を参照しください。C標準ライブラリのrand()関数に関するアドバイス

標準C ++の方法

ISO / IEC 14882:2011規格、つまりC ++ 11が公開されrandomて以来、ライブラリはしばらくの間C ++プログラミング言語の一部でした。このライブラリには、複数の PRNGと、均一分布正規分布二項分布などのさまざまな分布タイプが備わっています。次のソースコード例は、@ Predictabilityの元の質問に関するライブラリの非常に基本的な使用法を示しています。random

#include <iostream>
#include <cctype>
#include <random>

using u32    = uint_least32_t; 
using engine = std::mt19937;

int main( void )
{
  std::random_device os_seed;
  const u32 seed = os_seed();

  engine generator( seed );
  std::uniform_int_distribution< u32 > distribute( 1, 6 );

  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << distribute( generator ) << std::endl;
  return 0;
}

32ビットのメルセンヌツイスターを有するエンジン、一様分布整数値は、上記の例で使用しました。(ソースコードのエンジンの名前は2 ^ 19937-1の期間に由来するため、奇妙に聞こえます)。この例でstd::random_deviceは、オペレーティングシステムから値を取得するエンジンのシードにも使用されます(Linuxシステムを使用してstd::random_deviceいる場合は、/dev/urandom)。

エンジンのstd::random_deviceシードにはを使用する必要がないことに注意してください。定数またはライブラリを使用することもできます!また、32ビットバージョンのエンジンを使用する必要はありません。他のオプションもあります。ライブラリの機能の詳細については、cplusplus.comを参照してください。chronostd::mt19937random

全体として、C ++プログラマーはstd::rand()もう使用すべきではありません。それは悪いことではなく、現在の標準がより直接的信頼性の高いより良い代替手段を提供するためです。うまくいけば、あなたの多く、特に最近ウェブ検索をしたあなたの多くがこれが役立つと思いますgenerating random numbers in c++


2

これが解決策です。乱数を返す関数を作成し、それをメイン関数の外に置いてグローバルにします。お役に立てれば

#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
    srand((unsigned)time(0));
    int die1;
    int die2;
    for (int n=10; n>0; n--){
    die1 = rollDie();
    die2 = rollDie();
    cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
    return (rand()%6)+1;
}

2

このコードは、n〜の乱数を生成しmます。

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

例:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

2
これは実際には質問の答えにはなりません。
HolyBlackCat 2018

1
あなたはそれを修正しませんでした。問題は、プログラムを1秒間に複数回実行すると、同じランダム値が生成されることです。あなたのコードもそれを行います。
HolyBlackCat 2018

1
@HolyBlackCat複数の実行を確認しましたが、機能しています。srand(time(0))以前にmain関数に追加したことがありますrandom(n, m)か?
アミールFo

1
srand(time(0))forループまたは関数実装の内部ではなく、メイン関数に追加する必要があります。
アミールFo

1
コードをそのままコピーしました。1秒間複数回実行しましたか?
HolyBlackCat 2018

1

すべてのRUNファイルをランダムに

size_t randomGenerator(size_t min, size_t max) {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);

    return dist(rng);
}

1
ジェネレータを複数回作成することは想定されていません。(ランダムに見えるようにするために)適切な分布を持つ乱数のシーケンスを生成するように、状態の束を維持します。
マーティンヨーク

-2

これは、およその単純なランダムジェネレータです。0の周りに正の値と負の値を生成する確率は等しい:

  int getNextRandom(const size_t lim) 
  {
        int nextRand = rand() % lim;
        int nextSign = rand() % lim;
        if (nextSign < lim / 2)
            return -nextRand;
        return nextRand;
  }


   int main()
   {
        srand(time(NULL));
        int r = getNextRandom(100);
        cout << r << endl;
        return 0;
   }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.