Random选编 前缀和数组 + 二分查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution { int [] preSum; int n; int total; public Solution (int [] w) { n=w.length; preSum=new int [n]; preSum[0 ]=w[0 ]; for (int i = 1 ; i < n; i++) { preSum[i]=preSum[i-1 ]+w[i]; } total= Arrays.stream(w).sum(); } public int pickIndex () { int x=(int )(Math.random()*total)+1 ; return binarySearch(x); } int binarySearch (int target) { int left=0 ; int right=n; while (left<right){ int mid=left+(right-left)/2 ; if (preSum[mid]<target){ left=mid+1 ; }else { right=mid; } } return left; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class LeetCode382 { class Solution { int count; ListNode head; public Solution (ListNode head) { count=0 ; this .head=head; ListNode p=head; while (p!=null ){ p=p.next; count++; } } public int getRandom () { int index=(int )(Math.random()*count); ListNode p=head; while (index-->0 ){ p=p.next; } return p.val; } } }
法二:reservoir sampling https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247490892&idx=1&sn=c1fe373edc88142cbabd383ef3c0669b
总的样本数量未知 ,从所有样本中抽取若干个,要求每个样本被抽到的概率相等 。
从前往后处理每个样本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { Random random; ListNode head; public Solution (ListNode head) { this .head=head; this .random=new Random (); } public int getRandom () { int res=0 ; int count=0 ; ListNode p=head; while (p!=null ){ count++; int ran=random.nextInt(count); if (ran==0 ){ res=p.val; } p=p.next; } return res; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { Random random; Map<Integer, List<Integer>> map; public Solution (int [] nums) { random=new Random (); map=new HashMap <>(); for (int i = 0 ; i < nums.length; i++) { if (!map.containsKey(nums[i])){ map.put(nums[i],new ArrayList <>()); } map.get(nums[i]).add(i); } } public int pick (int target) { List<Integer> list = map.get(target); int size=list.size(); return list.get(random.nextInt(size)); } }
法二:reservoir sampling 时间换空间,会超时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { int [] nums; Random random; public Solution (int [] nums) { this .nums=nums; this .random=new Random (); } public int pick (int target) { int count=0 ; int res=0 ; for (int i=0 ; i<nums.length; i++) { if (nums[i]==target){ count++; if (random.nextInt(count)==0 ){ res=i; } } } return res; } }
32/35 直接法错误!居然还能过32个测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { int [][] rects; Random random; public Solution (int [][] rects) { this .rects=rects; this .random=new Random (); } public int [] pick() { int n=rects.length; int i = random.nextInt(n); int x1=rects[i][0 ]; int x2=rects[i][2 ]; int y1=rects[i][1 ]; int y2=rects[i][3 ]; int x=(int )(Math.random()*(x2-x1+1 ))+x1; int y=(int )(Math.random()*(y2-y1+1 ))+y1; return new int []{x,y}; } }
蓄水池采样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution { int [][] rects; Random random; public Solution (int [][] rects) { this .rects=rects; this .random=new Random (); } public int [] pick() { int sum=0 ; int index=0 ; for (int i=0 ; i< rects.length; i++) { int [] rect=rects[i]; int area=(rect[2 ]-rect[0 ]+1 )*(rect[3 ]-rect[1 ]+1 ); sum+=area; if (random.nextInt(sum)<area){ index=i; } } int [] arr=rects[index]; int x1=arr[0 ]; int y1=arr[1 ]; int x2=arr[2 ]; int y2=arr[3 ]; return new int []{random.nextInt(x2-x1+1 )+x1,random.nextInt(y2-y1+1 )+y1}; } }
注意:
Math.random(): [0,1)
random().nextInt(x): [0,x)
random().nextDouble(): [0.0d,1.0d)
random().nextFloat(): [0.0f,1.0f]
法一:rejection sampling: 拒绝采样 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { double r; double x_center; double y_center; public Solution (double radius, double x_center, double y_center) { r=radius; this .x_center=x_center; this .y_center=y_center; } public double [] randPoint() { while (true ){ double dx=Math.random()*2 *r-r; double dy=Math.random()*2 *r-r; if (dx*dx+dy*dy<=r*r){ return new double []{x_center+dx,y_center+dy}; } } } }
法二:几何法 随机生成len
和angle
,利用三角函数计算出dx
,dy
注意,随机生成len
时不能直接 在[0,r)间random,因为这样不是对每个点等概率的!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { Random random; double r; double x; double y; public Solution (double radius, double x_center, double y_center) { r=radius; x=x_center; y=y_center; random=new Random (); } public double [] randPoint() { double len=Math.sqrt(random.nextDouble()*r*r); double angle=random.nextDouble()*Math.PI*2 ; double dx=len*Math.cos(angle); double dy=len*Math.sin(angle); return new double []{x+dx,y+dy}; } }
380. Insert Delete GetRandom O(1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class RandomizedSet { List<Integer> list; Map<Integer,Integer> keyToIndex; Random rand; public RandomizedSet () { list=new ArrayList <>(); keyToIndex=new HashMap <>(); rand=new Random (); } public boolean insert (int val) { if (keyToIndex.containsKey(val)){ return false ; } keyToIndex.put(val,list.size()); list.add(val); return true ; } public boolean remove (int val) { if (!keyToIndex.containsKey(val)){ return false ; } int idx=keyToIndex.get(val); int last=list.get(list.size()-1 ); list.set(idx,last); list.remove(list.size()-1 ); keyToIndex.put(last,idx); keyToIndex.remove(val); return true ; } public int getRandom () { int idx=0 +rand.nextInt(list.size()); return list.get(idx); } }
why wrong?!
为何每次iterator.next()都返回相同值?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class RandomizedSet { Set<Integer> set; public RandomizedSet () { set=new HashSet <>(); } public boolean insert (int val) { if (set.contains(val)){ return false ; } set.add(val); return true ; } public boolean remove (int val) { if (!set.contains(val)){ return false ; } set.remove(val); return true ; } public int getRandom () { return set.iterator().next(); } }