输入:一组数据,其大小未知
输出:这组数据的K个均匀抽样
要求:
- 仅扫描数据一次
- 空间复杂性为O(K)【和抽样大小有关,和整个数据量无关,不可把所有数据都放在内存里进行抽样】
- 扫描到数据的前n个数字时(n>K),保存当前已扫描数据的K个均匀抽样
针对此种需求,水库抽样法应运而生
算法步骤:
- 申请一个长度为K的数组A保存抽样
- 保存首先接收到的K个元素
- 当接收到第 i 个新元素 t 时,以 K / i 的概率随机替换A中的元素(即生成 [ 1, i ]间随机数j,若 j ≤ K,则以 t 替换 A[ j ])
算法性质:
- 该采样是均匀的。
即在任何时候,接收到的大于K的n个数据,选出来的数都保证是均匀采样。
第 i 个数据接收到的时候是以 K / i 的概率在A当中,在接收到第 i + 1的时候,第 i 个数据还能保存在A当中的概率为(1 - (1 / i+1) ),因为在接收到第 i + 1个数的时候,第 i + 1 个数要以 K/(i +1)的概率随机替换A中的元素,而元素 i 在这一步被选出来替换的概率是1/K,这两个相乘为 1 / ( i+1),便为第 i 个元素被选出的概率,所以(1 - (1 / i+1) )为在第 i+1 个元素接收到的时候,第 i 个数在数组的概率。以此类推,在第 i+2 个数到来的时候,第 i 个数仍然在数组中的概率为(1 - (1 / i+2) ),当接受到第n个数的时候,第 i 个数保存在数组的概率为(1 - (1 / n) )。只有这些事件都发生了,那么才能在接收到第 n 个数的时候,第 i 个数还保留在抽样当中,所以其保留在抽样当中的概率为这些事件相乘。
- 空间复杂性为O(K)
在整个的算法处理当中,我们只需要一个长度为K的数组保存抽样,剩下的计算概率的啥的空间都是常数的,因此空间复杂度为O(K)