LeetCode528 Random Pick with Weight

the description of this problem is kind of vague.
I will rephrase this problem in the following way:

given [1, 9]
now we have possibilities of 1/10 to choose index 0
and we have possibilities of 9/10 to choose index 1.

so each time we call pickIndex() method, it will return the index based on the possibility of weight/total_weight.
in more concise words, we need to choose from 0 to nums.length-1 based in its weight.

idea:
if you go straight to this problem, you will struck into nothing.

now, think about this, how can we get the random number in Java? Math.random() method, which can get us a random double number from 0 to 1. and now, we would love to get a random number from 0 to nums.length - 1. that’s simple, we only need to use (nums.length - 1)*Math.random().
but we want more than that, we don’t want a totally random number, instead, we would love a random index number based on proportion.

So we use prefixSum array to solve this problem.
let assume we have [2, 1, 4, 3, 9, 2]
the prefix sum will be [2, 3, 7, 10, 19, 21]
so it is like we have 0 to 21 cubic boxes, so we use 21 * Math.random(), let say the result is 13, then 13 is larger than 10 and less than 19, so this pickIndex will choose index = 5.
More improvement for this algorithm? sure, we can take binary search to find the index instead of linear search. (but pay attention, due to prefixSum array here is a sorted array, so we can use binary search. that means if we contains positive and negative number in out original array, the prefix is not monotonic)

class Solution {
    
    private int[] prefixSum;

    public Solution(int[] w) {
        prefixSum = new int[w.length];
        prefixSum[0] = w[0];
        for (int i = 1; i < w.length; i++) {
            prefixSum[i] = prefixSum[i - 1] + w[i];
        }
        
    }
    
    public int pickIndex() {
        
        double rand = prefixSum[prefixSum.length - 1] * Math.random();
        System.out.println(rand);
      
        int l = 0;
        int r = prefixSum.length - 1;
        //find the first index that no less than rand
        while (l < r) {
            int mid = (r - l) / 2 + l;
            if (prefixSum[mid] < rand) {
                l = mid + 1;
            } else {
                r = mid;
            }
        }

        return r;
    }
}
posted @ 2020-06-07 02:38  EvanMeetTheWorld  阅读(15)  评论(0)    收藏  举报