力扣470
古典概型的定义:
1、结果是有限的
2、每个结果的概率相同
[1, X]
的随机数发生器 randX() 是一个古典概型:它的结果是有限的,且每个结果的概率相同。独立随机事件的概率:P(AB) = P(A) * P(B)
那么任意的 randX() 都可以使用以下方法构造:
m1*m2...mn >= X
,即把所有采样结果组合起来,最终的结果数量不少于 X,保证可以映射到 [1, X] 的每一个元素。这样做的好处是,我们构造了 m1*m2*...*mn
个结果,并且每个结果的概率是1/m1*m2*...*m n
m1 *m2*...mn
个结果中取 X 个,映射到 [1, X] 个区间,我们就得到了一个均匀分布在 [1,X] 的随机数发生器。第二步中的映射是 1:1 映射,实际应用中,第二步可以去 k*X 个结果来做 k:1 映射,以减少调用 rand7() 次数的期望。
通俗点说,rand7 得到(1~7 的概率都是 1/7
)多次调用 rand7 时,每次调用互相是独立的采样,因此把结果组合起来,我们就得到了很多种采样结果(第一次是1,第二次是1.或者第一次是1,第二次是2),并且每种采样结果的概率都是 1/7
的 n 次方,n 是调用 rand7 的次数。
然后需要将结果的集合映射到 [1,X] 范围内,当然可以直接 1:1 映射,但是这样每种结果发生的概率太小了(如果第一次得到的结果没有在映射的范围内,那么就得重新做一遍采样),因此为了提升成功的概率,减少 rand7 调用的次数,可以使用 k:1 的映射
具体来说,第一步决绝 7,对[1,6] 采样,把奇数和偶数作为 2 种结果,这 2 种结果的概率均为 0.5。rand7 拒绝 6,7,然后对 [1,5] 采样,有 5 种结果,每种概率均为 0.2.
class Solution extends SolBase {
public int rand10() {
int first, second;
while ((first = rand7()) > 6);
while ((second = rand7()) > 5);
return (first&1) == 1 ? second : 5+second;
}
}
上述方法理论上可以构造任何范围的随机数发生器,比如 rand11() :
构造 2 次采样,分别有 2 和 6 种结果,组合起来便有 12 种概率相同的结果。
把这 12 种结果映射到 [1,12] ,然后再拒绝 12 即可。
rand100():
构造 3 次采样,分别有 4,5,5 种结果,组合起来便有 100 种概率相同的结果。
把这 10 种结果映射到 [1,100] 即可。