Problem
Given an array w of positive integers, where w[i] describes the weight of index i, write a function pickIndex which randomly picks an index in proportion to its weight.
Note:
1 <= w.length <= 10000
1 <= w[i] <= 10^5
pickIndex will be called at most 10000 times.
Example 1:
Input:
["Solution","pickIndex"]
[[[1]],[]]
Output: [null,0]
Example 2:
Input:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
Output: [null,0,1,1,1,0]
Explanation of Input Syntax:
The input is two lists: the subroutines called and their arguments. Solution's constructor has one argument, the array w. pickIndex has no arguments. Arguments are always wrapped with a list, even if there aren't any.
Solution
- create an array
sum
, to save sum of weights - create a Random random to create random in a range
- the range should be [1, maxValue] as [1, sum[len-1]]
- use
Random.nextInt(maxValue) + 1
to get a random num of the range - use binary search to find the index of random in
sum
array
class Solution {
int[] sums;
int max;
Random random;
public Solution(int[] w) {
sums = new int[w.length];
sums[0] = w[0];
for (int i = 1; i < sums.length; i++) {
sums[i] = sums[i-1]+w[i];
}
max = sums[sums.length-1];
random = new Random();
}
public int pickIndex() {
int rand = random.nextInt(max)+1;
int index = findIndex(sums, rand);
return index;
}
private int findIndex(int[] nums, int k) {
int start = 0, end = nums.length-1;
while (start+1 < end) {
int mid = start+(end-start)/2;
if (nums[mid] == k) return mid;
else if (nums[mid] < k) start = mid+1;
else end = mid-1;
}
if (k > nums[end]) return end+1;
else if (k > nums[start]) return start+1;
else return start;
}
}