随机权值选择算法

按权值随机选择算法

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

输入:
["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]
......
诸若此类。

来源:力扣(LeetCode

方案一:前缀和+二分查找

把待选择的数放平到线段上

如:

待选择的数组:[ 1,3,2,4,2 ]

按数字权值大小在线段上分布放平:

1___3__2____4__2

求出前缀和:

1___4__6____10__12

不难发现:现在前缀和数组可以表示,每一个数字占所有可能选择的几率(线段长度除以总长度)

那如果选择的是7呢?

这时,比如数组左边抛出一个小球,随机落到7点处,即6-10之间,其实已经是10的概率分布范围了。这时需要二分查找,查找当前数字的最左值,即10的位置。

class Solution {

    int nums[];//前缀和数组

    public Solution(int[] w) {
        int n = w.length;
        nums = new int[n + 1];
        //第0位为占位符,可以理解为小球抛出点
        for (int i = 1; i <= n; i++) nums[i] = nums[i - 1] + w[i - 1];
    }

    public int pickIndex() {
        int n = nums.length;
        int t = (int) (Math.random() * nums[n - 1]) + 1;
        //二分法,获取最左边界值
        int l = 1;
        int r = n - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] < t) l = mid;
            else r = mid - 1;
        }
        return nums[r] < t ? r : r - 1;
    }
}
Licensed under CC BY-NC-SA 4.0
Last updated on Mar 23, 2024 06:11 UTC
让过去的过去,给时间点时间
Built with Hugo
Theme Stack designed by Jimmy