寻找一个完全随机的数字生成器。

5

我从事C语言编程,想(极力)制作一个随机数生成器,不仅可以每次运行生成不同的数字,而且每次运行程序都会生成不同的序列。我尝试过网上几乎所有的方法,最终得出了两种好用的随机数生成器。第一种方法是每次使用不同的种子(seed),但这意味着我必须每次使用不同的随机种子,这个问题我一开始并没有解决。以下是我现在尝试的方法,但它并不像我想要的那样真正随机:

int myrand(int random_seed){
  random_seed = random_seed * 1103515245 +12345;   
  return (unsigned int)(random_seed / 65536) % 32768; 
                           }

每次调用函数时,我将种子增加1。
第二种方法是使用time.Time更改来实现随机性。我也尝试了许多方法来实现这一点。我的最新尝试可以在此处找到:Compiler error-Possible IDE error"undefined reference to gettimeofday error",但是我无法使用gettimeofday函数,因为我在Windows上工作。而且,在那个问题中我没有得到任何答案。
有人能告诉我如何在Windows下使用C实现一个随机生成器(可能使用时间)吗?或者我应该使用Unix?

2
srand() 有问题吗?你可以使用时间作为种子来初始化,像这样:srand( time(NULL) ); - Hunter McMillen
我也尝试了srand(time(NULL)),但是我不得不制造延迟来等待时间的改变。原因是我想一次生成大量的随机数,而我不想等待2分钟让程序生成它们。我也尝试使用毫秒,但没有成功。也许毫秒是答案,但我无法正确实现它,问题仍然是我正在使用Windows。 - Dchris
2
@Dchris:在程序开始时使用时间仅为您的伪随机数生成器(PRNG)提供种子。然后,您可能需要确保每秒不要运行程序超过一次(引入毫秒将有所帮助),但您不必等待每次从PRNG读取数字时等待一秒钟。 - Steve Jessop
@SteveJessop:希望这个能行! - Dchris
6个回答

4

使用好的熵源来种子随机数生成器(RNG)。

在Unix操作系统中,使用/dev/random。

在Windows操作系统中,使用类似CryptoAPI这样的工具 - Windows中与/dev/random等效的工具


1
这仅适用于重要的随机性。从/dev/random读取将耗尽系统的熵,这将导致在最终为空时阻塞,直到系统收集更多的熵。当然,OP没有说明他试图解决的问题是否与安全有关。我怀疑不是。 - Nikos C.
更改为注明您可以通过这种方式仅设置随机数生成器的种子。 - evil otto
1
在C++11中,您可以使用std::random_device,它将使用系统上的任何真正随机数据源。 - user283145

3
你需要的不是随机数生成器,而是如何使用C标准库中已经包含的随机数生成器。
你只需要在程序启动时进行一次种子初始化即可:
srand(time(NULL));

这就是全部内容。它是可移植的,每次运行程序时会给你一个不同的序列,只要自上次运行以来至少过去了一秒钟。

重新初始化一次不会有任何影响,但也没有意义。


重新播种不应该轻率对待。根据PRNG算法,重新播种可能是增加熵的理想选择,但是重新播种有正确和错误的方式,并且基于不良重新播种的密码攻击也是存在的。正如您所说,对于简单的PRNG用例,重新播种通常是不必要的。例如,MT算法可以生成惊人的2^19937-1个数字,然后才会重复。其他算法在循环前可达到2^32个数字。 - bot403

1
C标准库有头文件time.h(如果您使用C ++,则为ctime)(reference)。这些函数将在Windows和Unix中得到支持。
我建议使用time()或clock()作为随机数生成器的种子。
另一种获取完全随机输入的方法是使用鼠标位置或其他受外部影响的事物。

0

有很多方法可以实现伪随机数生成器,但可惜的是它们都不是真正的随机数生成器。time(NULL) 是一个不错的选择,但我正在使用 "布伦布伦香比" 算法。它可以生成一位随机数。


0

引用:

制作一个随机生成器,不仅每次运行生成器都会生成不同的数字

随机数的定义并不包括这个概念。相反,这个想法是你有平等的机会选择任何数字,无论之前选择了哪个数字。这意味着理论上可能会选择两次相同的数字。

如果你在处理一副牌,那么符合你的标准就是没有重复的牌。使用发牌的方法意味着要跟踪“已使用”的数字。

你还应该知道,伪随机数生成器(PNRGs)是循环的(周期性的)。在生成了一些数字之后,通常是大量的数字,你就重新开始并重复完全相同的数字序列。UNIX rand() 函数生成范围在 [0,{RAND_MAX}] 的整数,并具有 2^32 的周期。

真的考虑阅读这个简短的页面:

请参见:http://pubs.opengroup.org/onlinepubs/009695399/functions/rand.html


0

既然您明确要求 Windows 解决方案,我建议避免使用 time(NULL)clock() 作为 srand() 的种子,因为它们的粒度非常有限(毫秒)。相反,您可以使用性能计数器的结果:

LARGE_INTEGER PerformanceCount;
QueryPerformanceCounter(&PerformanceCount);
srand(PerformanceCount.LowPart);

QueryPerformanceCounter() 的频率增量可以通过调用 QueryPerformanceFrequency() 来获得。这通常至少增加到 1 MHz,有时甚至达到 GHz 级别。因此,它提供了一个快速变化的种子源。

编辑:从您之前的问题中了解到,即使是类似于 gettimeofday() 的实现也无法提供精细的粒度。它可能会在其参数中显示单词tv_usec,但在 Windows 上,它不会像在 Linux 系统上那样提供微秒级的粒度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接