leetcode 470. 用 Rand7() 实现 Rand10() (数学,优化策略)
题目链接
https://leetcode-cn.com/problems/implement-rand10-using-rand7/
题意:
给定一个rand7()的生成器,求解如何产生一个rand10()的生成器。注意这里定义rand7()的范围是17,而不是06
思路:
关键在于产生的结果必须是等概率的,这是一类非常泛化的题。具体而言涉及到由任意的randm()转化到randn()的计算方式。
详细解读可以参考:https://blog.csdn.net/u010025211/article/details/49668017
在这里我们假设m>n,比如由rand7()转化到rand3()
那么我们只需要把大于3的倍数的值先舍去,比如
int rand3() {
int x=INT_MAX;
while(x>6){
x=rand7();
}
return x%3+1;
}
分析一下产生1~6的概率,那么1/7+1/7*1/7+(1/7)2*1/7+...+(1/7)n*1/7=1/6
而从1~6一共产生的数为1,2,3,4,5,6每个数字出现的概率相同,所以利用x%3+1可以转化得到1,2,3的出现概率也相同
考虑一般化的情况,如果m>n那么randm转化为randn的方式可以理解为:
int rand_n(){
//m>n
int d=m/n;
int ans=INT_MAX;
while(ans>n*d){//最靠近m的整数
ans=rand_m();
}
return ans%n+1;
}
同理如果m<n时,比如rand7()转移到rand10(),这时候需要等距离覆盖,比如如果直接用两个rand7()相加得到[2,14]之间所有数字的和,但是显然这个区间每个数字出现的次数的概率不是相同的(两边出现的概率小,中间出现的概率大),那么怎么才能生成等概率,却又能覆盖掉10以类的所有数字呢?
这里采用一种映射:7*(rand7()-1)+rand7(),前面rand7()-1映射到[0,6],每个数字等概率,7*(rand7()-1)映射到[0,7,14,21,28,35,42],每个数字等概率,后面映射到[1,2,3,4,5,6,7]每个数字等概率,那么两者得到的和[1,2,3,4,5,6,7,8,9,...,42]之间出现的每个数字都是等概率的,即:rand42()=7*(rand7()-1)+rand7()
然后再从rand42()映射回来rand10()就可以了
class Solution {
public:
int randn() {
int x=INT_MAX;
// while(x>10){ //这里还可以再优化
while(x>40){
x=7*(rand7()-1)+rand7();//rand42()
// if(x<=10){
// brmeak;
// }
}
return x%10+1;
}
};
同理可以推出更普遍的情况,如果m<n,那么由rand_m()映射到rand_n()可以为:
int rand_n(){
//m<n
//假设m^k>=n && m^(k-1)<n
int k=0;
int mul=0;
while(m<n){
mul*=m;
k++;
}
int ans=INT_MAX;
while(ans>(mul/n)*n){
int tmp=rand_m();
for(int i=1;i<=k;i++){
tmp=m*(tmp-1)+rand_m(); //rand2*m
}
ans=tmp;
}
return ans%n+1;
}