在 LeetCode 上做到两道非常有意思的题目,382 和 398 题,关于水塘抽样算法(Reservoir Sampling),本质上是一种随机概率算法。

如果有n个元素,每个元素被选中的概率都是1/n,不可以有统计意义上的偏差。
一般的想法就是,我先遍历一遍链表,得到链表的总长度n,再生成一个[1,n]之间的随机数为索引,然后找到索引对应的节点,不就是一个随机的节点了吗?

但题目说了,只能遍历一次,意味着这种思路不可行。题目还可以再泛化,给一个未知长度的序列,如何在其中随机地选择k个元素?想要解决这个问题,就需要著名的水塘抽样算法了。

如何从包含未知大小的数据流中随机选取k个数据,并且要保证每个数据被抽取到的概率相等?
比如说你现在你有 5 个元素,你已经随机选取了其中的某个元素a作为结果,但是现在再给你一个新元素b,你应该留着a还是将b作为结果呢,以什么逻辑选择a和b呢,怎么证明你的选择方法在概率上是公平的。
leetcode382.398蓄水池抽样算法

遇到第i个元素时,应该有1/i的概率选择该元素,1 - 1/i的概率保持原有的选择。
leetcode382.398蓄水池抽样算法

同理,如果要随机选择k个数,只要在第i个元素处以k/i的概率选择该元素,以1 - k/i的概率保持原有选择即可。
当 k = m 时
也就是说,我们每次能读m个数据。
leetcode382.398蓄水池抽样算法

相关文章: