C ++:在main()之外播种随机数生成器

时间:2010-12-15 21:37:26

标签: c++ random

我正在创建一个简单的程序来模拟我班级的抛硬币。 (实际上,课程已超过这个学期,我只是在完成其他不需要的项目)。它涉及创建和调用一个生成1到2之间随机数的函数。最初,我试图在将要使用它的函数中使用随机数生成器(coinToss);但是,它没有产生随机数。每次运行程序时都会使用相同的数字,就像我只使用

一样

rand()

而不是

unsigned seed = time(0);
srand(seed);
rand();

然而,当我在

中移动上述内容时
int main()

它运作良好。

我的问题是1)为什么在调用它的函数中进行设置时它不起作用?(2)rand()如何 有权访问srand()所做的事情,如果它们不同时出现在同一个函数中? 显然,我是初学者,如果我没有正确地提出问题,请原谅我。另外,我的书只是简单地触及了rand()srand(),所以这就是我真正知道的。
谢谢你的帮助!

相关代码:

首次尝试不起作用:

int main()
{  
    //...........
    coinToss();
    //...........
 }

 int coinToss()
 {
    unsigned seed = time(0);
    srand(seed);

    return 1 + rand() % 2;
  }

第二次尝试确实有效:

int main()
{
    unsigned seed = time(0);
    srand(seed);

    coinToss();
 }

 int coinToss()
 { 
    return 1 + rand() % 2;
  }

5 个答案:

答案 0 :(得分:9)

您可能只希望为随机数生成器播种一次。 rand()从其内部生成器返回下一个伪随机数。每次拨打rand(),您都会从内部发电机获得下一个号码。

然而,

srand()设置随机数生成器的初始条件。您可以将其视为设置内部随机数生成器的“起始点”(实际上它比这更复杂,但它是一个有用的认知模型)。

所以,你应该在应用程序中调用srand(time(0))一次 - 在某个地方接近开头。之后,您可以根据需要多次拨打rand()

<强>然而

要回答您的实际问题 - 第一个版本不起作用,因为time()返回自纪元以来的秒数。所以,如果你在一秒钟内多次调用coinToss()(例如,如果你想模拟100次硬币投掷),那么你就会不断地为随机数发生器播种相同的数字,从而重置它的内部状态(和因此,每次你得到的下一个数字。

无论如何 - 使用time()作为srand()的种子有点蹩脚 - time()不会经常变化,更糟糕的是,可预测。如果您知道当前时间,则可以计算rand()将返回的内容。互联网上有许多更好的srand()种子的例子。

答案 1 :(得分:3)

伪随机数生成器(如rand)通过获取单个起始编号(种子)并在每次请求新编号时对其执行数字转换来工作。您只想将生成器播种一次,否则它将不断重置,这不是您想要的。

正如您所发现的,您只需在srand中拨打main一次即可。还要注意,许多rand实现在低4位左右具有相当短的周期。在实践中,这意味着您可能会获得一个容易预测的重复循环数字您可能希望在获取rand之前将% 2的返回值右移4-8位。

编辑:电话会看起来像: return 1 + (rand() >> 6) % 2;

答案 2 :(得分:1)

每个程序仅种子,而不是每次调用coinToss()

答案 3 :(得分:0)

扩展Mark B的答案:随机数生成器重置并不是因为它设置了一个用于计算随机数的新变量。但是你的程序在调用srand之间没有那么多工作。因此,每次调用srand(time(0))时,它都使用相同的种子,因此您将重置随机数生成器的内部状态。如果你把睡眠放在那里以便时间(0)改变,你每次都不会得到相同的数字。

至于数据如何从srand传递到rand,它非常简单,使用全局变量。所有以下划线和大写字母或两个下划线开头的名称都保留给编译器使用的变量。很可能这个变量已被声明为静态,因此它在翻译单元之外是不可见的(也就是包含编译器标准库的库文件。)这样做是为了使#define STUFF 5不会破坏您的标准库。

答案 4 :(得分:0)

对于简单的模拟,在模拟过程中根本不能更改种子。在这种情况下,你的模拟会“更糟”。

要理解这一点,你应该把伪随机序列视为一大块财富。当您更改种子时,就像更改位置一样,然后,每次调用rand都会为您提供不同的编号。如果再次滚动,则更有可能发现自己重复数字。