目录
一、什么是蓄水池抽样
大数据流中的随机抽样问题:当内存无法加载全部数据时,如何从包含未知大小的数据流中随机选取k个数据,并且要保证每个数据被抽取到的概率相等。蓄水池抽样算法就是用来解决该类问题的算法。
二、蓄水池抽样算法[1]
在不知道数据流大小(长度)的情况下,我们需要从数据流中随机抽取一个数据,每一个数据被抽到的概率是相同的。
遍历数据流,共计n次抽样(n为数据流大小,未知),我们假设第i次抽样中,从数组[0, i)中抽样一个整数,如果该整数等于0,那么保留该位置数据,否则数据不变。这样能保证每个数据被抽中的概率都是1/n。
证明:P(第i个数据成为最后被选中的数据)
=P(第i次随机抽样的值=0)×P(第i+1次随机抽样的值≠0)×…×P(第n次随机抽样的值≠0)
3.LeetCode蓄水池抽样经典算法题
384. 打乱数组 该题采用Knuth 洗牌算法,非蓄水池
这里有一个大佬从两个角度讲解这个算法的等概率性[4]
洗牌算法[2]
共有 nn个不同的数,根据每个位置能够选择什么数,共有 n! 种组合。
题目要求每次调用 shuffle 时等概率返回某个方案,或者说每个元素都够等概率出现在每个位置中。
我们可以使用 Knuth 洗牌算法,在 O(n) 复杂度内等概率返回某个方案。
具体的,我们从前往后尝试填充 [0,n−1]该填入什么数时,通过随机当前下标与(剩余的)哪个下标进行值交换来实现。
对于下标 x而言,我们从[x,n−1] 中随机出一个位置与 x 进行值交换,当所有位置都进行这样的处理后,我们便得到了一个公平的洗牌方案。
对于下标为 0位置,从 [0,n−1] 随机一个位置进行交换,共有 n 种选择;下标为 1 的位置,从 [1,n−1] 随机一个位置进行交换,共有 n−1 种选择 ...
[3]
# 382. 链表随机节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def __init__(self, head: Optional[ListNode]):
self.head=head
def getRandom(self) -> int:
node=self.head
#notice,when i==1, the rand result must be 0 and the ans must be the val of the node.
ans=0
#i represents the times of the sampling
i=1
while node:
if randrange(i)==0:
ans=node.val
i+=1
node=node.next
return ans
#ps: the easist way: to make a list to save the vals
# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()
引用
[1]https://leetcode.cn/problems/linked-list-random-node/solutions/1210211/lian-biao-sui-ji-jie-dian-by-leetcode-so-x6it/[2]https://leetcode.cn/problems/shuffle-an-array/solutions/1114958/gong-shui-san-xie-xi-pai-suan-fa-yun-yon-0qmy/[3]https://leetcode.cn/problems/shuffle-an-array/solutions/672603/da-luan-shu-zu-yi-ding-gong-ping-de-xi-p-21iy/[4]https://leetcode.cn/problems/shuffle-an-array/solutions/1115175/wei-rao-li-lun-jing-dian-xi-pai-suan-fa-11ona/