2

我正在尝试生成一个随机数 0 - 59,并且对 C 中的 rand() 函数不满意。这是我正在玩的代码:

#include <stdlib.h>
#include <time.h>

main()
{

int num;
srand(time(NULL));
num = rand();
num = num % 59;
printf("%d\n", num);
}

我重复了这段代码的运行,并注意到生成的随机数看起来并不那么随机。生成的数字肯定遵循一个模式,因为每次我运行程序时,数字都会逐渐变大,直到它回到开头(即 2、17、21、29、38、47、54、59、4、 11....等)。

有没有一种方法可以为函数播种,这样每次我重新运行函数时,我都会得到一个真正的随机数,有 1/60 的机会生成?或者有什么替代方法我可以自己实现而不是使用 C 中的 rand() 函数?

4

3 回答 3

7

有没有一种方法可以为函数播种,这样每次我重新运行函数时,我都会得到一个真正的随机数

不,C 标准库使用 PRNG(伪随机数生成器)。你永远不会得到真正的随机数。

但是,您可以使用比 POSIX 更频繁地更改的内容来播种它time(),例如,在 POSIX 上:

struct timeval tm;
gettimeofday(&tm, NULL);
srandom(tm.tv_sec + tm.tv_usec * 1000000ul);

此外,使用模运算符生成随机数也不是一个好的解决方案(它会严重降低熵)。如果您有 BSD 风格的 libc 实现,请使用

uint32_t n = arc4random_uniform(60);

或者,如果您没有此功能:

// random() is guaranteed to return a number in the range [0 ... 2 ** 31)
#define MAX_RANDOM ((1 << 31) - 1)

long n;

do {
    n = random();
} while (n > (MAX_RANDOM - ((MAX_RANDOM % 60) + 1) % 60));
n %= 60;

请注意random()- 它的使用优于rand()(具有并且具有许多低质量实现)。这个函数可以使用srandom().

或者有什么替代方法我可以自己实现而不是使用 C 中的 rand() 函数?

您可以(当然,否则 C 库实现的作者将如何做到这一点?),但最好不要 - 可以这么说,编写一个好的 PRNG 是一门独立的科学。

于 2013-08-15T14:10:15.763 回答
1

按照你的程序编写方式,你必须每次重新运行它以获得一个新的随机数,这也意味着它每次都会重新播种。重新播种 PRNG 是不好的

您想播种一次,然后生成一堆随机数。

这样做:

int main(void)
{
    int num, i;
    srand(time(NULL));  // Seed ONCE

    for(i=0; i<100; ++i) // Loop 100 times for random numbers
    {
        num = rand();
        num = num % 59;
        printf("%d\n", num);
    }
}

现在你应该得到更好的结果。

于 2013-08-15T14:47:35.790 回答
0

每次重新运行程序时,您都使用 重新播种time(),并且该功能每秒只前进一次(如果您重新运行程序足够快,您将获得相同的结果)。

它似乎一直在增加直到它翻转的事实表明第一次调用rand()正在返回未修改的种子——该数字每秒增加一次。在这种情况下,您将获得与运行相同的结果(或非常相似的结果):

printf("%d\n", time(NULL) % 59);

我相信你能看出这有什么问题。

在这种情况下,如果你使用 'more correct' rand() * 59 / RAND_MAX,这意味着更喜欢来自 'more random' 位的值,你会遇到更糟糕的情况——结果可能在 500 秒内根本不会改变,或者更多。

从根本上说,您需要找到一种不太可预测的种子,但您可能还希望在使用之前查看它是否已正确混合。

阅读 from/dev/urandom应该提供一个好的种子,在这种情况下你不需要担心混合,但否则调用rand()几次应该有助于消除你开始使用的低质量种子的特别明显的伪影(除了,当然,它每秒只更改一次的问题)。

于 2013-08-15T15:01:10.763 回答