带权重的随机选择算法

带权重的随机选择算法

https://leetcode.cn/problems/random-pick-with-weight/

  1. 题干:

    给你一个 下标从 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%)。

  2. 思路:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-02JiwU6I-1653638973816)(C:\Users\lebronHArden\AppData\Roaming\Typora\typora-user-images\image-20220527153827623.png)]

    可以这么理解:

    因为原数组的每个下标处的元素除以所有元素加起来的和就是取到其下标的概率值,或者可以换句话说:每个下标的自身的值越大,那就说明取到这个下标的概率就越大,或者我们可以理解成每个数字都代表一定的区间长度,这个区间长度越长,则取到这个数字对应下标的概率就越大!

    如上图:

    0下标的数字1:可对应1个格子

    1下标的数字3:可对应3个格子

    2下标的数字2:可对应2个格子

    3下标的数字1:可对应1个格子

    所以我们可以玩一个游戏,在1-7范围内随机的扔石子,这个石子落到各个颜色的区间里头时(只会落在格子里),我们就取出这个颜色对应的下标即为所求.

    模拟玩游戏,需要借助于前缀和,对应上图:在前缀和里随机生成1-7的数字,即可有自己对应的下标:

    扔到格子1:对应原数组的0下标

    扔到格子2,3,4:对应原数组的1下标

    扔到格子的5,6:对应原数组的2下标

    扔到格子的7:对应原数组的3下标

    至此游戏结束,我们也得了自己拿到的下标


    代码:

    class Solution {
        private int[] presum;
        private Random random = new Random();
        public Solution(int[] w) {
            int n=w.length;
            presum=new int[n+1];
            presum[0]=0;
            for(int i=1;i<=n;i++){
                presum[i]=presum[i-1]+w[i-1];
            }
        }
    
        public int pickIndex() {
            int len=presum.length;
            int pick=random.nextInt(presum[len-1])+1;//生成[1,presum[n-1]]的随机整数
            //在前缀和数组中找出大于等于这个随机数的第一个元素,即左边界(二分查找)
            int index=left_bound(presum,pick)-1;
            return index;
        }
    
        // 搜索左侧边界的二分搜索
        private int left_bound(int[] nums, int target) {
            if (nums.length == 0) return -1;
            int left = 0, right = nums.length;
            while (left < right) {
                int mid = left + (right - left) / 2;
                if (nums[mid] == target) {
                    right = mid;
                } else if (nums[mid] < target) {
                    left = mid + 1;
                } else if (nums[mid] > target) {
                    right = mid;
                }
            }
            return left;
        }
    }
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值