本文主要是介绍C 语言中的 srand 和 rand,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
大家在写 C/C++ 程序时,难免会遇到要求获取某个范围内的随机数,我查阅了一些资料后,总结如下。本文分两部分,先介绍 C 语言中与随机数相关的两个函数 srand 和 rand,后介绍 C++ 中的 random 库,每一部分最后会给出生成特定范围内的随机数模板供参考。
1 C 语言中的 srand 和 rand
1.1 实现
- #define RAND_MAX 32767 // in <stdlib.h>
- unsigned long _Randseed = 1; // global seed
- void srand(unsigned int seed) {
- _Randseed = seed;
- }
- int rand(void) {
- _Randseed = _Randseed * 1103515245 + 12345;
- return ((unsigned int)(_Randseed >> 16) & RAND_MAX);
- }
#define RAND_MAX 32767 // in <stdlib.h>
unsigned long _Randseed = 1; // global seed
void srand(unsigned int seed) {
_Randseed = seed;
}
int rand(void) {
_Randseed = _Randseed * 1103515245 + 12345;
return ((unsigned int)(_Randseed >> 16) & RAND_MAX);
}
第一次接触 C 语言中的随机数时,很疑惑为什么有种子这个玩意,只提供一个产生随机数的函数不就行了吗,看了上面的源码后,就明白了,因为计算机不能产生真正的随机数,只能靠数学的方法产生伪随机数。srand 函数的作用是设置种子,如果不设置的话种子(上面的 _Randseed)则默认初始是1,种子是全局变量。rand 的实现就跟数论有关了,上面的实现用的是线性同余法。可以看到它的返回值范围是 [0, RAND_MAX]。
1.2 time
既然计算机不能产生真正的随机数,那怎么才能使程序每次运行的结果不同呢?总得有个随机的东西,那就借助 time 这个函数产生种子,引入一个新东西又会带来一些坑,我早年写过这种程序:
- // 生成十个随机数(错误用法)
- for (int i = 0; i < 10; ++i) {
- srand((unsigned int)time(NULL));
- printf("%d: %d\n", i, rand());
- }
// 生成十个随机数(错误用法)
for (int i = 0; i < 10; ++i) {
srand((unsigned int)time(NULL));
printf("%d: %d\n", i, rand());
}
它将产生10个相同的数。要解释这个问题,就得弄懂 time 这个函数,它的函数原型如下:
- time_t time(time_t *timer);
time_t time(time_t *timer);
它返回“当前时间”,这个“时间“的类型是 time_t,在 VC 中被 typedef 为 unsigned long,标准中只规定它是个算数类型,至于它是如何表示时间的未定义。一般是返回 UNIX 时间戳,定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数。上面的程序执行时很快,在一秒内完成循环,所以它产生了相同的随机数。
1.3 my rand
下面提供两个生成随机数的模板。
- int g_is_first = 1;
- /*
- ** return a random integer in the interval
- ** [a, b]
- */
- int uniform_int(int a, int b) {
- if (g_is_first) {
- g_is_first = 0;
- srand((unsigned int)time(NULL));
- }
- return (int)((double)rand() / ((RAND_MAX + 1.0) / (b - a + 1.0)) + a);
- }
- /*
- ** return a random real in the interval
- ** [a, b] (also [a, b))
- */
- double uniform_real(double a, double b) {
- if (g_is_first) {
- g_is_first = 0;
- srand((unsigned int)time(NULL));
- }
- return (double)rand() / ((double)RAND_MAX / (b - a)) + a;
- }
int g_is_first = 1;
/*
** return a random integer in the interval
** [a, b]
*/
int uniform_int(int a, int b) {
if (g_is_first) {
g_is_first = 0;
srand((unsigned int)time(NULL));
}
return (int)((double)rand() / ((RAND_MAX + 1.0) / (b - a + 1.0)) + a);
}
/*
** return a random real in the interval
** [a, b] (also [a, b))
*/
double uniform_real(double a, double b) {
if (g_is_first) {
g_is_first = 0;
srand((unsigned int)time(NULL));
}
return (double)rand() / ((double)RAND_MAX / (b - a)) + a;
}
为了保证 srand 函数只执行一次,这里用了全局标志 g_is_first。其实最好在头文件中定义接口,在源文件中实现,这里为了使用方便就全放一起了。当要求的随机数范围过大时,uniform_int 和 uniform_real 貌似有 bug。
2 C++ 中的 random 库
- ** return a random integer in the interval [a, b]
- */
- int uniform_int(int a, int b) {
- static std::default_random_engine e{std::random_device{}()}; // avoid "Most vexing parse"
- static std::uniform_int_distribution<int> u;
- return u(e, decltype(u)::param_type(a, b));
- }
- /*
- ** return a random real in the interval [a, b] (also [a, b))
- */
- double uniform_real(double a, double b) {
- static std::default_random_engine e{std::random_device{}()};
- static std::uniform_real_distribution<double> u;
- return u(e, decltype(u)::param_type(a, b));
- }
|
|
2. 使用方法
|
|
|
|
3. 注意事项
- 求一定范围内的随机数。
|
- 伪随机浮点数。
大家可能很多次讨论过随机数在计算机中怎样产生的问题,在这篇文章中,我会对这个问题进行更深入的探讨,阐述我对这个问题的理解。
|
|
|
3.建议:如果想在一个程序中生成随机数序列,需要至多在生成随机数之前设置一次随机种子。
|