题目:
给你一个 下标从 0 开始 的正整数数组 w ,其中 w[i] 代表第 i 个下标的权重。
请你实现一个函数 pickIndex ,
它可以 随机地 从范围 [0, w.length - 1] 内(含 0 和 w.length - 1)选出并返回一个下标。
选取下标 i 的 概率 为 w[i] / sum(w) 。
例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),
而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。
------------------------
示例 1:
输入:
["Solution","pickIndex"]
[[[1]],[]]
输出:
[null,0]
解释:
Solution solution = new Solution([1]);
solution.pickIndex();
示例 2:
输入:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
输出:
[null,1,1,1,1,0]
解释:
Solution solution = new Solution([1, 3]);
solution.pickIndex();
solution.pickIndex();
solution.pickIndex();
solution.pickIndex();
solution.pickIndex();
由于这是一个随机问题,允许多个答案,因此下列输出都可以被认为是正确的:
[null,1,1,1,1,0]
[null,1,1,1,1,1]
[null,1,1,1,0,0]
[null,1,1,1,0,1]
[null,1,0,1,0,0]
......
诸若此类。
提示:
1 <= w.length <= 104
1 <= w[i] <= 105
pickIndex 将被调用不超过 104 次
-----------------
思路:前缀和 + 二分查找
这道题是非常的典型的随机权重问题,应用的场景非常多。
比如,我们经常说的负载均衡策略,同一个服务有多台服务器,一般来说有哪些策略这些服务器呢?
一般来说有:
1. 轮询;2.随机;3.轮询+权重;4.随机+权重;5.一致性Hash;6.普通hash;7.最小连接数;
8.最短响应时间;等等。
随机权重就是其中的一种。
我们先考虑3台服务器的情况,假设他们的权重分别为:[3, 5, 2],我们应该怎么来搞呢?
通常的做法,
是生成一个10以内的随机数,如果这个随机数小于3,就选第一台机器;
如果这个随机数小于8(3 + 5),就选第二台机器;
否则,就选第三台。
这其实就是前缀和的思想,
我们从第二台开始与随机数的比较是需要加上前面机器的权重的,这样更好计算。
查找的过程,我们是要查找生成的随机数在有序数组(前缀和是递增的)中的位置,
所以,显然我们可以使用二分查找来加速。
-----------------------------
先计算前缀和数组-----获取随机数----二分查找 前缀和数组和随机数
class Solution {
private Random random;
private int[] preSum;
public Solution(int[] w) {
this.random = new Random();
this.preSum = new int[w.length];
this.preSum[0] = w[0];
for (int i = 1; i < w.length; i++) {
this.preSum[i] = this.preSum[i - 1] + w[i];
}
}
public int pickIndex() {
int max = preSum[preSum.length - 1];
int rand = random.nextInt(max) + 1;
return binarySearch(preSum, rand);
}
private int binarySearch(int[] preSum, int rand) {
int left = 0;
int right = preSum.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
if (rand < preSum[mid]) {
right = mid - 1;
}else if (rand > preSum[mid]){
left = mid + 1;
}else {
return mid;
}
}
return left;
}
}
LC