C语言 C 中的随机数生成器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18254325/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:10:31  来源:igfitidea点击:

Random Number Generator in C

crandom

提问by sammis

I'm trying to generate a random number 0 - 59, and am not satisfied with the rand() function in C. Here is the code I'm playing around with:

我正在尝试生成一个 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);
}

I've repeated the run on this code and noticed that the random numbers being generated don't really seem that random. The generated numbers produced are definitely following a pattern, as each time I run the program the number gets progressively larger until it wraps back around to the beginning (i.e. 2, 17, 21, 29, 38, 47, 54, 59, 4, 11....etc).

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

Is there a way I can seed the function so that every time I re-run the function I get a true random number with a 1/60 chance of being generated? Or are there any alternative methods I can implement myself rather than using the rand() function in C?

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

回答by

Is there a way I can seed the function so that every time I re-run the function I get a true random number

有没有办法可以为该函数设置种子,以便每次重新运行该函数时我都会得到一个真正的随机数

No, the C standard library uses a PRNG (pseudorandom number generator). You will never get truerandom numbers.

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

You can, however, seed it with something that changes more frequently than time(), for example, on POSIX:

但是,您可以使用比time()POSIX更频繁更改的内容为其播种:

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

Also, using the modulo operator for generating a random number is not a good solution (it severely decreases entropy). If you have a BSD-style libc implementation, use

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

uint32_t n = arc4random_uniform(60);

Or, if you don't have this function:

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

// 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;

Note the use of random()- it is superior to rand()(which had and has a number of low-quality implementations). This function can be seeded using srandom().

请注意使用random()- 它优于rand()(它具有并具有许多低质量的实现)。此功能可以使用srandom().

Or are there any alternative methods I can implement myself rather than using the rand() function in C?

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

You can (of course, else how would the writers of the C library implementation do it?), but you better not - it's a separate science to write a good PRNG, so to say.

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

回答by abelenky

The way your program is written, you must re-run it each time to get a new random number, which also means it gets re-seeded each time. Re-seeding a PRNG is bad.

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

You want to seed once, then generate a bunch of random numbers.

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

Do it this way:

这样做:

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

Now you should get much better results.

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

回答by sh1

Each time you re-run the program, you re-seed with time(), and that function only advances once a second (if you re-run the program quickly enough you'll get the same result).

每次您重新运行程序时,您都会使用 重新设定种子time(),并且该功能每秒只执行一次(如果您重新运行程序的速度足够快,您将获得相同的结果)。

The fact that it seems to increment until it rolls over suggests that the first call to rand()is returning the un-modified seed -- that number which increments once per second. In that case you're getting the same results (or very similar results) as if you'd run:

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

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

I'm sure you can see what's wrong with that.

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

In this case, if you used the 'more correct' rand() * 59 / RAND_MAX, which is meant to prefer a value from the 'more random' bits, you would have an even worse situation -- the results wouldn't change at all for maybe 500 seconds, or more.

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

Fundamentally you need to find a less predictable seed, but you may also want to see that it's properly mixed before you use it.

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

Reading from /dev/urandomshould provide a good seed, in which case you won't need to worry about mixing, but otherwise calling rand()a couple of times should help to do away with the especially glaring artefacts of the low-quality seed you started with (except, of course, the problem that it only changes once a second).

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