zoukankan      html  css  js  c++  java
  • 利用等概率Rand5产生等概率Rand3

    转自http://outofmemory.cn/wr/?u=http%3A%2F%2Fwww.gocalf.com%2Fblog%2Funbalanced-coin.html

    利用等概率Rand5产生等概率Rand3

    问题本身很明确,但不知道起个什么题目好,姑且先这么说吧。

    问题描述:现在有一个叫做Rand5的函数,可以生成等概率的[0, 5)范围内的随机整数,要求利用此函数写一个Rand3函数(除此之外,不能再使用任何能产生随机数的函数或数据源),生成等概率的[0, 3)范围内的随机整数。

    我第一次遇到这个问题的时候,着实犯了一回傻,自以为是地证明了这个题目是无解的。其实从概率的角度来看,题目的要求就是,利用一个1/5的概率源,通过某种方式产生出1/3的概率输出。我们都知道,概率运算法则有加法和乘法,而在我的记忆中,算法是“在有限步骤内求解某一问题所使用的一组定义明确的规则”,算法的一个重要特征就是有穷性,即一个算法必须保证执行有限步之后结束。那么有限多个1/5通过加法和乘法是不可能的到1/3这个数值的,因为加法和乘法都不会给分母带来新的因子,那么分母中的3根本就不可能出现。

    然而我忽略了这样一个式子:

    i=0(25)i=1125=53

    基于这个想法,我们来看看这个算法是什么样子的:

    1
    2
    3
    4
    5
    def Rand3():
        x = -1
        while not 0 <= x < 3:
            x = Rand5()
        return x
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int Rand3()
    {
        int x;
        do
        {
            x = Rand5();
        } while (x >= 3);
        return x;
    }
    

    算法很简单,x是我们最终要输出的数字,只要它不在[0, 3)范围内,就不断地调用Rand5来更新它。直观地看,算法输出的数字只有0、1、2这三个,而且对任何一个都没有偏袒,那么显然每个数字的概率都是1/3,那让我们来严格地计算一下。

    以输出0为例,看看概率是多少。x的第一个有效数值是通过Rand5得到的。Rand5返回0的概率是1/5,如果这事儿发生了,我们就得到了0,否则只有当Rand5返回3或4的时候,我们才有机会再次调用它来得到新的数据。第二次调用Rand5之后,又是有1/5的概率得到0,2/5的概率得到3或4导致循环继续执行下去,如此反复。因此概率的计算公式为:

    p=====15+25×(15+25×(15+25×()))15×i=0(25)i15×112515×5313

    喏,计算表明,Rand3输出0的概率确实是1/3,对于另外两个数字也是一样的。

    那么这段代码是不是一个算法呢,它是否满足算法的有穷性呢?我不能确定,虽然它不停机的概率是0,然而这个概率是一个极限值,唉,回去复习极限知识先。

    【2013年11月7日添加】今天想到,对于上面那个函数,需要再了解一下它消耗的时间。具体来讲,就是要知道平均每调用一次Rand3,相当于调用了多少次Rand5。根据算法可以知道,Rand3函数执行一次,有3/5的概率是只调用一次Rand5就能停机;刚好调用两次Rand5后停机的概率是(2/5) * (3/5)。类推下去,刚好调用k次Rand5后停机的概率应该是(2/5) ^ (k-1) * (3/5)。根据这个概率分布,可以计算出停机前Rand5被调用次数的数学期望,即

    k=1k×p(k)=k=1k35(25)k1=35×1(125)2=53

    可见,Rand3函数每运行一次,平均需要调用1.67次Rand5。

    更一般地,当我们依据上述算法,将一种分布的随机信号转换成另外一种随机信号时,如果每消耗m个源信号,就有p的概率可以产生一个目标信号,那么平均来讲,停机前需要使用的源信号数据个数的期望为:

    k=1kmp(1p)k1=mp

    【2013年11月7日添加结束】

    改变一下题目,如果要求利用Rand5编写Rand7怎么办?很简单,用两个Rand5可以拼出Rand25,然后就用前面的方法即可:

    1
    2
    3
    4
    5
    def Rand7():
        x = -1
        while not 0 <= x < 21:
            x = Rand5() * 5 + Rand5()
        return x % 7
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int Rand7()
    {
        int x;
        do
        {
            x = Rand5() * 5 + Rand5();
        } while (x >= 21);
        return x % 7;
    }
    

    【2013年11月7日】可以直接算出,按照这种方法,平均每运行一次Rand7,需要调用Rand5的次数。这里m等于2,p等于21/25,所以最后的结果是50/21,大约是2.38。

  • 相关阅读:
    SQL的各种连接(cross join、inner join、full join)的用法理解
    解决sonarQube 'Unknown': sonar.projectKey
    DOS批处理高级教程
    java自定义注解
    inode备忘
    sed备忘
    awk备忘
    Oracle 删除表中记录 如何释放表及表空间大小
    Oracle 11g 分区拆分与合并
    Maven的settings.xml文件结构之mirrors
  • 原文地址:https://www.cnblogs.com/jinee/p/4419395.html
Copyright © 2011-2022 走看看