题目
给定黑盒函数f1()等概率随机生成[1,6]之间的数,求目标函数g()等概率随机生成[1,8]之间的数。 注:限制只能使用f1()的随机方法生成数据,不能使用Math.random()等方法。
思路:
- 由目标函数返回值出发,如果我们方法可以等概率返回[0,7]之间的数,那只需要将结果加1就能得到目标函数;
- 由于f1()返回[1,6]之间是等概率的,所以返回1
3和46区间内的值是等概率的,即50%的概率返回[1,3],50%的概率返回[4,6]。 - 将思路2转换一下,让f1()方法将返回[1,3]的结果设置为0,将返回[4,6]的结果设置为1,这样我们得到了等概率生成0或1的方法 4.这时我们转变一下思路,位运算中,int类型可以用32位的数来表示,0对应的32位为00000000000000000000000000000000,7对应的32位为00000000000000000000000000000111, 我们取其末尾3位数,即000~111,而思路3可以等概率生成0或1,所以我们将请求思路3的方法三次,将其结果拼成一个数,它的结果也将会是等概率的,这样我们就得到了[0,7]的等概率生成方法,将其结果+1就得到了目标函数g()。
解法:
- 给定函数f()
/**
* 默认方法,不能改,随机等概率返回1-6之间的数,
* @return int [1,6]
*/
public static int f1() {
return (int) (Math.random() * 6) + 1;
}
引用f1方法将其获取的值转成0或1
/**
* 等概率返回0或者1
* @return int 0或者1
*/
public static int f2() {
return f1() > 3 ? 1 : 0;
}
引用f2方法将其值拼接成000~111
/**
* 等概率返回000~111,其实就是等概率返回[0,7]
* @return int类型 等概率返回[0,7]
*/
private static int f3(){
return (f2()<<2)+(f2()<<1)+(f2());
}
将f3方法的结果+1得到目标函数g()
/**
* 目标函数,等概率返回[1,8]之间的整数
* @return int
*/
private static int g(){
int count = f3();
return count+1;
}
验证方法:
int[] counts = new int[9];
int testTimes = 1000000;
for (int i = 0; i < testTimes; i++) {
int num = g();
counts[num]++;
}
for (int i = 0; i < counts.length; i++) {
System.out.println(i+"这个数出现了 "+counts[i]+"次");
}
结果:控制台运行后可以看出每个数字产生的结果是等概率的。