随机生成器
- 在不知道文件总行数的情况下,如何从文件中随机的抽取一行或是k行。
- 已知random_m()随机数生成器的范围是[1, m] 求random_n()生成[1, n]范围的函数,m < n && n <= m *m。
- 已知一随机发生器,产生0的概率是p,产生1的概率是1-p。现在要你构造一个发生器, 使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;构造一个发生器,使得它构造1、2、3、…n的概率均为1/n,要求复杂度最低。
1、思路:
蓄水池问题:当n≤k时,序号为n的这个数放入容量为k的容器中,表示该数肯定会被选中;当n>k时,要使得前n个数每个数被选中的概率都是k⁄n。
证明:假设n≥k,前n个数每个数被选中的概率都是k⁄n,则需要归纳证明前n+1个数每个数被选中的概率是k⁄(n+1)。
分两个角度去思考这个问题:一是第n+1个被选中的概率,很显然是k⁄(n+1)。二是第1、2、...、n个数被选中的概率也要是k⁄(n+1)。
后者分析:以第m个数为例(k<m<n+1),它被选中的概率为”m被选中的概率*[m后面元素没有被选中+m后面的元素被选中*但是m没被替换的概率]“。
1 #include <iostream> 2 #include <algorithm> 3 #include <ctime> 4 5 using namespace std; 6 7 void SamplePool(int *data, int n, int m) 8 { 9 int i, s; 10 srand(time(0)); 11 for (i = m; i < n; i++) 12 { 13 s = rand() % i; 14 if (s < m) 15 swap(data[i], data[s]); 16 } 17 for (i = 0; i < m; i++) 18 cout << data[i] << ' '; 19 cout << endl; 20 } 21 22 int main() 23 { 24 int data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 25 SamplePool(data, 10, 5); 26 return 0; 27 }
2、思路:
假设要求[1,5]随机一个[1,7]。则先用[1,5]随机生成[1,25],再通过拒绝采样定理选取[1,21],其中选21而不选7的原因是提高随机生成的效率。
首先,将(1,5)之间的随机发生器使用两次,按照五进制进行使用,拼成一个(1,25)的随即发生器既:([gen][gen]),每一[]为一个5进制上的位,换算为十进制为:x=gen*5+gen。在十进制上的范围为:6-30,进行一个简单的左移动,可换算成1-25范围上的值;然后将(1,25)平均分配到7中情况上面,考虑21是7的倍数,因此可以每三个做一个映射(当然,也可以不管,直接截断7后面的数字,但是范围太小,效率不高),ƒ(1~3) = 1,ƒ(4~6) = 2,此时就是等概率的,如果产生了22-25之间的数字,则拒绝采样。
1 int Rand_M2N(int m, int n) 2 { 3 srand(time(0)); 4 int i, s, t, k; 5 //k是n的最大倍数,但是小于m*m;t是倍数的量。 6 for (i = 1; i <= MAX; i++) 7 { 8 if (i * n < m * m) 9 { 10 k = i * n; 11 t = i; 12 } 13 else 14 break; 15 } 16 cout << "k: " << k << endl; 17 //s是被随机选中的数。 18 do 19 { 20 s = m * (rand() % m) + (rand() % m + 1); 21 }while(s > k); 22 if (s % t == 0) 23 return s / t; 24 else 25 return s / t + 1; 26 }
3、思路:
思考角度1:生成1,2,…n的概率分别是1/n,也就是均等的。那么我们可以想怎么生成一个序列,里面有n个独立事件,概率是相等。而且我们能够猜测到这些概率的形式为px(1-p)y,如果要相等,那么x必须等于y。这样就说明了序列中0和1的个数是相等的。而且这样的情况必须有多于n个情况才行。
思考角度2:然后是1/n的情况了,我们以5为例,此时我们取x=2,因为C(2x,x)=C(4,2)=6是比5大的最小的x,此时我们就是一次性生成4位二进制,把1出现个数不是2的都丢弃,这时候剩下六个:0011,0101,0110,1001,1010,1100,取最小的5个,即丢弃1100,那么我们对于前5个分别编号1到5,这时候他们的概率都是p*p*(1-p)*(1-p)相等。关键是找那个最小的x,使得C(2x,x)>=n这样能提升查找效率。我之所以取C(2x,x)是为了让同样的序列长度下可用的资源尽量多,因为C(n,i)最大是在i接近n/2的地方取得,此时我有更大比率的序列用于生成,换句话说被抛掉的更少了,这样做是为了避免大量生成了丢弃序列而使得生成速率减慢,其它的没什么特别的意思。