如何在c++中生成一个随机数?

我尝试着创造一款带有骰子的游戏,我需要在其中添加一些随机数字(游戏邦注:即模拟骰子的边缘)。我知道如何在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次生成相同的随机数。是否有这样的命令?

761436 次浏览

使用模数可能会在随机数中引入偏置,这取决于随机数生成器。更多信息请看这个问题。当然,在一个随机序列中得到重复的数字是完全可能的。

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

上面不是唯一的方法,但这是一种方法。

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

srand函数的全部意义是用一个随机种子初始化伪随机数的序列

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

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

显然,当您恰好在同一秒运行应用程序时—您使用相同的种子值—因此您的结果当然是相同的(Martin York已经在对该问题的评论中提到过)。

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

修改1 -示例代码:

好吧,我明白了。 显然口头描述是不够的(可能是语言障碍或其他什么……:)) . < / p >

老式的C代码示例,基于问题中使用的相同srand()/rand()/time()函数:

#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函数作为随机种子,原因在我看来已经很明显了。这些用于教育目的,有时可以用来说明问题,但对于任何严肃的用途,它们大多是无用的。

修订2 -详细说明:

重要的是要理解,到目前为止,有没有 C或c++标准特性(库函数或类)最终产生实际上随机的数据(即由标准保证实际上是随机的)。解决这个问题的唯一标准特性是std:: random_device,不幸的是,它仍然不能保证实际的随机性。

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

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

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

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

直观地当你请求随机数时-返回的结果不应该依赖于先前返回的值,也不应该依赖于if 任何人之前要求过什么,都不应该依赖于什么时候 通过什么程序,什么电脑,什么发电机 在哪个星系被要求。这就是单词“random"的意思 毕竟-不可预测和独立于任何东西- 否则它就不是随机的了,对吧?根据这种直觉,它是正确的 很自然地在网上搜索一些魔法咒语来获得

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

而“随机数”这个有意义的概念;存在(某种程度上)——没有所谓的“伪随机数”。伪随机数发生器实际上产生伪随机数序列

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

当专家们谈论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标准中定义的逐对象式伪随机数生成器。使用相同的模板参数和相同的初始化参数,不同的对象在使用c++ 11兼容标准库构建的任何应用程序的任何计算机上将生成完全相同的每个对象输出序列。该类的优点是其可预测的高质量输出序列和跨实现的完全一致性。

此外,c++ 11标准中还定义了其他(更简单的)PRNG引擎——std:: linear_congruential_engine<比;(在一些C标准库实现中,历史上被用作质量较好的srand/rand算法)和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;
}

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

如果你正在使用提高库,你可以通过以下方式获得一个随机生成器:

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

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

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

用例场景

我把“可预测性”的问题比作一袋六块纸,每个纸上写着一个从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类是因为它可以很容易地重定向输出。

下面的代码中…

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。

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

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

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

例子:

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

每当你在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的常用用法,针对@ predictable最初提出的问题:

#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()的整体接口对于在给定范围内正确生成伪随机数不是很直观,例如,以@可预测性想要的方式生成[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库的一个非常基本的用法,关于@ predictable的原始问题:

#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来播种任何引擎。你可以使用常量甚至chrono库!你也不必使用32位版本的std::mt19937引擎,有其他选项!有关random库功能的更多信息,请参阅cplusplus.com

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

我知道如何在c++中生成随机数,而不使用任何标头,编译器intrinsic或任何东西。

#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。

以下是我的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;
}

非常固执己见的回答

c++ <random>库违背了软件工程的最佳原则之一:“简单的事情做简单,复杂的,不寻常的事情可以更复杂一点。”

相反,他们甚至把简单和常见的用例变得过于复杂,只是因为他们患有文化疾病,害怕像“这不够普遍”这样的评论。

因此,现在无论何时你想要一个简单的随机数,你都必须查看文档,阅读满是文本的堆栈,美化这个糟糕的设计,而不仅仅是一个易于记忆的一两行代码。(通用Lisp更实用:(random 5)生成从0..4和(random 1.0)产生介于0.0..1.0之间的实数。这是最常见的用例,而且触手可及。如果你需要更复杂的东西,你必须找到包和库,或者自己动手。)

只需计算一下全球范围内每个人浪费在理解标题及其内容上的时间累积的工时,就可以看到它有多糟糕。

即使我现在在浪费我的时间,写这个答案,你也在浪费时间,阅读它,只是因为他们创造了一个复杂的谜题,这与其他现代令人厌恶的东西相似,比如Vulkan API。

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