2018-03-05 14:06:40
问题描述:给出一个数据流,这个数据流的长度很大或者未知。并且对该数据流中数据只能访问一次。请写出一个随机选择算法,使得数据流中所有数据被选中的概率相等。
问题求解:如果是长度已知或者有限的问题,那么可以使用朴素的方法,先遍历一遍得到的长度。然后在得到长度后可以使用随机算法得到一个随机的index。
但是本题已经明确指出数据流长度很大或者未知,也就是说只能遍历一次,而且要保证每个数被挑选的概率相等。
标准解法是使用Reservoir Sampling算法,该算法由Knuth的学生在斯坦福读计算机博士时想出来。
算法描述:
相关问题:
-
382. Linked List Random Node
问题描述:
问题求解:
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
问题描述:
问题求解:
如果仅存在一个数,那么将之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;}
}