随机数之随笔
一直以来就觉得随机是一个很神奇的东西。随机在某种程度上代表的是不可预测性。不可预测性又使得我们的世界变得缤纷多彩起来:变幻莫测的天气,不到最后一刻胜负难料的足球,刺激的赌博,博彩等等。
随机可以在很多地方发生。如抓阄(人脑控制手的随机动作),掷骰子(骰子发生碰撞时的力和角度的随机),还有薛定谔的那只猫(在不打开门之前,猫是死是活都是随机的)。然而计算机也可以产生随机数。难道计算机真的能主动的进行随机的操作么?其实不然,计算机生成的随机数大多都是伪随机数。之所以叫“伪”,是因为伪随机数是用种子数和某种算法算出来的,也就是说只要知道种子数和算法,我们就可以反推出这个“随机”的过程。
以下是K&R书中关于伪随机数发生器函数的代码:
1: unsigned long int next = 1;
2:
3: void srand(unsigned int seed)
4: {
5: next = seed;
6: }
7:
8: int rand()
9: {
10: next = next*1103515245 + 12345;
11: return (unsigned int)(next/63336)%32768;//返回0-32767的整数
12: }
srand()函数是获取随机数发生器的种子,这个种子决定了生成的随机数。所以要使伪随机数看起来随机,种子不能一成不变。一般情况都是取本地的时间数为种子,因为这个数够大,而且每时每刻都不一样。
以下是获取随机数的两个小问题。
- 在[x,x+n]上获得m个不同的随机数
A.最朴实的一种应该就是每次都取一个随机数,然后在pool数组(初始为空)中查找这个数,若pool中有这个数则再生成一个,直到pool中无法找到为止再把它push进pool中。
B.另一种方法是用一个数组pool保存[x,x+n]上的所有的数,然后用一个for i=(0-m-1)循环,在这个循环中我们随机出[0,n-1]的数作为下标r,令pool[i]和pool[r]进行交换。然后取pool数组的前m个数即可。
C.还有一种方法,该方法的核心思想是在剩下的r个数里取出s个数,则取到每个数的概率为s/r。
下面是伪码的实现:
1: r = n2: s = m3: for i = [0,n)
4: if( bigrand()%r < s)//bigrand为生成超大随机数(远大于m,n)的函数
5: i+x即为要找的数6: s--7: r-- - 以不同的概率来取不同区间上的随机数,如以80%的概率取[0,20)的数,%20的概率取[20,100)的数。
A.区间扩大。将[0,20)的数扩大到((80/0.2)*0.8)/20 = 16倍即可。也就是说样本里有16份重复的[0,20),而只有1份[20,100)。然后再在全局随机。但这样太浪费空间了,而且不够精确。
B.区间映射。可在0-1000内取随机数p,若p落在区间[0,800),则随机数为r = 0+p%20;若p落在[800,1000)则随机数r = 20+p%80
博客地址转移至:http://wuzhiwei.net
浙公网安备 33010602011771号