题目:
思路:
要求rand10()输出均匀分布并不代表着每个输出的数概率为1/10。
运行两次rand7(),第一次作为十位第二次作为个位,比如第一次取得6,第二次取得3,则(63)为一次组合,这个组合为7进制数。
将此数转化为十进制数,公式为7*(6-1)+3=38。即运行两次rand7()可以得到的十进制数应该为7*(rand7()-1)+rand7()。
我们令res=7*(rand7()-1)+rand7(),显然res∈[1,49]。因为两次运行rand7()独立,所以res取得[1,49]内的每个数的概率都是1/49。实际上到这里这题已经解答出来了。
入门解:
令res=7*(rand7()-1)+rand7(),只要res大于10,拒绝采样,重新取res直到res取值小于等于10。
// The rand7() API is already defined for you.
// int rand7();
// @return a random integer in the range 1 to 7
class Solution {
public:
int rand10() {
int res=11;
while(res>10){
res = (rand7()-1)*7 + rand7();
}
return res;
}
};
进阶解:
上述代码在res大于10时就要重新运行两次rand7(),也就是我们抛弃了11-49的数,这使得时间效率低下。实际上res在取得[1,40]时都是等概率,只需要取余即可:
// The rand7() API is already defined for you.
// int rand7();
// @return a random integer in the range 1 to 7
class Solution {
public:
int rand10() {
int res=41;
while(res>40){
res = (rand7()-1)*7 + rand7();
}
return res%10+1;
}
};
注意对10取余的结果范围为[1,9],需要+1,不影响分布
此时我们丢弃了41-49,此算法还可以再优化,即取得41-49时,它们也是等概率的。可以再令9*(rand7()-1)+(res_41_to_49 - 40),此式的取值范围为[1,63],按照之前的理论可以抛弃3个数,此数已经足够小了不需要再往下了。
抛弃9个数的运行结果如下:
结果还行