3

在注意到 rand() 函数每次产生相同的输出 41 后,我使用 srand(time(0)) 为生成器播种。这解决了重复输出的问题,但现在它给了我不断增加的数字。(即 245、248、250、253、255、256)。由于系统时间的影响,我可以理解它正在增加,但这是否正常?

这是我的程序:

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main()
{

    int number;

    srand(time(0));

    cout << rand() % 1000;

    return 0;

}

我反复运行它而不是循环运行。多次试验的产出:285 295 305 311 325 334 344 354 355

4

6 回答 6

7

MS 的C++rand()使用最简单的随机生成器线性同余生成器

这是它的代码:

int __cdecl rand (
    void
    )
{
    _ptiddata ptd = _getptd();

    return( ((ptd->_holdrand = ptd->_holdrand * 214013L
        + 2531011L) >> 16) & 0x7fff );
}

因此,每当您为函数播种时,您只需设置第一个值(如果您快速运行程序,显然会不时增加一些单位)

现在,如果您插入rand()a 值的数学方程,x+a其中x是上次调用函数的值,并且a是自该调用以来您的时间变化,您会注意到:((x+a) * 214013 + 2531011) > > 16 = (x*214013+2531011 + a*214013) >> 16

由于您运行程序非常快。假设你的a变化在0和秒之间。5然后,当您将此数字右移 16 位时,您a*214013的最大值现在为十进制,这大约是您的新输出与以前的输出不同的程度(我说大约是因为您不能说 (x* 214013+2531011 + a*214013) >> 16 = (x*214013+2531011 >> 16) + (a*214013 >> 16) 因为进位)107006516

于 2013-06-29T23:46:54.910 回答
4

这个错误大约每周出现一次:

如果你srand()每次都在调用 rand() 之前调用,那么你根本不会得到随机数,你会得到一个时间的哈希函数。在循环外调用srand()一次,并且只调用一次,最好在程序的最开始时调用,然后rand()根据需要调用多次以获取值。

没有生成“一个随机数”这样的事情。如果您在一系列程序调用中需要随机数,您别无选择,只能在程序之外生成这些随机数。一种方法是从/dev/urandom(在 Linux 上)读取或使用CryptGenRandom(在 Windows 上)。另一种选择是使用硬件或类似random.org.

不管你有多么好的生成器——如果你为每次调用播种,你就不会得到随机数,你会得到种子值的散列函数。如果您的种子值变化得足够快,并且散列函数非常好,那可能就足够了——但它仍然根本没有使用 RNG 算法。

于 2013-06-30T07:19:33.563 回答
1

我不知道你为什么会得到这些结果,但是 C++11 中有更好的方法:

#include <random>
#include <iostream>
int main()
{
    auto rnd = std::default_random_engine(std::random_device{}());
    std::uniform_int_distribution<> dis(1, 999);
    std::cout << dis(rnd) << '\n';
}
于 2013-06-29T23:35:05.013 回答
0

我最近遇到了同样的问题,发现 Karoly Horvath 对您的原始问题的评论解决了这个问题,尽管它有点“hacky”。我看到返回值出现可预测的增加,并且在 之后rand()立即粘贴另一个之后srand(),问题就消失了。我最终得到了这个:

srand(time(NULL));
rand();
int seed = rand();

我想弄清楚为什么会发生这种情况……但与此同时,这很有效。

于 2013-10-15T18:06:05.663 回答
0

工作方式rand是您需要遵循两个规则:

  1. 您必须先播种 ( srand) 并且
  2. 您不想在每次调用rand. 您只需在整个序列之前播种一次。

所以要真正测试它,你的代码应该看起来更像这样:

int main()
{
    srand(time(0));

    // Output a sequence of 100 random numbers, each less than 1000
    for ( int i = 0; i < 100; i++ )    
        cout << rand() % 1000 << endl;

    return 0;
}

如果程序必须每次运行只输出一个随机数,并且程序每秒运行一次或更多,那么time(0)可能不是合适的种子。也许使用clock_gettime(3)which 会在几毫秒内给你更多的东西。

于 2013-06-29T23:19:55.490 回答
0

rand在运行时库中实现为线性同余生成器,其形式为:

result = (seed * result + offset) % big_number

如果我们认为是无穷大并且你在你得到big_number的时候运行你的程序t

result = t * result + offset

如果您在很短的时间后再次运行该程序,alpha您会得到

result = (t + alpha)* result + offset

如果运行时库以较小的值初始化结果,则显示的结果将以 的较小值递增alpha * result

time()用更高分辨率的计数器替换将大大改善这一点。例如在 x86 上,rdtcs指令(通常作为编译器内在函数提供)几乎可以解决问题。

一个更好的解决方案是使用非线性同余生成器来播种,rand()就像 Jesse Good 建议的那样,它也可以通过 boost 库在非 c++11 编译器上使用。

如果可能,最好坚持使用 c++11 随机生成器而不是 c rand。

于 2013-06-29T23:49:21.943 回答