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

How to generate a random number in C++?

本文关键字:随机数 C++      更新时间:2023-10-16

我正在尝试用骰子做一个游戏,我需要在其中包含随机数(以模拟骰子的侧面。 我知道如何在 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 次生成相同的随机数。 有没有可以执行此操作的命令?

使用模可能会在随机数中引入偏差,具体取决于随机数生成器。有关详细信息,请参阅此问题。当然,完全有可能以随机顺序获得重复的数字。

尝试一些 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 随机数的更多信息,请参阅此问题/答案。以上不是执行此操作的唯一方法,但是一种方法。

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

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

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

BUT 在您的示例应用程序中,伪随机序列仅包含一个元素 - 从等于当前时间精度的种子生成的伪随机序列的第一个元素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%dn", n, n % 6 + 1 );
}
return 0;
}

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

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

修订2 - 详细说明:

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

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

实际上安全级随机数是一个单独的行业,值得单独写一篇文章。(我在我的这个答案中简要地触及了它。

在大多数情况下,伪随机数生成器就足够了 - 例如用于科学模拟或游戏。在某些情况下,甚至需要一致定义的伪随机序列 - 例如,在游戏中,您每次都可以在运行时生成相同的地图以节省安装包的大小。

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

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

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

虽然"随机数">

的有意义的概念存在(有点),但没有"伪随机数"这样的东西。伪随机数生成器实际上会产生伪随机数序列

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

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

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

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

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

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

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

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

现代 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;
}

每当您使用C++编程语言对random number generation进行基本的网络搜索时,这个问题通常是第一个弹出的!我想把我的帽子扔进戒指,希望能更好地澄清伪随机数生成的概念,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 '%'的快速算法。以下是此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()的常见用法消除了伪随机数均匀分布的可能性。
  3. 从技术上讲,std::rand()通过std::srand( ( unsigned int )std::time( nullptr ) )播种的常见方式是不正确的,因为time_t被认为是受限制的类型。因此,不能保证time_tunsigned 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 位 Mersenne Twister 引擎。(源代码中引擎的名称听起来很奇怪,因为它的名称来自其周期 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++的人!

如果您使用的是 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);
}
};
#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

可以从这里获得生成随机数的完整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

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

用例场景

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

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

算法

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

我之所以选择bool[],是因为每一项的索引已经是每张纸的价值。如果论文需要写任何其他东西,那么我会用Dictionary<string, bool>代替它。布尔值用于跟踪数字是否已被绘制。

名为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。

一个非常固执己见的答案

c++<random>库违反了软件工程的最佳原则之一:"简单的事情做简单,复杂,不常见的事情可能会更复杂一些。

相反,他们甚至使简单和常见的用例过于复杂,只是因为他们患有文化疾病,害怕诸如"这不够普遍"之类的评论。

因此,现在每当你想要一个简单的随机数时,你都必须查看文档,阅读堆积如山的文本墙,美化这个可怕的设计,而不仅仅是一个容易记住的一两行。(Common Lisp 更实用:(random 5)产生从 0..4 开始的均匀分布整数,(random 1.0)产生 0.0..1.0 之间的实数。这是最常见的用例,触手可及。如果你需要更复杂的东西,你必须找到包和库,或者自己做。

只需计算一下全球范围内每个人浪费时间了解该标题及其内容的累计工时,看看它有多糟糕。

即使我现在也在浪费时间,写这个答案,你浪费你的时间,阅读它,只是因为他们创造了一块复杂的拼图,与其他现代可憎的东西(如 Vulkan API)具有相似的精神。

那么,如何应对呢?浪费一次时间,为自己最常见的用例编写一个头文件,然后在需要时重复使用它。

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

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

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

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

例:

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

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

我知道如何在不使用任何标头、编译器内部函数或其他任何东西的情况下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("%llun", num);
}

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

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

看起来很随意。

工作原理:

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

这是我的 5 美分:

// System includes
#include <iostream>
#include <algorithm>
#include <chrono>
#include <random>
// Application includes
// Namespace
using namespace std;
// Constants
#define A_UNUSED(inVariable) (void)inVariable;

int main(int inCounter, char* inArguments[]) {
A_UNUSED(inCounter);
A_UNUSED(inArguments);
std::random_device oRandomDevice;
mt19937_64 oNumber;
std::mt19937_64::result_type oSeed;
std::mt19937_64::result_type oValue1;
std::mt19937_64::result_type oValue2;
for (int i = 0; i < 20; i++) {
oValue1 = (std::mt19937_64::result_type) std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()
).count();
oValue2 = (std::mt19937_64::result_type) std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count();
oSeed = oRandomDevice() ^ (oValue1 + oValue2);
oNumber.seed(oSeed);
cout << "oNumber: " << oNumber << "n";
cout << "oNumber.default_seed: " << oNumber.default_seed << "n";
cout << "oNumber.initialization_multiplier: " << oNumber.initialization_multiplier << "n";
cout << "oNumber.mask_bits: " << oNumber.mask_bits << "n";
cout << "oNumber.max(): " << oNumber.max() << "n";
cout << "oNumber.min(): " << oNumber.min() << "n";
cout << "oNumber.shift_size: " << oNumber.shift_size << "n";
cout << "oNumber.state_size: " << oNumber.state_size << "n";
cout << "oNumber.tempering_b: " << oNumber.tempering_b << "n";
cout << "oNumber.tempering_c: " << oNumber.tempering_c << "n";
cout << "oNumber.tempering_d: " << oNumber.tempering_d << "n";
cout << "oNumber.tempering_l: " << oNumber.tempering_l << "n";
cout << "oNumber.tempering_s: " << oNumber.tempering_s << "n";
cout << "oNumber.tempering_t: " << oNumber.tempering_t << "n";
cout << "oNumber.tempering_u: " << oNumber.tempering_u << "n";
cout << "oNumber.word_size: " << oNumber.word_size << "n";
cout << "oNumber.xor_mask: " << oNumber.xor_mask << "n";
cout << "oNumber._Max: " << oNumber._Max << "n";
cout << "oNumber._Min: " << oNumber._Min << "n";
}
cout << "Random v2" << endl;
return 0;
}

下面是一个简单的随机生成器,在 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;
}