C语言随机数生成方法——srand种子的讨论

通常的随机数实现方法如下面的代码所示(这个函数返回一个最小值 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值