蓄水池抽样算法 Reservoir Sampling

时间:2021-02-12 17:17:23

2018-03-05 14:06:40

问题描述:给出一个数据流,这个数据流的长度很大或者未知。并且对该数据流中数据只能访问一次。请写出一个随机选择算法,使得数据流中所有数据被选中的概率相等。

问题求解:如果是长度已知或者有限的问题,那么可以使用朴素的方法,先遍历一遍得到的长度。然后在得到长度后可以使用随机算法得到一个随机的index。

但是本题已经明确指出数据流长度很大或者未知,也就是说只能遍历一次,而且要保证每个数被挑选的概率相等。

标准解法是使用Reservoir Sampling算法,该算法由Knuth的学生在斯坦福读计算机博士时想出来。

算法描述:

蓄水池抽样算法 Reservoir Sampling

相关问题:

  • 382. Linked List Random Node

问题描述

蓄水池抽样算法 Reservoir Sampling

问题求解:

public class Solution {
ListNode head;
Random rand; /** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
public Solution(ListNode head) {
this.head = head;
this.rand = new Random();
} /** Returns a random node's value. */
public int getRandom() {
int k = 1;
ListNode cur = head;
List<Integer> reservoir = new ArrayList<>();
int i = 0;
while (i < k && cur != null) {
reservoir.add(cur.val);
cur = cur.next;
i++;
}
i++;
while (cur != null) {
if (rand.nextInt(i) < k) {
reservoir.set(rand.nextInt(k), cur.val);
}
i++;
cur = cur.next;
}
return reservoir.get(0);
}
}
  • 398. Random Pick Index

问题描述:

蓄水池抽样算法 Reservoir Sampling

问题求解:

如果仅存在一个数,那么将之index返回,如果存在多个数,其index的返回值需要是等概率的,也就是说对于k个相同的数,我们需要每个index的返回概率为1/k。根据蓄水池算法,我们首先要建立一个大小为1的池子,然后对每个出现的target的index以当前出现个数的概率选择他,然后从池中随机挑选一个数来与它交换,由于池中仅有一个值,因此只需要将res的值变为挑选值即可。

public class RandomPickIndex {
int[] nums;
Random ran; public RandomPickIndex(int[] nums) {
this.nums = nums;
ran = new Random();
} public int pick(int target) {
int res = -1;
int cnt = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != target) continue;
if (ran.nextInt(++cnt) == 0) res = i;
}
return res;
}
}