zoukankan      html  css  js  c++  java
  • 一天一道算法题—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;
        }
    }      

      

      

      

  • 相关阅读:
    Visual Studio 2008 每日提示(四)
    修改XP注册到用户名和公司组织名
    Visual Studio技巧之打造拥有自己标识的代码模板
    收集的学习资料
    多个记录更新(存储过程)
    '1,2,3,68,10'转换为'1,2,3,6,7,8,10'
    .NET程序员面试的题一部 (转)
    [.net]DataGrid中绑定DropDownList[转]
    使用DELETE与TRUNCATE删除表所有行的区别
    sysobjects 各列的含义
  • 原文地址:https://www.cnblogs.com/shixiaomiao/p/4883676.html
Copyright © 2011-2022 走看看