首页 > 代码库 > Reservoir Sampling - snapchat
Reservoir Sampling - snapchat
首先说一下Reservoir Sampling的原理
ref: http://www.geeksforgeeks.org/reservoir-sampling/
如果输入的Stream里面有N个数,我们需要从中选取k个随机的数,那么: 先把前k个数放入“水塘”(index范围是[0, k - 1]) 从index为[k,N - 1]中的第i个数中,随机生成一个[0, i - 1]的值r 如果r < k那么就把res[r]换成stream[i]
分析其中的概率:
对于第i个数,它被换入水塘的概率是 k/i,并且在下一次替换中,下一个数被替换进去的概率是 k/i+1,刚好替换到它的概率是 1/k,所以它下一轮中,被替换出来的概率是k/i+1 * 1/k = 1/i+1,所以,它在下一轮中没有被替换出来的概率是 1 - 1/i+1 = i/i+1。
所以,直到最后一个数为止,它留在水塘中的概率是 k/i * i/(i+1) * (i+1)/(i+2) * ... * (n-1)/n = k/n
对我来说……这个已经很难了…………= =
需要注意的是Random r = new Random(). int nextInt = r.nextInt(k). nextInt的值的范围是[0, k-1],不包括k
面经里的题的意思是,给你一个数组,在所有最大的元素里,随机选出一个坐标返回,要求每个坐标被选中的概率一样。
“具体请参考 给你一个输入 nums = [1, 4, 5, 2, 3, 5, 1, 3, 5], 然后impement一个getMaxIndex。 这里因为最大值是5,所以有2,5,8三个index。只用返回一个。要求是这三个index被返回的概率要相等。只能用O(1)的extra space. ”
本题做法:
对于一个数组,从头走到尾,如果它是当前最大的数,就结果是它,如果遇到好几个一样的最大的数,就在它的计数cnt中,随机生成[0, cnt]的值,如果rint < k(此处是1),即rint == 0的时候,更换maxIdx。
走完之后返回maxIdx的值
代码:
1 public int getMaxIdx(int[] stream) { 2 int cnt = 1; 3 int max = stream[0]; 4 int maxIdx = 0; 5 Random r = new Random(); 6 for (int i = 1; i < stream.length; i++) { 7 if (stream[i] == max) { 8 cnt++; 9 if (r.nextInt(cnt) == 0) { 10 maxIdx = i; 11 } 12 } else if (stream[i] > max) { 13 cnt = 1; 14 maxIdx = i; 15 max = stream[i]; 16 } 17 } 18 return maxIdx; 19 }
时间复杂度是O(n), extra space是O(1)。
我是在太机智了哈哈哈哈哈哈
Reservoir Sampling - snapchat