一天一道算法题—2015-10-22(等概率的随机数)

参考文章:
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,则只需要满足  2>= (b-a+1)就好了。

如果2k= (b-a+1), 那么[0,2k]就可以直接映射到[a,b]了。

但是如果2> (b-a-1),则需要截取,就要抛硬币中产生11一样,舍弃重来

int random( int a , int b)
{
     int m = 1; 
   int k = 0;
int len = b - a + 1;
int i ;
while( m < len )
{
     k++;
m *= 2;
   }
m = 0;
  //构造三位二进制 000,001,010...等概率的产生[0,2
k
]的整数
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;
    }
}      

  

  

  

 

posted on 2015-10-22 20:36 施小喵 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/shixiaomiao/p/4883676.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值