一刷269-前缀和 + 二分模块-528. 按权重随机选择(m)(同:剑指 Offer II 071. 按权重生成随机数)

题目:
给你一个 下标从 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(); // 返回 0,因为数组中只有一个元素,所以唯一的选择是返回下标 0。
示例 2:

输入:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
输出:
[null,1,1,1,1,0]
解释:
Solution solution = new Solution([1, 3]);
solution.pickIndex(); // 返回 1,返回下标 1,返回该下标概率为 3/4 。
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 0,返回下标 0,返回该下标概率为 1/4 。

由于这是一个随机问题,允许多个答案,因此下列输出都可以被认为是正确的:
[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.一致性Hash6.普通hash;7.最小连接数;
8.最短响应时间;等等。
随机权重就是其中的一种。

我们先考虑3台服务器的情况,假设他们的权重分别为:[3, 5, 2],我们应该怎么来搞呢?
通常的做法,
是生成一个10以内的随机数,如果这个随机数小于3,就选第一台机器;
如果这个随机数小于83 + 5),就选第二台机器;
否则,就选第三台。

这其实就是前缀和的思想,
我们从第二台开始与随机数的比较是需要加上前面机器的权重的,这样更好计算。

查找的过程,我们是要查找生成的随机数在有序数组(前缀和是递增的)中的位置,
所以,显然我们可以使用二分查找来加速。
-----------------------------
先计算前缀和数组-----获取随机数----二分查找 前缀和数组和随机数
class Solution {
    private Random 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;//生成的随机数不能包含0, 否则部分用例过不了
        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;
    }
}
/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(w);
 * int param_1 = obj.pickIndex();
 */

LC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值