按权重随机选择(leetcode 528)
Posted 恋喵大鲤鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了按权重随机选择(leetcode 528)相关的知识,希望对你有一定的参考价值。
文章目录
1.问题描述
给你一个下标从 0 开始的正整数数组 w ,其中 w[i] 代表第 i 个下标的权重。
请你实现一个函数 pickIndex ,它可以 随机地 从范围 [0, w.length - 1] 内(含 0 和 w.length - 1)选出并返回一个下标。选取下标 i 的 概率 为 w[i] / sum(w) 。
要求: 返回的随机函数的时间复杂度不超过 O(logn)。
例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即 25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即 75%)。
2.难度等级
medium。
3.热门指数
★★★★☆
出题公司:萌时科技。
4.解题思路
可以使用“前缀和 + 二分查找”来实现。
设数组 w 的权重之和为 total。根据题目的要求,我们可以看成将 [1,total] 范围内的所有整数分成 n 个部分(其中 n 是数组 w 的长度)。第 i 个部分恰好包含 w[i] 个整数,并且这 n 个部分两两的交集为空。随后我们在 [1,total] 范围内随机生成一个整数 x,如果整数 x 被包含在第 i 个部分内,我们就返回 i。
例如对于数组 w[1,3,5,6],计算其累计的前缀和数组为 [1,4,9,15],然后随机产生一个 [1,15] 之间的随机数。
如果随机数落在 [1,1],应该找到的值为 1, 对应元素下标为 0,
如果随机数落在 [2,4] 区间,应该找到值 4, 对应元素下标为 1,
如果随机数落在 [5,9] 区间,应该找到值 9, 对应元素下标为 2,
如果随机数落在 [10,15],应该找到值 15, 对应元素下标为 3,
如果使用顺序遍历来查找元素效率较低, 由于前缀和数组是有序的, 所以可以使用二分法查找。
注意:需要先确定待查找的元素在数组中的位置 , 根据题意, 前缀和数组中第一个 >= x 的元素就是待查找的元素,确定了这一点才能写出正确的二分查找。
复杂度分析:
时间复杂度:初始化的时间复杂度为 O(n),每次选择的时间复杂度为 O(logn),其中 n 是数组 w 的长度。
空间复杂度:O(n),即前缀和数组需要使用的空间。
5.实现示例
5.1 C++
#include <random>
#include <vector>
#include <numeric>
#include <iterator>
using namespace std;
class Solution
private:
mt19937 gen;
uniform_int_distribution<int> dis;
vector<int> pre;
public:
Solution(vector<int>& w): gen(random_device()), dis(1, accumulate(w.begin(), w.end(), 0))
partial_sum(w.begin(), w.end(), back_inserter(pre));
int pickIndex()
int x = dis(gen);
return lower_bound(pre.begin(), pre.end(), x) - pre.begin();
;
5.2 Golang
import (
"sort"
"math/rand"
)
type Solution struct
pre []int
func Constructor(w []int) Solution
for i := 1; i < len(w); i++
w[i] += w[i-1]
return Solutionw
func (s *Solution) PickIndex() int
x := rand.Intn(s.pre[len(s.pre)-1]) + 1
return sort.SearchInts(s.pre, x)
参考文献
以上是关于按权重随机选择(leetcode 528)的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 528 按权重随机选择[前缀和 二分法] HERODING的LeetCode之路
LeetCode 1480. 一维数组的动态和 / 1588. 所有奇数长度子数组的和 / 528. 按权重随机选择(随机化)