Reversoir sampling

1.背景

最近Leetcode上答题时,遇到了Reservoir sampling 算法。挺有趣的。记录一下。

首先说一下,遇到的题目:

Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability of being chosen.

根据上面的题目,我们立即可以想到,将linked list所有元素放入ArrayList中,然后用一个随机数,取出某一个元素。Bingo!问题解决。

不过,当题目中的Linkedlist中Node数目非常庞大时,也许上亿,那么怎么办,如果将所有元素放入内存之后,内存会耗光。

(工作中,当数据量级变大时,处理的方法,也要随之变化)

我们可以是使用蓄水池算法(reservoir sampling)来解决。

 

2.什么是reservoir sampling

它就是解决一个随机采样的问题。它很简单,常见,但是,非常慢。

下面是它的算法:

(* S has items to sample, R will contain the result *)
ReservoirSample(S[1..n], R[1..k])
  // fill the reservoir array
  for i := 1 to k
      R[i] := S[i]

  // replace elements with gradually decreasing probability
  for i := k+1 to n
    (* randomInteger(a, b) generates a uniform integer from the inclusive range {a, ..., b} *)
    j := randomInteger(1, i)
    if j <= k
        R[j] := S[i]

时间复杂度:O(n), 空间复杂度为 1.

接下来,是我对上面算法的推导(为什么这个算法可以确保每个元素被取出的概率是一样的):

(短短的几行推导,我也是花了些时间才弄懂得,惭愧。但是,要感谢自己,感谢自己没有放弃。)

 

这个算法最大的问题是,如果数据量非常大的话,那么你惨了,你要等好久,才能等到算法执行完毕。

wiki中,还给出了许多优化,优化的算法,有兴趣的可以深入研究。

 

References:

https://en.wikipedia.org/wiki/Reservoir_sampling

https://leetcode.com/problems/linked-list-random-node/

 

posted @ 2021-04-12 22:32  Mr.袋鼠  阅读(47)  评论(0编辑  收藏  举报