随机数不随机

真正意义上的随机数在某次产生过程中是按照实验过程中表现的分布概率随机产生的,其结果是不可预测的,是不可见的。而计算机中的随机函数是按照一定算法模拟产生的,其结果是确定的,是可见的。我们可以这样认为这个可预见的结果其出现的概率是100%。所以用计算机随机函数所产生的“随机数”并不随机,是伪随机数。在c语言中我试用过好多次srand()和rand()函数产生随机数,一直知道它产生的是一个伪随机数,不是真正意义上的随机数,但是具体怎么实现的我还不知道。前两天在生成一些随机数用于测试的时候,被自己坑了一把,我想生成10W个随机数,我写了下面的一段代码

1
2
3
4
5
6
7
8
9
10
11
12
FILE *fp;
if (fp= fopen ( "rand.txt" , "w" )) {
     printf ( "success\n" );
} else {
     printf ( "failed\n" );
}
int i = 0;
for (i = 0; i < 100000; ++i) {
     srand ( time (NULL));
     fprintf (fp, "%d\n" , rand ());
}
fclose (fp);

程序执行完之后发现生成的随机数全是一样的,有点小惊讶,仔细分析程序后发现每次循环我都调用srand()函数,设置了随机种子,于是我将程序稍作调整

1
2
3
4
5
6
7
8
9
10
11
12
FILE *fp;
if (fp= fopen ( "rand.txt" , "w" )) {
     printf ( "success\n" );
} else {
     printf ( "failed\n" );
}
int i = 0;
srand ( time (NULL));
for (i = 0; i < 100000; ++i) {
     fprintf (fp, "%d\n" , rand ());
}
fclose (fp);

这次执行的结果和我的预期一样,生成了毫无规律的十万个随机数。为什么我每次产生随机数之前都调用srand()函数,设置随机种子就达不到我生成10W个随机数的需求呢?于是我决定这次要彻底搞清楚这个随机数产生的原理,我先用man rand命令看了一下
rand()函数一个位于0到RAND_MAX之间的伪随机整数,srand()函数将其参数作为一个种子,初始化一个伪随机数序列,供rand()函数调用返回。
如果随机种子相同,那么每次初始化的伪随机序列也是相同的。
手册最后给出了一种实现rand()和srand()函数的方法:

1
2
3
4
5
6
7
8
9
10
11
static unsigned long next = 1;
 
/* RAND_MAX assumed to be 32767 */
int myrand( void ) {
    next = next * 1103515245 + 12345;
    return ((unsigned)(next/65536) % 32768);
}
 
void mysrand(unsigned seed) {
    next = seed;
}

看了这个例子我基本明白了srand()和rand()函数产生随机数的原理了。为了彻底搞清楚c语言中随机数的原理,我决定看一下sran()和rand()函数的具体实现方法。在glibc的stdlib文件夹下面查看random.c和random_r.c两个文件,我抽出了rand()函数(__random())和srand()函数(__srandom())的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <stdio.h>
#include <stdlib.h>
 
#define TYPE_0      0
#define BREAK_0     8
#define DEG_0       0
#define SEP_0       0
 
#define TYPE_1      1
#define BREAK_1     32
#define DEG_1       7
#define SEP_1       3
 
#define TYPE_2      2
#define BREAK_2     64
#define DEG_2       15
#define SEP_2       1
 
#define TYPE_3      3
#define BREAK_3     128
#define DEG_3       31
#define SEP_3       3
 
#define TYPE_4      4
#define BREAK_4     256
#define DEG_4       63
#define SEP_4       1
 
#define MAX_TYPES   5
 
static int32_t randtbl[DEG_3 + 1] =
   {
     TYPE_3,
 
     -1726662223, 379960547, 1735697613, 1040273694, 1313901226,
     1627687941, -179304937, -2073333483, 1780058412, -1989503057,
     -615974602, 344556628, 939512070, -1249116260, 1507946756,
     -812545463, 154635395, 1388815473, -1926676823, 525320961,
     -1009028674, 968117788, -123449607, 1284210865, 435012392,
     -2017506339, -911064859, -370259173, 1132637927, 1398500161,
     -205601318,
   };
 
static struct random_data unsafe_state =
   {
     .fptr = &randtbl[SEP_3 + 1],
     .rptr = &randtbl[1],
     .state = &randtbl[1],
     .rand_type = TYPE_3,
     .rand_deg = DEG_3,
     .rand_sep = SEP_3,
     .end_ptr = &randtbl[ sizeof (randtbl) / sizeof (randtbl[0])]
};
 
 
int __srandom_r (unsigned int seed, struct random_data *buf)
{
   int type;
   int32_t *state;
   long int i;
   long int word;
   int32_t *dst;
   int kc;
 
   if (buf == NULL)
     goto fail;
   type = buf->rand_type;
   if ((unsigned int ) type >= MAX_TYPES)
     goto fail;
 
   state = buf->state;
   if (seed == 0)
     seed = 1;
   state[0] = seed;
   if (type == TYPE_0)
     goto done;
 
   dst = state;
   word = seed;
   kc = buf->rand_deg;
   for (i = 1; i < kc; ++i)
     {
       long int hi = word / 127773;
       long int lo = word % 127773;
       word = 16807 * lo - 2836 * hi;
       if (word < 0)
     word += 2147483647;
       *++dst = word;
     }
 
   buf->fptr = &state[buf->rand_sep];
   buf->rptr = &state[0];
   kc *= 10;
   while (--kc >= 0)
     {
       int32_t discard;
       ( void ) __random_r (buf, &discard);
     }
 
  done:
   return 0;
 
  fail:
   return -1;
}
 
int __random_r ( struct random_data *buf, int32_t *result)
{
   int32_t *state;
 
   if (buf == NULL || result == NULL)
     goto fail;
 
   state = buf->state;
 
   if (buf->rand_type == TYPE_0)
     {
       int32_t val = state[0];
       val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;
       state[0] = val;
       *result = val;
     }
   else
     {
       int32_t *fptr = buf->fptr;
       int32_t *rptr = buf->rptr;
       int32_t *end_ptr = buf->end_ptr;
       int32_t val;
 
       val = *fptr += *rptr;
       *result = (val >> 1) & 0x7fffffff;
       ++fptr;
       if (fptr >= end_ptr)
     {
       fptr = state;
       ++rptr;
     }
       else
     {
       ++rptr;
       if (rptr >= end_ptr)
         rptr = state;
     }
       buf->fptr = fptr;
       buf->rptr = rptr;
     }
   return 0;
 
  fail:
   return -1;
}
 
void __srandom (unsigned int x)
{
   ( void ) __srandom_r (x, &unsafe_state);
}
 
long int __random ()
{
   int32_t retval;
   ( void ) __random_r (&unsafe_state, &retval);
   return retval;
}

      程序比较简单易懂,其中有一个很关键的伪随机数表——randtbl,所谓的随机数就是通过它里面的值算出来的,如果随机种子设置为0,那么它会自动转化成1,所以调用__srandom(0)和__srandom(1)是一样的效果。我还发现不设随机种子,直接调用rand()函数产生一个随机数,和设置随机种子为1的结果是相同的。
通过gdb调试我发现,当随机种子为1时,调用__srandom()之后,随机表randtbl的结果为:
{3, -1726662223, 379960547, 1735697613, 1040273694, 1313901226, 1627687941, -179304937, -2073333483, 1780058412, -1989503057, -615974602, 344556628,
939512070, -1249116260, 1507946756, -812545463, 154635395, 1388815473, -1926676823, 525320961, -1009028674, 968117788, -123449607, 1284210865, 435012392,
-2017506339, -911064859, -370259173, 1132637927, 1398500161, -205601318}
和初始化之前一模一样,所以直接调用__random()函数和设置随机中0或者1之后在调用__random()函数产生的随机数是相同的,都是1804289383。

      看来随机数真的不随机啊,这就是为什么经常说,rand()函数产生的是一个伪随机数。只要随机种子一定,那么调用rand()函数所产生的就是一个可确定的值。这就很好解释,我之前想生成10W个随机数,却生成了10W一模一样的数,因为我每次循环都使用了srand(time(NULL)),而程序执行非常快,在1s之内,time(NULL)在程序执行过程中是一个固定值,所以每次产生的随机数都是相同的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值