一、思路:ios
蓄水池问题:当n≤k时,序号为n的这个数放入容量为k的容器中,表示该数确定会被选中;当n>k时,要使得前n个数每一个数被选中的几率都是k⁄n。dom
证实:假设n≥k,前n个数每一个数被选中的几率都是k⁄n,则须要概括证实前n+1个数每一个数被选中的几率是k⁄(n+1)。函数
分两个角度去思考这个问题:一是第n+1个被选中的几率,很显然是k⁄(n+1)。二是第一、二、...、n个数被选中的几率也要是k⁄(n+1)。spa
后者分析:以第m个数为例(k<m<n+1),它被选中的几率为”m被选中的几率*[m后面元素没有被选中+m后面的元素被选中*可是m没被替换的几率]“。code
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 }
二、思路:blog
假设要求[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 }
三、思路:it
思考角度1:生成1,2,…n的几率分别是1/n,也就是均等的。那么咱们能够想怎么生成一个序列,里面有n个独立事件,几率是相等。并且咱们可以猜想到这些几率的形式为px(1-p)y,若是要相等,那么x必须等于y。这样就说明了序列中0和1的个数是相等的。并且这样的状况必须有多于n个状况才行。io
思考角度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的地方取得,此时我有更大比率的序列用于生成,换句话说被抛掉的更少了,这样作是为了不大量生成了丢弃序列而使得生成速率减慢,其它的没什么特别的意思。