从输入流中随机抽取k个元素
有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从这个输入流中随机取得m个记录。
思路:
我们可以用Fisher-Yates随机排列算法解决该问题。用大小为m的数组arr[0:m-1]来保存随机抽取的元素,arr[0:m-1]逐步初始化为输入流的前m个元素的一个随机排列。
对于输入流中的第k (k>m)个元素,随机生成一个[0,k-1]区间内的整数i,如果此随机整数i小于等于m,那么就用第k个元素覆盖掉arr[i-1],否则,丢弃第k个元素。
注意,每从输入流中提取一个新元素,我们都要随机生成一个整数,而此整数的有可能出现的范围都要加1.
http://peizhyi.iteye.com/blog/1434391 给出的算法是错误的,这个算法有两个问题:
- 随机流的大小未知,所以所谓的“假设有n条记录”是不成立的;
- 即便随机流的大小是n,但是每次从[0,n-1]的范围随机生成一个整数,将此整数作为元素在arr中的位置,这样找出的m个元素并非等概率的m个元素。
为什么http://peizhyi.iteye.com/blog/1434391 给出的算法不能生成一个等概率的抽取呢?原因很简单:由于数组arr被初始化为输入流的前m个元素,前m个元素是以100%的概率被插入到数组arr中的。但是,对于输入流中往后的元素,如果从[0,n-1]中随机生成它们的位置,显然它们只能以m/n的概率被插入到数组arr中,用这种方式生成的随机抽取显然就不是等概率的了。