528.按权重随机选择
题目描述
给定一个正整数数组w,其中w[i]代表下标i的权重(下标从0开始),请写一个函数pickIndex,它可以随机地获取下标o,选取下标i的概率与w[i]成正比。
例如,对于w = [1, 3],挑选下标0的概率为1/(1+3)=0.25(即25%),而选取下标1的概率为3/(1+3)=0.75(即75%)。
也就是说,选取下标i的概率为w[i] / sum(w)。
示例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]
…
诸如此类。
提示
- 1 <= w.length <= 10000
- 1 <= w[i] <= 105
- pickIndex将被调用不超过10000次。
思路:前缀和+二分查找
重新构建列表,从列表随机选数
题目比较不好理解,直白的讲就相当于有一个很大的列表,然后对于每个坐标i,都有w[i]个i在这个数组中,最后随即从数组里面选择一个。但是这样的思路比较浪费空间。
class Solution:
def __init__(self, w: List[int]):
total = sum(w)
self.list = [i for i in range(len(w))]
self.weight = [0] * len(w)
for i, num in enumerate(w):
self.weight[i] += float(num/total)
self.picks = random.choices(self.list, self.weight, k=10000)
self.idx = -1
def pickIndex(self) -> int:
self.idx += 1
return self.picks[self.idx]
# Your Solution object will be instantiated and called as such:
# obj = Solution(w)
# param_1 = obj.pickIndex()
前缀和+二分查找
假定第一个坐标有k0个,那么占用的区间为[1, k0],假定第二个坐标有k1个,占用的区间为[k0+1, k0+k1],这个时候我们生成一个随机数在区间[]1, k0+k1],落在第一个坐标的区间概率为k0/(k0+k1)。并且随机数可以通过二分查找找到对应的原坐标。
class Solution:
def __init__(self, w: List[int]):
# 计算前缀和
self.presum = list(accumulate(w))
def pickIndex(self) -> int:
rand = random.randint(1, self.presum[-1])
return bisect_left(self.presum, rand)
# Your Solution object will be instantiated and called as such:
# obj = Solution(w)
# param_1 = obj.pickIndex()