为啥我总是用 rand() 得到相同的随机数序列?
Posted
技术标签:
【中文标题】为啥我总是用 rand() 得到相同的随机数序列?【英文标题】:Why do I always get the same sequence of random numbers with rand()?为什么我总是用 rand() 得到相同的随机数序列? 【发布时间】:2010-11-09 16:13:06 【问题描述】:这是我第一次使用 C 尝试随机数(我想念 C#)。这是我的代码:
int i, j = 0;
for(i = 0; i <= 10; i++)
j = rand();
printf("j = %d\n", j);
使用此代码,我每次运行代码时都会得到相同的序列。但是如果我在for
循环之前添加srand(/*somevalue/*)
,它会生成不同的随机序列。谁能解释一下为什么?
【问题讨论】:
就像一个边节点:rand() 实现通常很糟糕。这意味着短的重复周期和低质量的随机数。所以我会使用第三方 PRNG,而不是你的运行时中包含的那个。 srand() — why call it only once? 【参考方案1】:你必须播种。随时间播种是个好主意:
srand()
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main ()
srand ( time(NULL) );
printf ("Random Number: %d\n", rand() %100);
return 0;
您会得到相同的序列,因为如果您不调用 srand()
,rand()
会自动以 1 的值作为种子。
编辑
由于cmets
rand()
将返回一个介于 0 和 RAND_MAX
之间的数字(在标准库中定义)。使用 modulo 运算符 (%
) 给出除法的余数 rand() / 100
。这将强制随机数在 0-99 范围内。例如,要获取 0-999 范围内的随机数,我们将应用 rand() % 1000
。
【讨论】:
我已经知道了,但我的问题是为什么当我不使用 srand 时它会给出相同的顺序? 因为如果你不手动播种,默认情况下它总是播种为1。请参阅 Aditya 的回答。 如果安全是一个问题,用时间播种是一个相当糟糕的主意,因为攻击者通常可以相对容易地找到或猜测启动时间(在几十到几百次尝试内),然后重播你的伪随机数序列。如果可能,请尝试为您的种子使用操作系统提供的熵源。 如果安全是一个问题,使用 rand() 是一个相当糟糕的主意,无论你如何播种它。除了 PRNG 算法的未知强度之外,它通常只需要 32 位种子,因此即使您没有通过随时间播种来使其变得特别容易,暴力破解也是合理的。使用熵源为 rand() 植入安全性就像给驴子注射类固醇并将其输入到 [肯塔基州] 德比中。 "例如,要获得 0-999 范围内的随机数,我们将应用 rand()%1000" 警告,除非 1000 均分到 RAND_MAX+1,否则结果不会均匀分布(它可能不会因为 RAND_MAX 通常是 (2^n)-1),而且还有很多其他问题。见azillionmonkeys.com/qed/random.html【参考方案2】:rand()
返回pseudo-random 数字。它根据给定的算法生成数字。
该算法的起点始终相同,因此您会看到每次调用都生成相同的序列。当您需要验证程序的行为和一致性时,这很方便。
您可以使用srand
函数设置随机生成器的“种子”(在程序中只调用一次srand
)从rand()
生成器获取不同序列的一种常用方法是将种子设置为当前时间或进程id:
srand(time(NULL));
或 srand(getpid());
在程序开始时。
生成真正的随机性对于计算机来说非常非常困难,但对于实际的非加密相关工作,尝试均匀分布生成的序列的算法可以正常工作。
【讨论】:
我如何获得getpid()
?我需要什么特殊的图书馆吗?
@hola #include <unistd.h>
【参考方案3】:
引用man rand:
srand() 函数设置它的参数 作为新序列的种子 要返回的伪随机整数 通过兰德()。这些序列是 可通过调用 srand() 来重复 相同的种子值。
如果没有提供种子值,则 rand() 函数是自动 种子值为 1。
因此,没有种子值,rand()
假定种子为 1(在您的情况下每次)并且具有相同的种子值,rand()
将产生相同的数字序列。
【讨论】:
【参考方案4】:这里有很多答案,但似乎没有人真的解释了为什么 rand() 总是在给定相同种子的情况下生成相同的序列 - 甚至是种子的真正含义正在做。就这样吧。
rand() 函数维护一个内部状态。从概念上讲,您可以将其视为某种类型的全局变量,称为 rand_state。每次调用 rand() 时,它都会做两件事。它使用现有状态计算一个新状态,并使用新状态计算一个数字返回给你:
state_t rand_state = INITIAL_STATE;
state_t calculate_next_state(state_t s);
int calculate_return_value(state_t s);
int rand(void)
rand_state = calculate_next_state(rand_state);
return calculate_return_value(rand_state);
现在您可以看到,每次调用 rand() 时,都会使 rand_state 沿着预先确定的路径移动一步。您看到的随机值仅基于您在该路径上的位置,因此它们也将遵循预先确定的顺序。
现在是 srand() 的用武之地。它可以让您跳转到路径上的不同点:
state_t generate_random_state(unsigned int seed);
void srand(unsigned int seed)
rand_state = generate_random_state(seed);
state_t、calculate_next_state()、calculate_return_value() 和 generate_random_state() 的具体细节可能因平台而异,但它们通常很简单。
从中可以看出,每次你的程序启动时,rand_state 都会从 INITIAL_STATE 开始(相当于 generate_random_state(1))——这就是为什么如果你不使用 srand 总是得到相同的序列()。
【讨论】:
【参考方案5】:如果我记得在随机数生成一章开头引用 Knuth 的开创性著作“计算机编程的艺术”的话,它是这样的:
“从技术上讲,任何试图通过数学方法生成随机数的人都处于犯罪状态”。
简单地说,股票随机数生成器是算法、数学和 100% 可预测的。在很多情况下,这实际上是一件好事,其中需要可重复的“随机”数字序列 - 例如对于某些统计练习,您不希望真正随机数据引入的结果中出现“摆动”,这要归功于聚类效应。
虽然从计算机硬件中获取“随机”数据位是一种流行的第二种选择,但它也不是真正的随机 - 尽管操作环境越复杂,随机性的可能性就越大 - 或者至少是不可预测的。
真正的随机数据生成器倾向于寻找外部来源。放射性衰变是最受欢迎的,类星体的行为也是如此。任何源于量子效应的东西实际上都是随机的——这让爱因斯坦很烦恼。
【讨论】:
【参考方案6】:随机数生成器实际上并不是随机的,它们像大多数软件一样是完全可预测的。 rand 所做的是每次调用一个看起来是随机的 One 时创建一个不同的伪随机数。为了正确使用它,您需要给它一个不同的起点。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main ()
/* initialize random seed: */
srand ( time(NULL) );
printf("random number %d\n",rand());
printf("random number %d\n",rand());
printf("random number %d\n",rand());
printf("random number %d\n",rand());
return 0;
【讨论】:
【参考方案7】:这是来自http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.13.html#rand:
声明:
void srand(unsigned int seed);
此函数为函数 rand 使用的随机数生成器提供种子。用相同的种子播种 srand 将导致 rand 返回相同的伪随机数序列。如果 srand 没有被调用,rand 的行为就像 srand(1) 被调用一样。
【讨论】:
【参考方案8】:rand() 返回系列中的下一个(伪)随机数。发生的事情是每次运行时您都有相同的系列(默认为“1”)。要播种新系列,您必须在开始调用 rand() 之前调用 srand()。
如果你每次都想要随机的东西,你可以试试:
srand (time (0));
【讨论】:
【参考方案9】:Rand 不会给你一个随机数。它为您提供pseudorandom 数字生成器生成的序列中的下一个数字。要在每次启动程序时获得不同的序列,您必须通过调用 srand 为算法播种。
一种(非常糟糕的)方法是传递当前时间:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
srand(time(NULL));
int i, j = 0;
for(i = 0; i <= 10; i++)
j = rand();
printf("j = %d\n", j);
return 0;
为什么这是一个不好的方法?因为伪随机数生成器和它的种子一样好,而且种子一定是不可预测的。这就是为什么您可能需要更好的熵源,例如阅读 /dev/urandom
。
【讨论】:
【参考方案10】:在调用 rand()
之前先调用 srand(sameSeed)
。更多详情here.
【讨论】:
【参考方案11】:Seeding the rand()
void srand (unsigned int seed)
这个函数将种子建立为一系列新的伪随机数的种子。如果您在使用 srand 建立种子之前调用 rand,它将使用值 1 作为默认种子。
要在每次运行程序时生成不同的伪随机序列,请执行 srand (time (0))
【讨论】:
【参考方案12】:你们都没有回答他的问题。
使用此代码,每次代码我都会得到相同的序列,但如果我在 for 循环之前添加 srand(/somevalue/),它会生成随机序列。有人能解释一下为什么吗?
根据我的教授告诉我的内容,如果您想确保您的代码正常运行并查看是否有问题或是否可以更改某些内容,则使用它。
【讨论】:
如果您正确阅读答案,他的问题之前已经回答过。您的教授告诉您的只是对 rand() 算法按预期方式编程的原因的推断。以上是关于为啥我总是用 rand() 得到相同的随机数序列?的主要内容,如果未能解决你的问题,请参考以下文章