【问题标题】:How to generate a random number in C++?如何在 C++ 中生成随机数?
【发布时间】:2012-11-06 21:30:17
【问题描述】:

我正在尝试用骰子制作游戏,并且我需要在其中包含随机数(以模拟骰子的侧面。我知道如何将其设置在 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次相同的随机数。有没有这样的命令?

【问题讨论】:

  • 抛开分布问题,请记住,随机数有可能连续多次获得相同的结果。如果保证您不会连续两次获得相同的数字,那么结果就不会是随机的,不是吗?
  • 是什么让您认为这些数字不是随机的?真正地掷骰子,你很可能会得到这个结果。如果保证它们在每次投掷之间都是不同的,那么它就不会真的是随机的了。
  • 另请阅读 eternallyconfuzzled.com/arts/jsw_art_rand.aspx 为什么使用模运算符通常不是一个好主意。
  • 您的误解远远超出评论甚至答案的范围。您需要独立学习伪随机数生成器、种子、选择真正随机种子的重要性以及均匀分布。
  • 随着时间的推移播种。这也意味着,如果您每秒运行程序超过一次,您将获得相同的数字。

标签: c++ random


【解决方案1】:

你的测试应用最根本的问题是你调用srand一次,然后调用rand一次然后退出。

srand 函数的全部意义在于用随机种子初始化伪随机数序列

这意味着如果您在两个不同的应用程序中将相同的值传递给srand(具有相同的srand/rand 实现),那么您将得到完全相同在这两个应用程序中读取的rand() 值的序列

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

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

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

修正 1 - 示例代码:

好的,我明白了。 显然口头描述是不够的(可能是语言障碍之类的...... :))。

基于问题中使用的相同 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;
}

^^^ 那个程序的单次运行序列应该看起来是随机的。

请注意,我不建议在生产中使用 rand/srand 函数,原因如下,我绝对不建议使用函数 time 作为随机种子,因为 IMO 已经很明显了。这些对于教育目的很好,有时也可以说明这一点,但对于任何严肃的用途,它们大多是无用的。

修正 2 - 详细说明:

重要的是要了解,到目前为止,没有 C 或 C++ 标准功能(库函数或类)最终确定地产生实际随机数据(即由标准保证实际上是随机的)。解决此问题的唯一标准功能是 std::random_device,遗憾的是它仍然不能保证实际随机性。

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

但是,安全级随机数是一个独立的行业,值得单独写一篇文章。我正在我的this answer 中简要讨论它们。

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

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

直观地当您请求随机数时 - 返回的结果不应依赖于先前返回的值,也不应依赖于 任何人之前都要求过任何东西,不应该取决于什么时候 以及通过什么过程,在什么计算机上,从什么发生器和 在哪个星系被要求。这就是“随机”这个词的意思 毕竟——不可预测且独立于任何事物—— 否则它不再是随机的,对吧?凭着这种直觉 很自然地在网上搜索一些魔法咒语来获得 任何可能的上下文中的此类随机数。

^^^ 在涉及Pseudo-Random Number Generators 的所有情况下,这种直观的期望都是非常错误和有害的 - 尽管对于真正的随机数是合理的。

虽然“随机数”这个有意义的概念存在(有点)——但不存在“伪随机数”之类的东西。 Pseudo-Random Number Generator 实际上会产生伪随机数序列

伪随机序列实际上总是确定性(由其算法和初始参数预先确定)——也就是说,它实际上没有任何随机性。

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

特别是rand()/srand(s) 对函数提供了一个单一的每进程非线程安全(!)伪随机数序列,使用实现定义的算法生成。函数rand() 产生[0, RAND_MAX] 范围内的值。

引自 C11 标准 (ISO/IEC 9899:2011):

srand 函数使用参数作为新序列的种子 后续调用rand 将返回的伪随机数。如果 然后使用相同的种子值调用srand,序列为 伪随机数应重复。如果rand 在任何之前被调用 已调用srand,应生成相同的序列 当 srand 第一次被调用时,种子值为 1。

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

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

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

上面过时的 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;
}

【讨论】:

  • 我在这里link 提出了类似的问题,但仍然找不到任何明确的答案。你能用代码演示“实际上你应该调用 srand(seed) 一次,然后调用 rand()”吗?因为我已经按照你说的做了,但它不能正常工作。
  • @bashburak 看来您完全错过了这个答案的重点。你到底为什么剪掉我的报价?我在回答中字面意思是“实际上你应该调用 srand(seed) 一次,然后调用 rand() 很多次 并分析该序列 - 它应该看起来是随机的。”您是否注意到在单次 srand(...) 调用后应该多次调用 rand() ?您在链接中的问题与此问题完全相同,但误解完全相同。
  • 这是一个旧的答案,但是当你用谷歌搜索“C++ 随机数生成”时它就会出现。这对 C++ 程序员来说是个糟糕的建议,因为它建议您使用 rand()srand()。可以更新一下吗?
  • @Yakk-AdamNevraumont 实际上并不建议使用rand()srand()。事实上,它只是用提供的描述回答了这个问题。从描述(使用rand/srand)中可以明显看出,应该解释伪随机数生成的基本概念——就像伪随机序列及其种子的含义一样。我正在尝试做到这一点并使用最简单和熟悉的rand/srand 组合。有趣的是,其他一些答案——即使评分很高——也与问题的作者有同样的误解。
  • @Yakk-AdamNevraumont 我听取了您的建议,并用一些有关最新 C++ 添加的信息修改了我的答案。虽然我认为这有点离题 - 但您的建议以及其他一些答案表明,良好的旧 std::rand/std::srand 和新的 C++ 库功能,如 std::random_device&lt;&gt;std::mersenne_twister_engine<> 和大量 random distributions 都需要一些解释。
【解决方案2】:

我知道如何在 C++ 中生成随机数,而无需使用任何头文件、编译器内部函数或其他任何东西。

#include <cstdio> // Just for printf
int main() {
    auto val = new char[0x10000];
    auto num = reinterpret_cast<unsigned long long>(val);
    delete[] val;
    num = num / 0x1000 % 10;
    printf("%llu\n", num);
}

运行一段时间后我得到了以下统计数据:

0: 5268
1: 5284
2: 5279
3: 5242
4: 5191
5: 5135
6: 5183
7: 5236
8: 5372
9: 5343

看起来很随意。

它是如何工作的:

  • 现代编译器使用ASLR(地址空间布局随机化)保护您免受缓冲区溢出。
  • 因此您可以在不使用任何库的情况下生成一些随机数,但这只是为了好玩。不要那样使用 ASLR。

【讨论】:

    【解决方案3】:

    每当您在 C++ 编程语言中对random number generation 进行基本的网络搜索时,通常最先出现这个问题!我想把我的帽子扔进戒指,希望能更好地阐明 C++ 中伪随机数生成的概念,以供将来不可避免地在网络上搜索相同问题的程序员使用!

    基础知识

    伪随机数生成涉及利用deterministic algorithm 的过程,该deterministic algorithm 生成其属性近似于随机数 的数字序列。我说近似相似,因为真正的随机性在数学和计算机科学中是相当 elusive mystery 的。因此,为什么使用 pseudo-random 一词更准确!

    在您实际使用 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 头文件中的 linear congruential generator!此 PRNG 通过使用模运算的不连续分段函数(即喜欢使用 modulo operator '%' 的快速算法)发挥作用。以下是此 PRNG 的常见用法,关于 @Predictability 提出的原始问题:

    #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. std::rand() 的常见用法消除了伪随机数均匀分布的可能性,因为Pigeonhole Principle
    3. std::rand() 通过std::srand( ( unsigned int )std::time( nullptr ) ) 播种的常见方式在技术上是不正确的,因为time_t 被认为是restricted type。因此,从time_tunsigned int 的转换无法保证!

    有关使用 C 的 PRNG 的整体问题以及如何规避这些问题的更多详细信息,请参阅Using rand() (C/C++): Advice for the C standard library’s rand() function

    标准 C++ 方式

    自从 ISO/IEC 14882:2011 标准(即 C++11)发布以来,random 库已经成为 C++ 编程语言的一部分。该库配备了多个 PRNG,以及不同的分发类型,例如:uniform distributionnormal distributionbinomial distribution 等。以下源代码示例演示了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 位 Mersenne Twister 引擎,均匀分布整数值。 (源代码中引擎的名称听起来很奇怪,因为它的名称来自它的 period 的 2^19937-1 )。该示例还使用std::random_device 为引擎播种,该引擎从操作系统获取其值(如果您使用的是Linux 系统,则std::random_device/dev/urandom 返回一个值)。

    请注意,您不必使用std::random_device 来播种任何引擎。您可以使用 constants 甚至 chrono 库!您也不必使用 32 位版本的 std::mt19937 引擎,还有其他选项!关于random库的更多功能,请参考cplusplus.com

    总而言之,C++ 程序员不应该再使用std::rand(),不是因为它不好,而是因为当前标准提供了更好的替代方案,更直截了当可靠。希望对你们中的许多人有所帮助,尤其是那些最近在网上搜索过generating random numbers in c++ 的人!

    【讨论】:

    • 我注意到了这个link,现在是你的帖子。我想,至少提出的一些观点不再有效?你也可以看看那里,看看是不是这样?
    • 你好@Rika,我查看了这个问题,我确实看到那里提出的观点是有效的。虽然需要更多的阐述。我可能需要一段时间才能提供正确的答案,因为 RNG 播种在 C++ 中很复杂。也许在这个答案中添加一个新的部分来描述 C++ PRNG 播种陷阱是有益的。
    【解决方案4】:

    此代码生成从nm 的随机数。

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

    示例:

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

    【讨论】:

    • 这并不能真正回答问题。
    • 你没有修复它。问题的关键在于,如果您每秒多次运行该程序,那么它会生成相同的随机值。您的代码也是如此。
    • @HolyBlackCat 我已经检查了多次运行,它正在工作。你有没有在random(n, m)之前添加srand(time(0))到main函数中?
    • 你应该将srand(time(0))添加到主函数而不是for循环或函数实现中。
    • 我已经逐字复制了你的代码。您是否每秒运行多次
    【解决方案5】:

    使用取模可能会在随机数中引入偏差,具体取决于随机数生成器。 See this question for more info. 当然,完全有可能得到随机序列中的重复数字。

    尝试一些 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;
    }
    

    See this question/answer for more info on C++11 random numbers.以上不是唯一的方法,而是一种方法。

    【讨论】:

    • 使用%6 引入的偏差量非常小。如果您正在编写一个在拉斯维加斯使用的掷骰子游戏,这可能很重要,但在几乎任何其他情况下都无关紧要。
    • HotLicks:同意,但如果您使用的 C++ 版本已经支持 random_devicemt19937,那么实际上没有理由全力以赴并使用标准的uniform_int_distribution
    • 所有程序员都应该建议人们避免像瘟疫那样使用模数,因为它使用除法并且会花费数百个时钟周期,并且可能会打乱您的应用程序时序和/或消耗大量电池电量。
    • rng 代表“范围”吗?
    • @ChristofferHjärtström:它适用于 random number generator。
    【解决方案6】:

    Can get full Randomer class code for generating random numbers from here!

    如果您在项目的不同部分需要随机数,您可以创建一个单独的类 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";
    }
    

    您可以检查this link as an example how i use 这样的Randomer 类来生成随机字符串。如果您愿意,也可以使用Randomer

    【讨论】:

    • 您不想为所有 Randomer 对象重复使用生成器吗?特别是因为创建初始化和维护其状态相对昂贵。
    【解决方案7】:

    这是一个简单的随机生成器,大约。 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;
       }
    

    【讨论】:

      【解决方案8】:

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

      用例场景

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

      ...由此,我可以创建一种算法。

      算法

      包通常是Collection。我选择了一个bool[](也称为布尔数组、位平面或位图)来充当包的角色。

      之所以选择bool[],是因为每一项的索引已经是每张纸的价值。如果文件需要写在上面的任何其他内容,那么我会使用Dictionary&lt;string, bool&gt; 代替它。布尔值用于跟踪数字是否已被绘制。

      一个名为RemainingNumberCount 的计数器被初始化为5,它在选择随机数时倒计时。这样我们就不必在每次想画一个新数字时计算还剩多少张纸。

      为了选择下一个随机值,我使用for..loop 来扫描索引包,并在indexfalse 时使用计数器来计数,称为NumberOfMoves

      NumberOfMoves 用于选择下一个可用号码。 NumberOfMoves 首先设置为05 之间的随机值,因为我们可以通过袋子进行 0..5 个可用步骤。在下一次迭代中,NumberOfMoves 设置为04 之间的随机值,因为现在我们可以通过袋子进行 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::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 编写的,我选择使用 .Net 4.6.1 将其设为 Visual C++ Windows Console Application 项目。

      我在这里没有做任何特别的事情,所以代码应该也可以在早期版本的 Visual Studio 上运行。

      【讨论】:

      • 如果这是 VS 2017,您应该使用最新版本的标准库:en.cppreference.com/w/cpp/numeric/random。目前,此示例使用 C 随机库函数,并且“无法保证生成的随机序列的质量”。
      【解决方案9】:

      随机每个 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);
      }
      

      【讨论】:

      • 您不应该多次创建生成器。它维护一堆状态,以便生成具有适当分布的随机数序列(使其看起来随机)。
      【解决方案10】:

      如果你使用boost libs,你可以通过这种方式获得一个随机生成器:

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

      【讨论】:

      • 既然我们将随机作为标准的一部分,我不鼓励使用 boost 版本,除非您使用的是真正的旧编译器。
      【解决方案11】:
      #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。)如果您决定使用rand C 库函数的std::rand C++ API,那么为什么不使用std::timestd::srand 以保持C++ 风格的一致性?
      【解决方案12】:

      这里有一个解决方案。创建一个返回随机数的函数并将其放置 在 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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-23
        • 2019-05-15
        • 1970-01-01
        • 2011-12-16
        相关资源
        最近更新 更多