已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。
不要使用系统的 Math.random() 方法。
开胃小菜,用rand7 实现rand5怎么做?
rand7 等概率生成1-7,如果出现,6和7,那么拒绝,重新roll,这样就可以等概率生成1-5.
下面是用rand7实现rand10.
利用一个七进值的结论:
(rand(7)-1)*7 + rand(7)-1 可以等概率生成[0,48]的数
(rand(7)-1)*7 + rand(7) 可以等概率生成[1,49]的数。
为了生成rand10,那我们当然可以超过10就拒绝,小于10就接受,但是这样的性能太差了。
为了优化,我们取一个%10后等概率的最小的数。这个数显然就是40,超过40就拒绝,再来
(idx-1)可以等概率产生0到39的数,(idx-1)%10+1 可以等概率产生0到10的随机数。
class Solution {
public:
int rand10() {
int num;
do{
int a = rand7();
int b = rand7();
num = (a-1)*7+b; // 根据进位制知识。随机生成1到49的随机数
}while(num>40); // [1到40]
return (num-1)%10+1;
}
};
下面是调用rand7次数的证明
方法二:合理使用被拒绝的随机数
我们可以通过合理地使用被拒绝的采样,从而对方法一进行优化。
在方法一中,我们生成 [1, 49] 的随机数,若生成的随机数 x 在 [41, 49] 中,我们则拒绝 x。然而在 x 被拒绝的情况下,我们得到了一个 [1, 9] 的随机数,如果再调用一次 Rand7(),那么就可以生成 [1, 63] 的随机数。我们保留 [1, 60] 并拒绝 [61, 63]:这是 [1, 3] 的随机数。我们继续调用 Rand7(),生成 [1, 21] 的随机数,保留 [1, 20] 并拒绝 [1]。此时 [1] 已经没有任何用处,若出现了拒绝 1 的情况,我们就重新开始生成 [1, 49] 的随机数。
class Solution {
public:
int rand10() {
int a, b, idx;
while (true) {
a = rand7();
b = rand7();
idx = b + (a - 1) * 7;
if (idx <= 40)
return 1 + (idx - 1) % 10;
a = idx - 40;
b = rand7();
// get uniform dist from 1 - 63
idx = b + (a - 1) * 7;
if (idx <= 60)
return 1 + (idx - 1) % 10;
a = idx - 60;
b = rand7();
// get uniform dist from 1 - 21
idx = b + (a - 1) * 7;
if (idx <= 20)
return 1 + (idx - 1) % 10;
}
}
};