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

results matching ""

    No results matching ""