如何在C++中生成随机数?

Pre*_*ity 113 c++ random

我正在尝试用骰子制作游戏,我需要在其中有随机数字(模拟模具的两侧.我知道如何在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"; 
}
Run Code Online (Sandbox Code Playgroud)

效果不好,因为当我运行程序几次时,这是我得到的输出:

6
1
1
1
1
1
2
2
2
2
5
2
Run Code Online (Sandbox Code Playgroud)

所以我想要一个每次都会产生不同随机数的命令,而不是连续5次产生不同的随机数.有没有命令可以做到这一点?

Cor*_*lks 165

使用模可以将偏差引入随机数,这取决于随机数发生器.有关详细信息,请参阅此问题.当然,完全有可能以随机顺序重复数字.

尝试一些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;
}
Run Code Online (Sandbox Code Playgroud)

有关C++ 11随机数的更多信息,请参阅此问题/答案.以上不是唯一的方法,但这是一种方式.

  • HotLicks:同意了,但是如果你使用的是一个支持`random_device`和`mt19937`的C++版本,那么就没有理由*不*全力以赴并使用标准的`uniform_int_distribution`. (6认同)
  • rng 是“范围”吗? (6认同)
  • 使用'%6`引入的偏差量非常小.如果你正在编写一个在拉斯维加斯使用的掷骰子游戏,这可能很重要,但在几乎任何其他环境中都没有任何影响. (4认同)
  • 所有程序员都应该建议人们避免像瘟疫这样的模数,因为它使用了分区,并且需要花费数百个时钟周期,并且会使应用程序的时序混乱和/或耗费大量的电池电量. (3认同)
  • @ChristofferHjärtström:这是给* r **随机* n ** ** g **生成器的。 (3认同)
  • @user2356685 不同意。`uniform_int_distribution` 在底层使用除法,它比单个模数复杂得多。硬性规定往往无法考虑替代方案。 (3认同)

Ser*_*ich 57

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

整个srand功能点是用随机种子初始化伪随机数序列.这意味着如果您将相同的值传递给srand两个不同的应用程序(具有相同srand/ rand实现),那么您将获得完全相同的rand()值序列.但是你的伪随机序列只包含一个元素 - 你的输出由不同的伪随机序列的第一个元素组成,这些元素以1秒精度的时间播种.那你期望看到什么?当您碰巧在同一秒运行应用程序时,您的结果当然是相同的(正如Martin York在答案评论中已提到的那样).

实际上你应该拨打srand(seed)一次,然后rand()多次打电话并分析这个序列 - 它应该是随机的.

  • @bashburak看来您完全错过了此答案的重点。你为什么精确削减我的报价?我从字面上回答说:“实际上,您应该一次调用srand(seed),然后*多次*调用rand()并分析该顺序-它看起来应该是随机的。” 您是否注意到在单次调用srand(...)之后应多次调用rand()?您在链接中提出的问题是该问题的完全重复,但有完全相同的误解。 (2认同)
  • @Yakk-AdamNevraumont 实际上并不建议使用 `rand()` 和 `srand()`。事实上,它只是通过提供的描述回答了问题。从描述(使用“rand”/“srand”)可以明显看出,应该解释伪随机数生成的基本概念 - 就像伪随机序列及其种子的含义一样。我正在尝试做到这一点并使用最简单和熟悉的“rand”/“srand”组合。有趣的是,其他一些答案 - 即使评分很高 - 也遭受与问题作者相同的误解。 (2认同)
  • @Yakk-AdamNevraumont 我采纳了你的建议,并用一些有关最新 C++ 添加的信息修改了我的答案。虽然我认为这有点偏离主题 - 但你的建议以及其他一些答案表明,旧的 `std::rand/std::srand` 和新的 C++ 库功能如 `std::random_device&lt;&gt;`、[ std::mersenne_twister_engine&lt;&gt;](https://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_engine/mersenne_twister_engine) 和大量的[随机分布](https://en.cppreference.com/w/ cpp/named_req/RandomNumberDistribution) 需要一些解释。 (2认同)

小智 12

每当您random number generation在 C++ 编程语言中进行基本的网络搜索时,这个问题通常是第一个弹出的问题!我想把我的帽子扔进戒指,希望能更好地阐明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...
Run Code Online (Sandbox Code Playgroud)

因此,如果您想要一个好的数字序列,那么您必须为 PRNG 提供充足的种子!

旧 C 方式

C++ 具有的向后兼容的 C 标准库,使用了在头文件中找到的所谓的线性同余生成器cstdlib!此 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;
}
Run Code Online (Sandbox Code Playgroud)

C 的 PRNG 的常见用法包含大量问题,例如:

  1. std::rand()对于在给定范围之间正确生成伪随机数,例如,以@Predictability 想要的方式生成 [1, 6] 之间的数字,的整体界面不是很直观。
  2. 由于鸽巢原理,的常见用法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;
}
Run Code Online (Sandbox Code Playgroud)

32位梅森倍捻机发动机,具有均匀分布整数值被利用在上述的例子。(源代码中引擎的名字听起来很奇怪,因为它的名字来自它的时期2^19937-1 )。该示例还用于std::random_device为引擎设置种子,该引擎从操作系统获取其值(如果您使用的是 Linux 系统,std::random_device则从 返回一个值/dev/urandom)。

请注意,您不必使用std::random_device种子任何引擎。您可以使用常量甚至chrono库!您也不必使用 32 位版本的std::mt19937引擎,还有其他选择!有关random库功能的更多信息,请参阅cplusplus.com

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


mad*_*adx 10

如果您使用的是升压库,则可以通过以下方式获取随机生成器:

#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;
    }
}
Run Code Online (Sandbox Code Playgroud)

其中函数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);
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 现在我们已经将随机作为标准的一部分,我不鼓励使用 boost 版本,除非您使用的是真正旧的编译器。 (5认同)

Arl*_*ley 7

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 与问题作者的代码有什么区别?(除非您不使用 `%6`。)如果您决定使用 `rand` C 库函数的 `std::rand` C++ API,那么为什么不使用 `std::time` 和 `std:: srand` 为了 C++ 风格的一致性? (3认同)

Gus*_*ava 5

可以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_);
    }
};
Run Code Online (Sandbox Code Playgroud)

这样的类稍后会很方便:

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}
Run Code Online (Sandbox Code Playgroud)

您可以查看此链接作为我如何使用此类Randomer生成随机字符串的示例Randomer如果您愿意,您也可以使用。

  • 您不想为所有 Randomer 对象重复使用生成器吗?特别是因为创建、初始化和维护其状态相对昂贵。 (2认同)