场景
在大数据流中以相同概率选中每个元素。如当内存无法加载全部数据时,如何从未知大小的数据流中筛选出k个元素,要求它们被选中的概率相同。
k = 1时
题目:382. 链表随机节点 - 力扣(LeetCode) (leetcode-cn.com)。
解法1:保存链表中所有元素的值到一个数组,然后在pick时随机取一个索引即可。Pick的时间复杂度O(1),空间复杂度O(n)。
解法2:先计算出链表长度为n,然后每次Pick时通过n求出一个随机值,通过该随机值取到链表中的随机结点。时间复杂度O(n),空间复杂度O(1)。
解法3:蓄水池抽样算法。过程如下:
1. 假设不知道多少个数,只知道会有数不断地一个一个地进来;
2. 首先进来1,我们选中它,此时选中1的概率为1/1;
3. 进来2,此时共两个数。首先定义randInt(x)均匀随机返回0~x-1之间的某个数。我们令randInt(2)=0时选中2,否则选中的数还是1;
4. 进来3,此时共两个数。令randInt(3)=0时选中3,否则选中的数还是之前的;
5. 依此算法,直到该数据流中的数据全部走完。
证明
对于1,一开始选中它的概率为1; 2进来后,1和2被选中的概率各为1/2; 3进来后,选中3的概率为1/3,选中1或2的概率为2/3,1和2各自被选中的概率为1/2 * 2/3 = 1/3; 依此类推,最终所有元素被选中的概率为1/n。
k > 1时
共n个数,随机选取k个数。过程如下:
1. 对于前k个数,保留它们;
2. 对于第k+1个数,它有1/(k+1)的概率保留下来;如果它保留下来了,随机从前k个数中找出一个用第k+1个数替换它;
3. 依此模拟。
相似题目
398. 随机数索引 - 力扣(LeetCode) (leetcode-cn.com).
总结
蓄水池抽样算法有其特定的使用场景,就是大数据流和未知数据量大小。否则用上面的解法1或解法2效率更高。