通常的随机数实现方法如下面的代码所示(这个函数返回一个最小值 minnum 和最大值 maxnum 之间的随机数)。随机数种子由time函数生成:srand((unsigned) time(NULL))。
#include <stdio.h> #include <stdlib.h> #include <time.h>int random_index(minnum, maxnum) { srand((unsigned) time(NULL)); int choice = maxnum - minnum + 1; int a = rand() % choice + minnum; return a; }
这种产生随机数方式因为现代计算机速度太快,导致一秒内调用 random_index 函数返回的结果都一样,这样我们其实得到的就不是随机数了。例如我们运行下面的测试程序 test_rand1.c:
#include<stdio.h> #include<stdlib.h> #include<time.h> #define random(i) (rand()%i)void main() { for(int i=0; i < 10; i++) { srand((unsigned)time(0)); printf("%d\n",random(100)); } }
编译此程序:gcc -o test_rand1 test_rand1.c -std=gnu99
然后运行:
$./test_rand1 25 25 25 25 25 25 25 25 25 25
运行后发现10个数字都一样,但是再次运行跟前一次的10个数字又有不同。这就是因为循环速度没有超过一秒,srand((int)time(0)) 的种子time(0)没有变化,导致srand产生的起始值都一样,rand() 函数自然返回一样的结果。
这里说明一下:
void srand(unsigned seed); 参数seed是rand()的种子,用来初始化rand()的起始值。int rand(void); 从srand(seed)中指定的seed开始,返回一个 [seed, RAND_MAX (0x7fff) ) 间的随机整数。
如果我们修改上面的测试代码为 test_rand2.c:
#include<stdio.h> #include<stdlib.h> #include<time.h> #define random(i) (rand()%i)void main() { srand((unsigned)time(0)); for(int i=0; i < 10; i++) { printf("%d\n",random(100)); } }
编译运行:
$./test_rand2 97 75 58 81 60 31 9 83 52 19
那么循环给出的10个值就都不同了。只是这里我们得到的随机数使用了相同的种子,这样的随机数还不够“随机”(尽管计算机通过算法生成的随机数都属于伪随机数)。
我们想要每次返回的结果都是使用不同种子的随机数,该如何做呢?
C 的 time 库里提供一个 clock_gettime 函数,它可以返回纳秒级别的时间值,基本上CPU的工作时间单位也就是纳秒,这样利用这个函数取得的时间值就可以作为种子保证每次 rand() 函数的起始值都不同了。我们试试下面的测试代码 test_rand3.c:
#include<stdio.h> #include<stdlib.h> #include<time.h> #define random(i) (rand()%i)void main() { for(int i=0; i < 10; i++) { struct timespec time1 = { 0, 0 }; clock_gettime(CLOCK_REALTIME, &time1); srand(time1.tv_nsec); printf("%d\n",random(100)); } }
编译运行后可以看到,尽管我们把随机数种子设置函数 srand 放到了循环内,但是得到的随机数结果都不一样了。这里需要对 clock_gettime 说明一下(基于当前GNU libc和Linux内核):
int clock_gettime(clockid_t clk_id, struct timespec *tp);此函数返回0表示运行成功,返回-1表示失败。运行成功后会根据 clk_id 参数对 tp 数据结构赋值。clk_id 参数解释 CLOCK_REALTIME: 系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时, 中间时刻如果系统时间被用户该成其他,则对应的时间相应改变 CLOCK_MONOTONIC: 从系统启动这一刻起开始计时,不受系统时间被用户改变的影响 CLOCK_PROCESS_CPUTIME_ID: 本进程到当前代码系统CPU花费的时间 CLOCK_THREAD_CPUTIME_ID: 本线程到当前代码系统CPU花费的时间tp 数据结构解释 在 time.h 中定义:struct timespec { time_t tv_sec; /* 秒 seconds */ long tv_nsec; /* 纳秒,10-9秒 nanoseconds */ };
所以最后我们可以把一开头的函数改写为下面的形式,这样在调用它的时候就不用担心得到不随机的结果了。
#include <stdio.h> #include <stdlib.h> #include <time.h>int random_index(minnum, maxnum) { struct timespec time1 = { 0, 0 }; clock_gettime(CLOCK_REALTIME, &time1); srand(time1.tv_nsec); int choice = maxnum - minnum + 1; int a = rand() % choice + minnum; return a; }