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; } }