目录
一、什么是蓄水池抽样算法
我们现在来考虑一种出现在大数据流中的随机抽样问题,即:当内存无法加载全部数据时,如何从包含未知大小的数据流中随机选取k个数据,并且要保证每个数据被抽取到的概率相等。
蓄水池抽样算法就是用来解决该类问题的算法。
二、蓄水池抽样算法的证明
当时
也就是说,我们每次只能读一个数据。
假设数据流含有N个数,我们知道如果要保证所有的数被抽到的概率相等,那么每个数抽到的概率应该为
那如何保证呢?
先说方案:每次只保留一个数,当遇到第 i 个数时,以 的概率保留它,的概率保留原来的数。
举例说明: 1 - 10
- 遇到1,概率为 1,保留第一个数。······因为第一次时,第一个数一定会被选择
- 遇到2,概率为 ,这个时候,1和2各 的概率被保留。 ······要么选择之前保留的1,要么选择新来的2,此时我们假设选择的仍然是之前保留的1
- 遇到3,3被保留的概率为 ,(之前剩下的数假设1被保留), 的概率 1 被保留,(此时1被保留的总概率为 * = )。
- 遇到4,4被保留的概率为 ,(之前剩下的数假设1被保留), 的概率 1 被保留,(此时1被保留的总概率为 * * = )。
以此类推,每个数被保留的概率都是。
当
也就是说,我们每次能读m个数据。
对于的情况,我们可以采取类似的策略:
假设数据流中总共有N个数据,要保证每条数据被抽取到的概率相等,那么每个数被抽取到的概率必然是。
- 第一次,对于前k个数 ,我们一定会保留下来,所以他们的概率都为1。
- 对于第k+1个数,以 的概率保留它(这里只是指本次保留下来),那么前k个数中的任意一个数被保留的概率可以表示为:P(1~k中某个数被保留) = P(上一轮这个数被保留) * P(第k+1个数被丢弃) + P(上一轮这个数被保留) * P(第k+1个数没有被丢弃) * P(且前k个数中这个数没有被替换), 即P(1~k中某个数被保留) = 。
- 对于第k+2个数,以的的概率保留它(这里只是指本次保留下来),那么前k+1个数中的任意一个数被保留的概率可以表示为:P(1~k+1中某个数被保留) = P(上一轮这个数被保留) * P(第k+2个数被丢弃) + P(上一轮这个数被保留) * P(第k+2个数没有被丢弃) * P(且前k个数中这个数没有被替换), 即P(1~k+1中某个数被保留) = 。
- ... ...
- 对于第i(i>k)个数 ,以 的概率保留它,那么前i-1个数中的某一个数被保留的概率为 P = 。
即对于前k个数全部保留,对于第i(i>k)个数,以 的概率保留第i个数,并以 的概率与前面已选择的k个数中的任意一个替换。
证明完毕,上述的数学证明就是蓄水池抽样算法思想的体现。
三、例题
给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点 被选中的概率一样 。
实现
Solution
类:
Solution(ListNode head)
使用整数数组初始化对象。int getRandom()
从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等。
Demo
class Solution {
public:
ListNode* head;
Solution(ListNode* head) {
this->head = head;
}
int getRandom() {
ListNode* cur = head;
int ans = 0;
int num = 1;
while(cur != nullptr)
{
if(rand() % num == 0)
{
ans = cur->val;
}
cur = cur->next;
num++;
}
return ans;
}
};
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。
注意:
数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。示例:
int[] nums = new int[] {1,2,3,3,3}; Solution solution = new Solution(nums); // pick(3) 应该返回索引 2,3 或者 4。每个索引的返回概率应该相等。 solution.pick(3); // pick(1) 应该返回 0。因为只有nums[0]等于1。 solution.pick(1);
class Solution {
private:
vector<int> nums;
public:
Solution(vector<int>& nums):nums(nums) {}
int pick(int target) {
int ans = 0;
int count = 0;
for(int i=0;i<nums.size();++i)
{
if(nums[i] == target)
{
++count;
if(rand()%count == 0) ans = i;
}
}
return ans;
}
};