C++可预测Rand()输出

C++ Predictable Rand() Output

本文关键字:输出 Rand C++      更新时间:2023-10-16

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

这是我的程序:

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
    int number;
    srand(time(0));
    cout << rand() % 1000;
    return 0;
}

我在重复运行,而不是循环运行。多项试验的结果:285295305311325334344354355

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

这是它的代码:

int __cdecl rand (
    void
    )
{
    _ptiddata ptd = _getptd();
    return( ((ptd->_holdrand = ptd->_holdrand * 214013L
        + 2531011L) >> 16) & 0x7fff );
}

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

现在,如果你在rand()的数学方程中插入一个值x+a,其中x是上次调用函数的值,a是调用后时间的变化,你会注意到:((x+a)*214013+2531011)>>16=(x*214013+531011+a*214013)>>16

因为你的程序运行得很快。你的a05秒之间变化。那么你的a*214013现在有一个最大值1070065,当你把这个数字右移16位时,你得到的是十进制的16,这是大约你的新输出与上一个输出的差异有多大(我说大约是因为你不能说(x*214013+2531011+a*214013>>16=(x*2140013+2531013>>16)+(a*214012>>16,因为进位)

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

如果每次在调用rand()之前都调用srand(),那么您根本不会得到随机数,而是得到了一个时间的散列函数。在循环外调用srand()一次,AND ONLY一次,最好是在程序刚开始时,然后根据需要多次调用rand()以获取值。

不存在生成"一个随机数"这回事。如果在一系列程序调用中需要随机数,那么您别无选择,只能在程序之外生成这些随机数。一种方法是读取/dev/urandom(在Linux上)或使用CryptGenRandom(在Windows上)。另一种选择是使用硬件或类似random.org的服务。

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

我不知道为什么你会得到这些结果,但在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';
}

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。

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),它会在几毫秒内给你更多的东西。

我最近遇到了同样的问题,发现Karoly Horvath对你最初问题的评论解决了这个问题,尽管它有点"黑客"。我看到返回值有一个可预测的增长,在srand()之后立即粘贴另一个rand()后,问题就消失了。我最终得到了这个:

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

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