【题目】
给定一个等概率随机产生1~5的随机函数rand1To5()如下:
private static int randTo5(){
return (int) (Math.random()*5)+1;
}
除此之外不使用任何额外的随机机制,请用rand1To5()实现等概率随机产生1~7的随机函数rand1To7()。
【解答】
这种类型的题目需要经过“插空”和“筛选”两步就可以解决了。
分析:
rand1To5()等概率随机产生1,2,3,4,5。
rand1To5()-1等概率随机产生0,1,2,3,4。
(rand1To5()-1)*5等概率随机产生0,5,10,15,20。
(rand1To5()-1)*5+rand1To5()-1等概率随机产生0,1,2,3,4,5,...,20,21,22,23,24。这里面的rand1To5()是两次独立的调用,不能合并处理,rand1To5()-1产生的0~4刚好可以填补(rand1To5()-1)*5的每个元素的空隙,这就是“插空”。
然后进行“筛选”处理,0~6对7取余结果为0~6,7~13 % 7:0~6,14~20 % 7:0~6,21~27 % 7:0~6,因此在这里我们将筛选出(rand1To5()-1)*5+rand1To5()-1随机产生的0~20,对7取余后就可以等概率的得到0~6,然后再加1,即可以得到1~7。“筛选”的方法很简单,如果(rand1To5()-1)*5+rand1To5()-1生成的随机数大于20,则重复调用(rand1To5()-1)*5+rand1To5()-1继续生成随机数,直到生成的随机数在0~20之间即可,这样出现21~24的概率就会平均分配到0~20上了。
代码如下:
public class Rand1To7 {
public static int rand1To7(){
int m=(randTo5()-1)*5+randTo5()-1;
while(m>20){
m=(randTo5()-1)*5+randTo5()-1;
}
return m%7+1;
}
private static int randTo5(){
return (int) (Math.random()*5)+1;
}
}
【补充题目】
给定一个以p概率产生0,以1-p概率产生1的随机函数:
private static int rand01p(){
double p=0.76;//p can be changed.
return Math.random()<p?0:1;
}
除此之外,不能使用任何额外的随机机制,请使用rand01p()实现等概率随机产生1~6的随机函数rand1To6()。
【解答】
先实现等概率随机产生0和1:
private static int rand01(){//只有rand01p()两次产生的随机数为01或10时,rand01()才会返回一个数0或者1,
//而rand01p()产生随机数01或10的概率均为p(1-p),因此可以等概率生成0和1.
int n=rand01p();
while(n==rand01p()){
n=rand01p();
}
return n;
}
分析:
按照同样的道理:
rand01():0,1
rand01()*2:0,2
rand01()*2+rand01():0,1,2,3
(rand01()*2+rand01())*4+rand01()*2+rand01():0,1,2,3,...,14,15,“插空”完毕
再进行筛选,同之前的方法,等概率产生0~11,对6取余就可以等概率生成0~5,再加1,等概率产生1~6。
代码如下:
public class Rand1To6 {
public static int rand1To6(){
int m=(rand01()+rand01()*2)*4+rand01()+rand01()*2;
while(m>11){
m=(rand01()+rand01p()*2)*4+rand01()+rand01()*2;
}
return m%6+1;
}
private static int rand01p(){
double p=0.5;//p can be changed.
return Math.random()<p?0:1;
}
private static int rand01(){
int n=rand01p();
while(n==rand01p()){
n=rand01p();
}
return n;
}
}