Leetcode——按权重随机选择

Posted Yawn,

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode——按权重随机选择相关的知识,希望对你有一定的参考价值。

1. 按权重随机选择


(1)朴素思路

容易想到的思路就是将每个下标复制其权重份,然后随机选取一个:

如上图所示,下标 0 处权重为 2,那么我们就往一个数组中存入 2 个值为 0(对应下标值) 的节点;同理,我们存入 1 个值为 1 的节点,和 4 个值为 2 的节点

(2)前缀和优化

但是,根据题目给出的数据范围,最坏情况下要存入109个节点,说明该解法是不行的!但是,我们可以参考这个暴力的思路,来进行一定的优化。
上面思路的原理是,将数组分为 n 份,每份的权重为 w[i],如下图所示:

那么,我们只需要按照从小到大的顺序依次在数轴上划分每个部分即可,例如对于上面的 w 数组,第一份对应到数轴上的 [0, 2]区间,第二份对应到 [2, 3] 区间,第三份对应到 [3, 7]区间,然后,我们在 [0, 7] 区间随机选数就可以了。

在上述划分过程结束后,选数之后,要判断当前数字处在 w 数组哪一份内,也就是说,我们要提前维护好每一份对应的区间,而这个过程可以利用前缀和来解决

在代码中,我们维护一个数组presum 以及一个前缀和数组 sum 来进行前缀和的维护和查询。

  • 由于x是随机选自9个部分,落在每个部分的概率都是1/9,当某个区间越大,x落在这个区间的概率也越大,这样就达到了按照权重取数的这一目的
  • 总权重为数组的和,9
  • 从这个9个中随机选择一个部分
  • 然后使用二分法判断其属于哪个的区间

思路:

思路:前缀和数组 + 二分查找
    举例:
        给定数组是【3,1,2】——>
      前缀和数组【0,3,4,6】——>
      在【0,6)范围生成一个随机数r ——>
          如果r=012则返回index0
          如果r=3则返回index1
          如果r=45则返回index2
    前缀和数组:
        preSum[0]=0;
        preSum[i]=preSum[i-1]+nums[i-1];
    取随机值:
        int r = r.nextInt(preSum[length-1])
    二分查找:
        left = preSum的0
        right = preSum的length-1
        while(left <=right )
            mid = left + (right -left)/2
            如果preSum[mid] == r,则返回mid。
            如果preSum[mid] > r,则想办法把mid左移,即end = mid - 1;
            如果preSum[mid] < r,则先把此时mid记下来作为备选,然后想办法把mid右移,即start = mid + 1;
        循环的退出条件1:找到了preSum中的恰巧正好等于r的index,这个index == w的index,所以最后返回这个index即可。
        或
        循环的退出条件2:找到了preSum中的、比r小的index们、中的最大的index,这个index == w的index,所以最后返回这个index即可。

代码:

class Solution {
    int[] preSum;
    int sum;
    Random random;
    
    public Solution(int[] w) {
        preSum = new int[w.length];
        preSum[0] = w[0];
        sum = w[0];
        // 求总和 + 前缀和
        for (int i = 1; i < w.length; ++i) {
            preSum[i] = preSum[i - 1] + w[i];
            sum += w[i];
        }
        random = new Random();
    }
    
    public int pickIndex() {
        // 取出一个随机数
        int x = random.nextInt(sum);

        // 二分查找,目的是找到x落在了那个下标的区间里
        int left = 0, right = preSum.length;
        while (left < right) {
            int mid = (left + right) >>> 1;
            if (preSum[mid] <= x) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
}

以上是关于Leetcode——按权重随机选择的主要内容,如果未能解决你的问题,请参考以下文章

《LeetCode之每日一题》:135.按权重随机选择

leetcode 528 按权重随机选择

按权重随机选择(leetcode 528)

按权重随机选择(leetcode 528)

LeetCode 1480. 一维数组的动态和 / 1588. 所有奇数长度子数组的和 / 528. 按权重随机选择(随机化)

按权重随机选择--计算机系统调度策略之一:前缀和+二分