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)。

我是在太机智了哈哈哈哈哈哈

 

posted @ 2016-10-31 08:52  warmland  阅读(285)  评论(0编辑  收藏  举报