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;
}

posted @ 2020-12-04 00:29  xzhws  阅读(175)  评论(0编辑  收藏  举报