Reservoir Sampling
首先说一下Reservoir Sampling的原理 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的值
public int getMaxIdx(int[] stream) {
int cnt = 1;
int max = stream[0];
int maxIdx = 0;
Random r = new Random();
for (int i = 1; i < stream.length; i++) {
if (stream[i] == max) {
cnt++;
if (r.nextInt(cnt) == 0) {
maxIdx = i;
}
} else if (stream[i] > max) {
cnt = 1;
maxIdx = i;
max = stream[i];
}
}
return maxIdx;
}
时间复杂度是O(n), extra space是O(1)。