zoukankan      html  css  js  c++  java
  • 给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数(均匀概率问题)

    google面试题:给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数。

    问题分析:现在给了一个能随机生成1~5的随机函数,怎样利用这个已知条件生成一个1~7的随机函数呢?既然要生成的是随机数那么生成1,2,3,4,5,6,7的概率就应该是一样的。显然现在光生成1~5之间的数就不够了,我们想到应该要加大生成数的范围,并且加大范围的同时还要保证每个数产生的概率一样,于是有这样一种方法用这个表达式来扩大生成数范围:rand5()*5+rand5(),新的数据范围变成:6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.并且可以看出来这个25个数出现的可能性是一样的,于是我们可以只用6~26之间的21个数变成1~7这7个数,于是就是要每3个数对应一个数,即:

    6,7,8对应1

    9,10,11对应2

    …………

    24,25,26对应7

    这种变化对应的方式是(6 - 3)/ 3 = 1,(7 - 3) / 3 = 1,(8-3) / 3 = 1.

    int rand7()
    {
        int i;
        //直到产生6~26之间的数跳出循环
        while((i=rand5()*5+rand5()) > 26) ;
        return (i-3)/3;
    }

    总感觉这种方式不好,虽然1-7概率均匀,但相加不等于1。现在深入分析:

    假设我们要等概率生成一个3位的10进制数(000 - 999),我们可以在 随机生成整数0到9的函数 基础上,随机生成3个数字组成三位数就得到了结果。

    这里类似,我们首先必须认识到:任何一个数都可以用5进制的数来表示,如12 = 5进制(22) = 2*5 + 2。因此假设我们要随机生成[0,444]范围的数,我们只要随机生成3个5进制的数字组合就可以。

    这里的主要问题是:7不是5的幂次方。

    但是我们可以将某一个5的幂次方均分成 7 段(分别为0 - 6,等概率的落到每一段),利用5进制随机成一个数,看这个数在哪一个段,就代表我们要生成哪一个数字,这样就保证了等概率的生成0 - 6.

    有一个小的问题就是:5的幂次方不能整除7,会遗漏最高的几个数。但是我们这里只要数字足够大,则遗漏的概率相当的小

    下面是简单的代码:

    const int K = 10;//[0, 5^K - 1]
    int Base[K],Rnd[K],Step; //Step表每个区间的长度
    int Rand15() //已有的随机生成1 - 5的随机函数
    {
        return rand()%5 + 1;
    }
    void Init()
    {
        Base[0] = 1;
        for(int i=1 ;i<K; ++i)
            Base[i] = Base[i-1]*5;
        Step = Base[K-1]*5/7;
        //cout<<"Step = "<<Step<<" Step*7 = "<<Step*7<<endl;
        srand(time(0));
    }
    int Rand17() //我们需要写的随机生成1 - 7的随机函数
    {
        int Sum = 0;
        for(int i=0 ;i<K; ++i) //随机生成K个1到5的随机数
        {
            Rnd[i] = Rand15();
            Sum += (Rnd[i]-1)*Base[i];
        }
        return Sum/Step + 1;
    }

    这里K = 10的时候,保证遗漏了2个数,即落到余数里面的概率是:2/9765625.

    利用rand[1,5】得到rand[1,7]可以抽象成利用rand[0,1]得到rand[0,n],看下面的就清楚了。

    -----------------------------------------

    题目:已知rand7() 可以产生 1~7 的7个数(均匀概率),利用rand7() 产生rand10() 1~10(均匀概率)

    记住这道题重点是:均匀概率。

    rand7 产生的数概率是一样的,即1~7出现概率一样,由于我们对结果做了一定的筛选只能通过 1~5,而1~5出现的概率也是一样的,又由于范围为1~5 所以 temp1 出现 1~5的概率 为1/5 ,同理 后面的 出现 temp2 的概率为 1/2。

    首先temp1出现在1~5的概率为1/5,而temp2出现 1~2 的概率为1/2,也就是说 5*(temp2-1) 出现5或0的概率为1/2,所以假如你要得到1~5的数的话 那么 5*(temp2-1) 必须0,所以因为你要保证 5*(temp2-1)=0,这个概率只有1/2,再加上 你前面指定1~5 的概率 为1/5 ,所以结果为 1/5*1/2=1/10

    int rand10() 
    02
    { 
    03
        int temp1; 
    04
        int temp2; 
    05
        do 
    06
        { 
    07
            temp1 = rand7(); 
    08
        }while(temp1>5); 
    09
        do 
    10
        { 
    11
            temp2 = rand7(); 
    12
        }while(temp2>2); 
    13
        return temp1+5*(temp2-1); 
    14
    } 

    已知一个函数f可以得到1-5间的随机数,问怎么得到1-7的随机数

    对不对?

    这个其实和算法导论上的一个题很像么:已知random等概率返回0或者1,那么试写一个函数等概率返回[a,b]之间的整数。思路就是2进制表示[0, b-a]之间的数,先计算出至少需要多少位,按位生成一个二进制数,一旦大于b-a就重新生成。放到这里的话,表示成5进制就可以了~

    推广一下:对于等概率可以生成k个连续整数函数的函数randomk,设计生成[a,b]之间的整数的算法:
    令n = b - a;则等概率生成[0,n]上的一个整数即可。于是用k进制表示生成的整数,设m=ceiling(logk(n)),(这个不对)

    k^m-1=n; 求解m。m=ceil(logk(n+1)); 向上取整,如4,应该用3个二进制表示. log2(5) 向上取整. (m还可以写成:

    m= 1 +log2n)

    1. int randN() {
    2.   while (res > n) {
    3.      for (i = 0; i < m; i++)  
    4.          gen bit i for res with randomk
    5.   }
    6.   return res;
    7. }
    8. 期望的运行时间为t*m* i * (1 - (n+1)/k^m)^(i-1) * ((n+1)/k^m),i从1加到无穷,t表示randomk的运行时间,那么计算这个级数的值为t*m*k^m/(n+1).
      带入到这个题目,期望运行时间为50*t/7,还是很快的。
     利用rand()得到rand[0,n]的代码:
    void set(int &a,int i) { a |= (1<<i);}//一定要是引用
    int randN(int n)
    {
      
        int res=n+1;//让第一次进入while循环,res只要》n即可.
        //c++只有log(默认以e为底)和log10,如果自己设定底数用换底公式
        float floatN=n;
        float tmp=log(floatN+1)/log(2.0);
        int m=ceil(tmp);
        //cout<<"m:"<<m<<endl;
        while(res>n)
        {
            res=0;
            for(int i=0;i<m;i++)
            {
                Sleep(20);
                srand(time(NULL));
                
                int bit=rand()%(2-0)+0;//[0,1]
                if(bit==1)
                {
                    set(res,i);
                }
            }
        }
        return res;
    }

    参考:http://blog.csdn.net/pkueecser/article/details/7425994

  • 相关阅读:
    3com1100、3300未知故障升级软件后消失
    俺去测量去了,每天更新图片!!
    windows Installer出错的解决方法
    Gmail的POP3设置
    SharePonit 相关连接收集
    路由器默认密码
    今天dudu说区域很重要,我去读了一下
    CAD画任意角度线的方法
    修改动网新闻.net
    多条语句连续insert into 报ora00911解决方法
  • 原文地址:https://www.cnblogs.com/youxin/p/3351213.html
Copyright © 2011-2022 走看看