蓄水池抽样算法

324 阅读1分钟

今天leetcode的每日一题382. 链表随机节点的题解中出现了这个算法,因为是第一次了解到这个算法,简单记录一下。

蓄水池抽样算法

从一批总数未知的样本中选取若干个样本,要求每个样本被选取的概率相同。

假设总共有 n 个样本,对每一次抽取,从前往后遍历样本,对遇到的每一个样本做以下事情:
假设当前为第 i 个样本,那么以 1i\frac{1}{i} 的概率将这个样本作为结果样本,i1i\frac{i - 1}{i}的概率保留原来的结果样本

当遍历完整个样本群后,返回结果样本,完成一次抽取

为什么这样能保证每个样本被选取的概率相同呢?
P(第 i 个样本最后被抽取的概率)
= P(遍历到第 i 个样本时被作为结果样本的概率)×\timesP(遍历到第 i + 1 个样本时不被作为结果样本的概率)×\times...×\timesP(遍历到第 n 个样本时不被作为结果样本的概率)
= 1i\frac{1}{i} ×\times (11i+1)(1 - \frac{1}{i + 1}) ×\times ...... ×\times (11n)(1 - \frac{1}{n})
= 1i\frac{1}{i} ×\times ii+1\frac{i}{i+1} ×\times ...... ×\times n1n\frac{n-1}{n}
= 1n\frac{1}{n}

class Solution {

    Random random;
    ListNode head;

    public Solution(ListNode x) {
        random = new Random();
        head = x;
    }

    public int getRandom() {
        int i = 0;
        int ans = 0;
        ListNode t = head;
        //每一次抽取都需要遍历整个样本
        while(t != null && ++i > 0 ){
            // 等于 0 的概率为 1/i
            if(random.nextInt(i) == 0){
                ans = t.val;
            }
            t = t.next;
        }
        return ans;
    }
}