参考文章:
http://blog.csdn.net/a83610312/article/details/11864265
http://www.cnblogs.com/dwdxdy/archive/2012/07/28/2613135.html
刚刚笔试完滴滴的题目,有一道题叫做,用一枚硬币随机生成1~3的随机数??如果硬币不准,该怎么办?
想起一道类似的,叫做: 如果函数random(5)可以等概率的生成1~5之间的随机数,请用random(5)生成random(7)。
!!!原来是智力题,看来我的智商啊!!!被碾压了。。。。
今天查了一下,原来这个问题是产生等概率的随机数的衍生。
先看原始的:
已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器,
使得它构造0和1的概率均为1/2
解决方案:
这是随机概率发生器的典型题目。
由于需要产生1/2,而用1位0,或1位1无法产生等概率,因此,考虑将随机数扩展成2位:
00 p*p
01 p*(1-p)
10 (1-p)*p
11 (1-p)*(1-p)
有上述分析知道,01和10是等概率的,因此我们只需要产生01和10就行了。
于是可以,遇到00和11就丢弃,只记录01和10。可以令,01表示0,10表示1,则等概率1/2产生0和1了。
注:这种情况就是当滴滴笔试题中的当硬币不准的时候的情况。
所以:
滴滴题目的解答是:
(1)当硬币准的时候,记做正面为1,反正为0 ,产生的概率都为1/2. 要产生1~3的随机数,
因为都是1/2,所以只需要抛两次硬币.
00 代表 1 ; 01 代表 2 ,10 代表 3,抛到11则视为无效,重新抛。
缺点:抛 n + 1次(抛至两次硬币算作一次)结束的概率为 (1/4)^n * (3/4)次,n有可能为无限大的值,则进入死循环。
(2)当硬币不准的时候,同样 记做正面为1,反正为0 ,1的概率为p,则0的概率为 1- p
因为不等概,所以需要抛3次硬币。
产生: 000概率 p*p*p , 001概率 p*p*(1-p) , 010概率p* p * (1-p) , 011概率p*(1-p)*(1-p) , 100概率p * (1-p)*(1-p) , 101 概率(1-p)*p*(1-p), 110 概率p*p*(1-p) , 111概率 p*p*p
可以发现 001 ,010 ,100的概率是一样的,
所以: 001 代表1 ,010 代表 2,100 代表 3
集合a ={ 001 : 1, 010 : 2, 100 : 3}
抛三次硬币 ,得到 001, 010 ,100中的任一一个就结束,否则继续抛。
第二种问题:产生随机的整数从 a 到 b
用的基本元素是 random(0,1),这里的random(0,1)指的是等概率的生成 0 和 1.
分析一下: 其实这里应该是对应的滴滴问题(1)的等概的情况,如果还是用抛至的硬币的方式的话,
抛至的次数为k,则只需要满足 2k >= (b-a+1)就好了。
如果2k= (b-a+1), 那么[0,2k]就可以直接映射到[a,b]了。
但是如果2k > (b-a-1),则需要截取,就要抛硬币中产生11一样,舍弃重来
int random( int a , int b) { int m = 1;k
int k = 0;
int len = b - a + 1;
int i ;
while( m < len )
{
k++;
m *= 2;
}
m = 0;
//构造三位二进制 000,001,010...等概率的产生[0,2
]的整数
for( i = 0 ; i < k ;i++)
{
m += random(0,1) * ( 1 << i );
}
if( m+a > len) {
return random(a,b);
}
else
{
return m+a;
} }
第二种问题的扩展是:一直random5()函数可以等概率产生1到5的整数,求用random5()函数生成random7函数可以等概率的产生1到7的整数
解法一:比较好理解的是利用二维数组的形式
int random7() { int a[5][5] = [ {1,2,3,4,5}, {6,7,1,2,3}, {4,5,6,7,1}, {2,3,4,5,6}, {7,0,0,0,0}]; int i,j; int result = 0; while( result == 0 ) { int i = random5(); int j = random5(); int result = a[i-1][j-1]; } return result; }
解法二:
思路: random5() - 1就是 random(0,4). 将random7看成是求解random(1,7)。
则 底数应该是5, 5k >= (7-1+1) ,将 [0,24]截取[0,6],然后映射到[1,7]
int random7() { int k = 0 ; int m = 1; int len = 7; int i = 0; while( m < len ) { k++; m *= 2 ; } m = 0; for( i ; i < k; i++) { m += ( random5() - 1 ) * pow(5,i) ; } if( m+1 > len ) { return random7(); } else { return m+1; } }