如何在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
除了分布问题之外,请记住,使用随机数可能会连续多次获得相同的结果。如果保证您不会连续两次获得相同的数字,结果将不会是随机的,对吗?
cdhowie 2012年

5
是什么让您认为这些数字不是随机的?投掷真实的骰子,您很可能会得到那个结果。如果保证每次掷球之间它们是不同的,那么它并不是随机的。
mattjgalloway 2012年

2
还请阅读eternallyconfuzzledled.com/arts/jsw_art_rand.aspx,为什么使用模数运算符通常不是一个好主意。
本杰明·班尼尔

4
您的误解远远超出了评论或答案的范围。您需要独立学习伪随机数生成器,种子,选择真正随机种子的重要性以及均匀分布。
Kerrek SB 2012年

20
当您随着时间播种。这也意味着,如果您每秒运行一次以上程序,您将获得相同的编号。
马丁·约克

Answers:


79

测试应用程序最基本的问题是您先调用srand一次,然后再调用rand一次然后退出。

srand函数的全部要点是初始化伪随机数的序列使用随机种子。

这意味着,如果传递相同的值,以srand在两个不同的应用程序(使用相同srand/ rand执行),那么你会得到完全一样的序列rand()在这两个应用程序中读取值。

但是,在您的示例应用程序中,伪随机序列仅包含一个元素-从种子生成的伪随机序列的第一个元素等于当前时间 second精确。您期望在输出中看到什么?

显然,当您碰巧在同一秒上运行应用程序时-您使用了相同的种子值-因此,结果当然是相同的(正如Martin York在对该问题的评论中已经提到的那样)。

实际上,您应该呼叫srand(seed)一次,然后呼叫rand() 多次并分析该序列-它看起来应该是随机的。

编辑:

哦,我明白了。显然,口头描述是不够的(可能是语言障碍或其他... :))。

好。基于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从节目单运行顺序应该看起来是随机的。

编辑2:

使用C或C ++标准库时,重要的是要了解,到目前为止,还没有单个标准函数或类可以确定地(实际上由标准保证)产生随机数据。解决此问题的唯一标准工具是std :: random_device,遗憾的是仍无法提供实际随机性的保证。

根据应用程序的性质,您首先应该确定您是否真的需要真正的随机(不可预测)数据。最明显的情况是您确实需要真正的随机性是信息安全性-例如,生成对称密钥,非对称私钥,盐值,安全性令牌等。

但是,安全级随机数是一个单独的行业,值得单独撰写。

在大多数情况下,伪随机数生成器就足够了-例如用于科学模拟或游戏。在某些情况下,甚至需要一致定义的伪随机序列-例如,在游戏中,您可以选择在运行时生成完全相同的地图,以避免存储大量数据。

最初的问题和重复出现的相同/相似问题(甚至是许多错误的“答案”)表明,首先重要的是要区分随机数和伪随机数,并了解什么是伪随机数序列。首先,要认识到伪随机数生成器的使用方式与使用真正的随机数生成器的方式不同。

直观地,当您请求随机数时-返回的结果不应该依赖于先前返回的值,也不应该依赖于是否有人在此之前请求了什么,也不应该依赖于哪个时刻,哪个进程,哪个计算机,来自哪个生成器以及位于哪个位置。要求什么星系。那就是“随机”这个词到底意味着什么-不可预测且独立于任何事物-否则它不再是随机的,对吗?凭着这种直觉,在网络上搜索一些魔术咒语以在任何可能的情况下获得这样的随机数是很自然的。

^^^ 在涉及伪随机数生成器的所有情况下,这种直觉期望都非常错误并且有害 -尽管对于真正的随机数而言是合理的。

尽管存在“随机数”的有意义的概念-但不存在“伪随机数”之类的东西。一个伪随机数生成器实际上产生伪随机数序列

当专家谈论PRNG的质量时,他们实际上是在谈论所生成序列(及其引人注意的子序列)的统计特性。例如,如果您通过轮流使用两个高质量的PRNG来组合它们-您可能会产生不良的结果序列-尽管它们各自分别生成良好的序列(这两个良好的序列可能只是相互关联,从而导致组合不良)。

伪随机序列实际上始终是确定性的(由其算法和初始参数确定),即实际上没有随机性。

具体地说rand()/ srand(s)对函数提供了单数每进程非线程安全(!)与实现定义的算法产生的伪随机数序列。函数rand()产生范围内的值[0, RAND_MAX]

引用C11标准:

srand函数将参数用作新的伪随机数序列的种子,该序列将由后续对的调用返回rand。如果 srand随后使用相同的种子值调用,则应重复伪随机数序列。如果rand在进行任何调用之前调用了if srand,则应生成与srand首次调用when时相同的序列,其种子值为1。

许多人有理由期望这rand()会产生一系列范围0为到的半独立均匀分布的数RAND_MAX。好吧,它绝对是应该的(否则它是无用的),但是不幸的是,不仅标准不要求这样做-甚至有明确的免责声明,指出“不能保证所产生的随机序列的质量”。在某些历史案例中rand/ srand实现的质量确实很差。即使在现代实现中,它很可能足够好-但是信任被破坏并且不容易恢复。除了它的非线程安全特性外,它在多线程应用程序中的安全使用也非常棘手和有限(仍然可能-您可以从一个专用线程使用它们)。

新的类模板std :: mersenne_twister_engine <>(及其方便的typedef-std::mt19937 / std::mt19937_64具有良好的模板参数组合)提供了每个对象了C ++ 11标准中定义的伪随机数生成器。使用相同的模板参数和相同的初始化参数,不同的对象将在任何使用C ++ 11兼容标准库构建的应用程序中的任何计算机上,生成完全相同的每个对象输出序列。此类的优点是其可预测的高质量输出序列和各个实现之间的完全一致性。

另外,在C ++ 11标准中定义了更多PRNG引擎-std :: linear_congruential_engine <>srand/rand在某些C标准库实现中历史上用作公平质量算法)和std :: subtract_with_carry_engine <>。它们还生成完全定义的参数相关的每个对象输出序列。

现代化的C ++ 11示例替换了上面的过时的C代码:

#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),然后再调用rand()”,因为我已经按照您说的做了,但不能正常工作。
bashburak

2
@bashburak看来您完全错过了此答案的重点。你为什么精确削减我的报价?我从字面上回答说:“实际上,您应该一次调用srand(seed),然后多次调用rand()并分析该序列-它看起来应该是随机的。” 您是否注意到您应该在单次srand(...)调用后多次调用rand()?您在链接中提出的问题是该问题的完全重复,但有完全相同的误解。
Serge Dundich

这是一个旧答案,但是当您在Google中搜索“ C ++随机数生成”时会显示出来。对于C ++程序员来说,这是一个糟糕的建议,因为它建议您使用rand()srand()。你可以更新吗?
Yakk-Adam Nevraumont

@ Yakk-AdamNevraumont它实际上并不建议使用rand()srand()。实际上,它只是通过提供的描述来回答问题。从使用rand/ 的描述中可以明显看出,srand应该解释伪随机数生成的基本概念-就像伪随机序列及其种子的含义一样。我试图做到这些,并使用最简单和熟悉rand/ srand组合。有趣的是,其他一些答案(即使评分很高)也遭受与问题作者相同的误解。
Serge Dundich

@ Yakk-AdamNevraumont我接受了您的建议,并使用有关最新C ++附加内容的一些信息对我的答案进行了修改。虽然我认为这是一个有点题外话-但您的建议,以及一些其他的答案表明,既有良好的旧std::rand/std::srand新的C ++库的功能,如std::random_device<>性病:: mersenne_twister_engine <>和众多随机分布需要一些解释。
Serge Dundich

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随机数的更多信息,请参见此问题/答案。上面不是做到这一点的唯一方法,而是一种方法。


7
通过使用引入的偏差量%6几乎消失了。如果您正在编写要在拉斯维加斯使用的掷骰子游戏,可能会很有意义,但在几乎任何其他情况下都没有影响。
热门点击

9
HotLicks:同意,但是如果您使用的是已经支持random_devicemt19937已经使用过的C ++版本,则实际上没有理由全力以赴并使用该标准uniform_int_distribution
Quuxplusone

4
所有程序员都应该建议人们避免像瘟疫这样的模数,因为它使用除法运算,而且会花费数百个时钟周期,并且可能会使您的应用程序时序混乱和/或消耗大量电池电量。

3
rng是“范围”吗?
Christoffer '18

4
@ChristofferHjärtström:它是- [R andom ň enerator。
康沃尔斯(Cornstalks)'18年

11

如果您使用boost库,则可以通过以下方式获得随机生成器:

#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
既然我们已经将随机性作为标准的一部分,那么除非您使用的是真正的旧编译器,否则我不鼓励使用boost版本。
马丁·约克

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,rand那么为什么不使用std::time并且std::srand为了C ++样式的一致性?
Serge Dundich

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

每次生成一个不同的随机数,而不是连续六次生成相同的随机数。

用例场景

我将可预测性的问题比作一袋六比特的纸,上面写着0到5的值。每次需要新值时,都会从袋子中抽出一张纸。如果袋子是空的,则将数字放回袋子。

...据此,我可以创建各种算法。

算法

一个袋子通常是一个Collection。我选择了一个bool[](否则称为布尔数组,位平面或位图)来担任包的角色。

我选择a的原因bool[]是因为每个项目的索引已经是每张纸的价值。如果文件需要其他任何写在上面的内容,那么我会用Dictionary<string, bool>代替它。布尔值用于跟踪该数字是否已绘制。

RemainingNumberCount初始化一个称为的计数器,以5选择一个随机数时递减计数。这使我们不必每次要绘制一个新的数字时就剩下多少纸。

要选择我使用的是下一个随机值for..loop通过索引的袋子,一个计数器来扫描时,报数indexfalse称为NumberOfMoves

NumberOfMoves用于选择下一个可用号码。NumberOfMoves首先将其设置为0和之间的随机值5,因为我们可以在袋中进行0..5个可用步骤。在下一次迭代中,NumberOfMoves将设置为0和之间的随机值4,因为现在我们可以完成0..4个步骤。随着数字的使用,可用数字减少,因此我们改为使用rand() % (RemainingNumberCount + 1)计算下一个值NumberOfMoves

NumberOfMoves计数器达到零时,for..loop应如下所示:

  1. 将当前值设置为for..loop与的索引相同。
  2. 将包装袋中的所有数字设置为false
  3. 从突破for..loop

上述解决方案的代码如下:

(将以下三个块一个接一个地放入主.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随机库函数和“无法保证所产生的随机序列的质量”。
罗伯特·安德鲁杰克

3

每当您random number generation使用C ++编程语言进行基本的网络搜索时,通常都会首先出现此问题!我想大声疾呼,希望为将来的编码人员更好地阐明C ++中的伪随机数生成的概念,这些编码人员将不可避免地在网上搜索相同的问题!

基础

伪随机数生成涉及利用确定性算法的过程,该算法产生一系列数字,其性质近似类似于随机数。我说类似,因为真正的随机性是数学和计算机科学中一个难以捉摸的谜。因此,为什么用术语伪随机来更合理地纠正!

在实际使用PRNG之前,即,pseudo-random number generator必须为算法提供一个初始值(通常也称为seed)。但是,使用算法本身之前,种子只能设置一次

/// 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()对于在给定范围之间正确生成伪随机数,例如的方式,@@ 的整体界面不是很直观,例如,以@Predictability所需的方式在[1,6]之间生成数字。
  2. 由于Pigeonhole原理,通常的使用std::rand()消除了伪随机数均匀分布的可能性。
  3. std::rand()std::srand( ( unsigned int )std::time( nullptr ) )技术上来讲,播种的通用方法是不正确的,因为它time_t被视为受限类型。因此,不能保证time_t到的转换unsigned int

有关使用C的PRNG的总体问题以及如何规避它们的更多详细信息,请参阅使用rand()(C / C ++):C标准库的rand()函数的建议

标准C ++方式

自从发布ISO / IEC 14882:2011标准(即C ++ 11)random以来,该库已经脱离C ++编程语言已有一段时间了。该库配备了多个 PRNG,并且具有不同的分布类型,例如:均匀分布正态分布二项式分布等。以下源代码示例演示了random有关@Predictability原始问题的库的最基本用法:

#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

这是一个解决方案。创建一个返回随机数的函数,并将其放置在main函数之外以使其具有全局性。希望这可以帮助

#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

1
您没有修复它。问题的关键是,如果您每秒运行该程序多次,那么它将生成相同的随机值。您的代码也这样做。
HolyBlackCat

1
@HolyBlackCat我已经检查了多次运行,它正在运行。您之前添加srand(time(0))过main功能random(n, m)吗?
阿米尔·佛

1
您应该添加srand(time(0))到main函数而不是for循环或函数实现内部。
阿米尔·佛

1
我已经逐字复制了您的代码。您是否每秒运行多次
HolyBlackCat

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;
   }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.